1
0
Fork 0
mirror of https://github.com/ossrs/srs.git synced 2025-03-09 15:49:59 +00:00

Upgrade gperftools to 2.9 for GCP/GMC/GMP/GMD. (#2247)

This commit is contained in:
winlin 2021-12-12 15:38:30 +08:00
parent 63da0dca92
commit 44e9dc83e9
346 changed files with 169666 additions and 78 deletions

View file

@ -0,0 +1,422 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2005, 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
//
// A fast map from addresses to values. Assumes that addresses are
// clustered. The main use is intended to be for heap-profiling.
// May be too memory-hungry for other uses.
//
// We use a user-defined allocator/de-allocator so that we can use
// this data structure during heap-profiling.
//
// IMPLEMENTATION DETAIL:
//
// Some default definitions/parameters:
// * Block -- aligned 128-byte region of the address space
// * Cluster -- aligned 1-MB region of the address space
// * Block-ID -- block-number within a cluster
// * Cluster-ID -- Starting address of cluster divided by cluster size
//
// We use a three-level map to represent the state:
// 1. A hash-table maps from a cluster-ID to the data for that cluster.
// 2. For each non-empty cluster we keep an array indexed by
// block-ID tht points to the first entry in the linked-list
// for the block.
// 3. At the bottom, we keep a singly-linked list of all
// entries in a block (for non-empty blocks).
//
// hash table
// +-------------+
// | id->cluster |---> ...
// | ... |
// | id->cluster |---> Cluster
// +-------------+ +-------+ Data for one block
// | nil | +------------------------------------+
// | ----+---|->[addr/value]-->[addr/value]-->... |
// | nil | +------------------------------------+
// | ----+--> ...
// | nil |
// | ... |
// +-------+
//
// Note that we require zero-bytes of overhead for completely empty
// clusters. The minimum space requirement for a cluster is the size
// of the hash-table entry plus a pointer value for each block in
// the cluster. Empty blocks impose no extra space requirement.
//
// The cost of a lookup is:
// a. A hash-table lookup to find the cluster
// b. An array access in the cluster structure
// c. A traversal over the linked-list for a block
#ifndef BASE_ADDRESSMAP_INL_H_
#define BASE_ADDRESSMAP_INL_H_
#include "config.h"
#include <stddef.h>
#include <string.h>
#if defined HAVE_STDINT_H
#include <stdint.h> // to get uint16_t (ISO naming madness)
#elif defined HAVE_INTTYPES_H
#include <inttypes.h> // another place uint16_t might be defined
#else
#include <sys/types.h> // our last best hope
#endif
// This class is thread-unsafe -- that is, instances of this class can
// not be accessed concurrently by multiple threads -- because the
// callback function for Iterate() may mutate contained values. If the
// callback functions you pass do not mutate their Value* argument,
// AddressMap can be treated as thread-compatible -- that is, it's
// safe for multiple threads to call "const" methods on this class,
// but not safe for one thread to call const methods on this class
// while another thread is calling non-const methods on the class.
template <class Value>
class AddressMap {
public:
typedef void* (*Allocator)(size_t size);
typedef void (*DeAllocator)(void* ptr);
typedef const void* Key;
// Create an AddressMap that uses the specified allocator/deallocator.
// The allocator/deallocator should behave like malloc/free.
// For instance, the allocator does not need to return initialized memory.
AddressMap(Allocator alloc, DeAllocator dealloc);
~AddressMap();
// If the map contains an entry for "key", return it. Else return NULL.
inline const Value* Find(Key key) const;
inline Value* FindMutable(Key key);
// Insert <key,value> into the map. Any old value associated
// with key is forgotten.
void Insert(Key key, Value value);
// Remove any entry for key in the map. If an entry was found
// and removed, stores the associated value in "*removed_value"
// and returns true. Else returns false.
bool FindAndRemove(Key key, Value* removed_value);
// Similar to Find but we assume that keys are addresses of non-overlapping
// memory ranges whose sizes are given by size_func.
// If the map contains a range into which "key" points
// (at its start or inside of it, but not at the end),
// return the address of the associated value
// and store its key in "*res_key".
// Else return NULL.
// max_size specifies largest range size possibly in existence now.
typedef size_t (*ValueSizeFunc)(const Value& v);
const Value* FindInside(ValueSizeFunc size_func, size_t max_size,
Key key, Key* res_key);
// Iterate over the address map calling 'callback'
// for all stored key-value pairs and passing 'arg' to it.
// We don't use full Closure/Callback machinery not to add
// unnecessary dependencies to this class with low-level uses.
template<class Type>
inline void Iterate(void (*callback)(Key, Value*, Type), Type arg) const;
private:
typedef uintptr_t Number;
// The implementation assumes that addresses inserted into the map
// will be clustered. We take advantage of this fact by splitting
// up the address-space into blocks and using a linked-list entry
// for each block.
// Size of each block. There is one linked-list for each block, so
// do not make the block-size too big. Oterwise, a lot of time
// will be spent traversing linked lists.
static const int kBlockBits = 7;
static const int kBlockSize = 1 << kBlockBits;
// Entry kept in per-block linked-list
struct Entry {
Entry* next;
Key key;
Value value;
};
// We further group a sequence of consecutive blocks into a cluster.
// The data for a cluster is represented as a dense array of
// linked-lists, one list per contained block.
static const int kClusterBits = 13;
static const Number kClusterSize = 1 << (kBlockBits + kClusterBits);
static const int kClusterBlocks = 1 << kClusterBits;
// We use a simple chaining hash-table to represent the clusters.
struct Cluster {
Cluster* next; // Next cluster in hash table chain
Number id; // Cluster ID
Entry* blocks[kClusterBlocks]; // Per-block linked-lists
};
// Number of hash-table entries. With the block-size/cluster-size
// defined above, each cluster covers 1 MB, so an 4K entry
// hash-table will give an average hash-chain length of 1 for 4GB of
// in-use memory.
static const int kHashBits = 12;
static const int kHashSize = 1 << 12;
// Number of entry objects allocated at a time
static const int ALLOC_COUNT = 64;
Cluster** hashtable_; // The hash-table
Entry* free_; // Free list of unused Entry objects
// Multiplicative hash function:
// The value "kHashMultiplier" is the bottom 32 bits of
// int((sqrt(5)-1)/2 * 2^32)
// This is a good multiplier as suggested in CLR, Knuth. The hash
// value is taken to be the top "k" bits of the bottom 32 bits
// of the muliplied value.
static const uint32_t kHashMultiplier = 2654435769u;
static int HashInt(Number x) {
// Multiply by a constant and take the top bits of the result.
const uint32_t m = static_cast<uint32_t>(x) * kHashMultiplier;
return static_cast<int>(m >> (32 - kHashBits));
}
// Find cluster object for specified address. If not found
// and "create" is true, create the object. If not found
// and "create" is false, return NULL.
//
// This method is bitwise-const if create is false.
Cluster* FindCluster(Number address, bool create) {
// Look in hashtable
const Number cluster_id = address >> (kBlockBits + kClusterBits);
const int h = HashInt(cluster_id);
for (Cluster* c = hashtable_[h]; c != NULL; c = c->next) {
if (c->id == cluster_id) {
return c;
}
}
// Create cluster if necessary
if (create) {
Cluster* c = New<Cluster>(1);
c->id = cluster_id;
c->next = hashtable_[h];
hashtable_[h] = c;
return c;
}
return NULL;
}
// Return the block ID for an address within its cluster
static int BlockID(Number address) {
return (address >> kBlockBits) & (kClusterBlocks - 1);
}
//--------------------------------------------------------------
// Memory management -- we keep all objects we allocate linked
// together in a singly linked list so we can get rid of them
// when we are all done. Furthermore, we allow the client to
// pass in custom memory allocator/deallocator routines.
//--------------------------------------------------------------
struct Object {
Object* next;
// The real data starts here
};
Allocator alloc_; // The allocator
DeAllocator dealloc_; // The deallocator
Object* allocated_; // List of allocated objects
// Allocates a zeroed array of T with length "num". Also inserts
// the allocated block into a linked list so it can be deallocated
// when we are all done.
template <class T> T* New(int num) {
void* ptr = (*alloc_)(sizeof(Object) + num*sizeof(T));
memset(ptr, 0, sizeof(Object) + num*sizeof(T));
Object* obj = reinterpret_cast<Object*>(ptr);
obj->next = allocated_;
allocated_ = obj;
return reinterpret_cast<T*>(reinterpret_cast<Object*>(ptr) + 1);
}
};
// More implementation details follow:
template <class Value>
AddressMap<Value>::AddressMap(Allocator alloc, DeAllocator dealloc)
: free_(NULL),
alloc_(alloc),
dealloc_(dealloc),
allocated_(NULL) {
hashtable_ = New<Cluster*>(kHashSize);
}
template <class Value>
AddressMap<Value>::~AddressMap() {
// De-allocate all of the objects we allocated
for (Object* obj = allocated_; obj != NULL; /**/) {
Object* next = obj->next;
(*dealloc_)(obj);
obj = next;
}
}
template <class Value>
inline const Value* AddressMap<Value>::Find(Key key) const {
return const_cast<AddressMap*>(this)->FindMutable(key);
}
template <class Value>
inline Value* AddressMap<Value>::FindMutable(Key key) {
const Number num = reinterpret_cast<Number>(key);
const Cluster* const c = FindCluster(num, false/*do not create*/);
if (c != NULL) {
for (Entry* e = c->blocks[BlockID(num)]; e != NULL; e = e->next) {
if (e->key == key) {
return &e->value;
}
}
}
return NULL;
}
template <class Value>
void AddressMap<Value>::Insert(Key key, Value value) {
const Number num = reinterpret_cast<Number>(key);
Cluster* const c = FindCluster(num, true/*create*/);
// Look in linked-list for this block
const int block = BlockID(num);
for (Entry* e = c->blocks[block]; e != NULL; e = e->next) {
if (e->key == key) {
e->value = value;
return;
}
}
// Create entry
if (free_ == NULL) {
// Allocate a new batch of entries and add to free-list
Entry* array = New<Entry>(ALLOC_COUNT);
for (int i = 0; i < ALLOC_COUNT-1; i++) {
array[i].next = &array[i+1];
}
array[ALLOC_COUNT-1].next = free_;
free_ = &array[0];
}
Entry* e = free_;
free_ = e->next;
e->key = key;
e->value = value;
e->next = c->blocks[block];
c->blocks[block] = e;
}
template <class Value>
bool AddressMap<Value>::FindAndRemove(Key key, Value* removed_value) {
const Number num = reinterpret_cast<Number>(key);
Cluster* const c = FindCluster(num, false/*do not create*/);
if (c != NULL) {
for (Entry** p = &c->blocks[BlockID(num)]; *p != NULL; p = &(*p)->next) {
Entry* e = *p;
if (e->key == key) {
*removed_value = e->value;
*p = e->next; // Remove e from linked-list
e->next = free_; // Add e to free-list
free_ = e;
return true;
}
}
}
return false;
}
template <class Value>
const Value* AddressMap<Value>::FindInside(ValueSizeFunc size_func,
size_t max_size,
Key key,
Key* res_key) {
const Number key_num = reinterpret_cast<Number>(key);
Number num = key_num; // we'll move this to move back through the clusters
while (1) {
const Cluster* c = FindCluster(num, false/*do not create*/);
if (c != NULL) {
while (1) {
const int block = BlockID(num);
bool had_smaller_key = false;
for (const Entry* e = c->blocks[block]; e != NULL; e = e->next) {
const Number e_num = reinterpret_cast<Number>(e->key);
if (e_num <= key_num) {
if (e_num == key_num || // to handle 0-sized ranges
key_num < e_num + (*size_func)(e->value)) {
*res_key = e->key;
return &e->value;
}
had_smaller_key = true;
}
}
if (had_smaller_key) return NULL; // got a range before 'key'
// and it did not contain 'key'
if (block == 0) break;
// try address-wise previous block
num |= kBlockSize - 1; // start at the last addr of prev block
num -= kBlockSize;
if (key_num - num > max_size) return NULL;
}
}
if (num < kClusterSize) return NULL; // first cluster
// go to address-wise previous cluster to try
num |= kClusterSize - 1; // start at the last block of previous cluster
num -= kClusterSize;
if (key_num - num > max_size) return NULL;
// Having max_size to limit the search is crucial: else
// we have to traverse a lot of empty clusters (or blocks).
// We can avoid needing max_size if we put clusters into
// a search tree, but performance suffers considerably
// if we use this approach by using stl::set.
}
}
template <class Value>
template <class Type>
inline void AddressMap<Value>::Iterate(void (*callback)(Key, Value*, Type),
Type arg) const {
// We could optimize this by traversing only non-empty clusters and/or blocks
// but it does not speed up heap-checker noticeably.
for (int h = 0; h < kHashSize; ++h) {
for (const Cluster* c = hashtable_[h]; c != NULL; c = c->next) {
for (int b = 0; b < kClusterBlocks; ++b) {
for (Entry* e = c->blocks[b]; e != NULL; e = e->next) {
callback(e->key, &e->value, arg);
}
}
}
}
}
#endif // BASE_ADDRESSMAP_INL_H_

View file

@ -0,0 +1,85 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2011, 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: Alexander Levitskiy
//
// Generalizes the plethora of ARM flavors available to an easier to manage set
// Defs reference is at https://wiki.edubuntu.org/ARM/Thumb2PortingHowto
#ifndef ARM_INSTRUCTION_SET_SELECT_H_
#define ARM_INSTRUCTION_SET_SELECT_H_
#if defined(__ARM_ARCH_8A__)
# define ARMV8 1
#endif
#if defined(ARMV8) || \
defined(__ARM_ARCH_7__) || \
defined(__ARM_ARCH_7R__) || \
defined(__ARM_ARCH_7A__)
# define ARMV7 1
#endif
#if defined(ARMV7) || \
defined(__ARM_ARCH_6__) || \
defined(__ARM_ARCH_6J__) || \
defined(__ARM_ARCH_6K__) || \
defined(__ARM_ARCH_6Z__) || \
defined(__ARM_ARCH_6T2__) || \
defined(__ARM_ARCH_6ZK__)
# define ARMV6 1
#endif
#if defined(ARMV6) || \
defined(__ARM_ARCH_5T__) || \
defined(__ARM_ARCH_5E__) || \
defined(__ARM_ARCH_5TE__) || \
defined(__ARM_ARCH_5TEJ__)
# define ARMV5 1
#endif
#if defined(ARMV5) || \
defined(__ARM_ARCH_4__) || \
defined(__ARM_ARCH_4T__)
# define ARMV4 1
#endif
#if defined(ARMV4) || \
defined(__ARM_ARCH_3__) || \
defined(__ARM_ARCH_3M__)
# define ARMV3 1
#endif
#if defined(ARMV3) || \
defined(__ARM_ARCH_2__)
# define ARMV2 1
#endif
#endif // ARM_INSTRUCTION_SET_SELECT_H_

View file

@ -0,0 +1,209 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2003, 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: Lei Zhang, Sasha Levitskiy
//
// This file is an internal atomic implementation, use base/atomicops.h instead.
//
// LinuxKernelCmpxchg is from Google Gears.
#ifndef BASE_ATOMICOPS_INTERNALS_ARM_GENERIC_H_
#define BASE_ATOMICOPS_INTERNALS_ARM_GENERIC_H_
#include <stdio.h>
#include <stdlib.h>
#include "base/basictypes.h"
typedef int32_t Atomic32;
namespace base {
namespace subtle {
typedef int64_t Atomic64;
// 0xffff0fc0 is the hard coded address of a function provided by
// the kernel which implements an atomic compare-exchange. On older
// ARM architecture revisions (pre-v6) this may be implemented using
// a syscall. This address is stable, and in active use (hard coded)
// by at least glibc-2.7 and the Android C library.
// pLinuxKernelCmpxchg has both acquire and release barrier sematincs.
typedef Atomic32 (*LinuxKernelCmpxchgFunc)(Atomic32 old_value,
Atomic32 new_value,
volatile Atomic32* ptr);
LinuxKernelCmpxchgFunc pLinuxKernelCmpxchg ATTRIBUTE_WEAK =
(LinuxKernelCmpxchgFunc) 0xffff0fc0;
typedef void (*LinuxKernelMemoryBarrierFunc)(void);
LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier ATTRIBUTE_WEAK =
(LinuxKernelMemoryBarrierFunc) 0xffff0fa0;
inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 prev_value = *ptr;
do {
if (!pLinuxKernelCmpxchg(old_value, new_value,
const_cast<Atomic32*>(ptr))) {
return old_value;
}
prev_value = *ptr;
} while (prev_value == old_value);
return prev_value;
}
inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
Atomic32 old_value;
do {
old_value = *ptr;
} while (pLinuxKernelCmpxchg(old_value, new_value,
const_cast<Atomic32*>(ptr)));
return old_value;
}
inline Atomic32 Acquire_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
// pLinuxKernelCmpxchg already has acquire and release barrier semantics.
return NoBarrier_AtomicExchange(ptr, new_value);
}
inline Atomic32 Release_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
// pLinuxKernelCmpxchg already has acquire and release barrier semantics.
return NoBarrier_AtomicExchange(ptr, new_value);
}
inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}
inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}
inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
*ptr = value;
}
inline void MemoryBarrier() {
pLinuxKernelMemoryBarrier();
}
inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
MemoryBarrier();
*ptr = value;
}
inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
return *ptr;
}
inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
Atomic32 value = *ptr;
MemoryBarrier();
return value;
}
// 64-bit versions are not implemented yet.
inline void NotImplementedFatalError(const char *function_name) {
fprintf(stderr, "64-bit %s() not implemented on this platform\n",
function_name);
abort();
}
inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
NotImplementedFatalError("NoBarrier_CompareAndSwap");
return 0;
}
inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
NotImplementedFatalError("NoBarrier_AtomicExchange");
return 0;
}
inline Atomic64 Acquire_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
// pLinuxKernelCmpxchg already has acquire and release barrier semantics.
return NoBarrier_AtomicExchange(ptr, new_value);
}
inline Atomic64 Release_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
// pLinuxKernelCmpxchg already has acquire and release barrier semantics.
return NoBarrier_AtomicExchange(ptr, new_value);
}
inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
NotImplementedFatalError("NoBarrier_Store");
}
inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
NotImplementedFatalError("Release_Store");
}
inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
NotImplementedFatalError("NoBarrier_Load");
return 0;
}
inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
NotImplementedFatalError("Atomic64 Acquire_Load");
return 0;
}
inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
NotImplementedFatalError("Atomic64 Acquire_CompareAndSwap");
return 0;
}
inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
NotImplementedFatalError("Atomic64 Release_CompareAndSwap");
return 0;
}
} // namespace base::subtle
} // namespace base
#endif // BASE_ATOMICOPS_INTERNALS_ARM_GENERIC_H_

View file

@ -0,0 +1,310 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2011, 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: Sasha Levitskiy
// based on atomicops-internals by Sanjay Ghemawat
//
// This file is an internal atomic implementation, use base/atomicops.h instead.
//
// This code implements ARM atomics for architectures V6 and newer.
#ifndef BASE_ATOMICOPS_INTERNALS_ARM_V6PLUS_H_
#define BASE_ATOMICOPS_INTERNALS_ARM_V6PLUS_H_
#include <stdio.h>
#include <stdlib.h>
#include "base/basictypes.h" // For COMPILE_ASSERT
// The LDREXD and STREXD instructions in ARM all v7 variants or above. In v6,
// only some variants support it. For simplicity, we only use exclusive
// 64-bit load/store in V7 or above.
#if defined(ARMV7)
# define BASE_ATOMICOPS_HAS_LDREXD_AND_STREXD
#endif
typedef int32_t Atomic32;
namespace base {
namespace subtle {
typedef int64_t Atomic64;
// 32-bit low-level ops
inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 oldval, res;
do {
__asm__ __volatile__(
"ldrex %1, [%3]\n"
"mov %0, #0\n"
"teq %1, %4\n"
// The following IT (if-then) instruction is needed for the subsequent
// conditional instruction STREXEQ when compiling in THUMB mode.
// In ARM mode, the compiler/assembler will not generate any code for it.
"it eq\n"
"strexeq %0, %5, [%3]\n"
: "=&r" (res), "=&r" (oldval), "+Qo" (*ptr)
: "r" (ptr), "Ir" (old_value), "r" (new_value)
: "cc");
} while (res);
return oldval;
}
inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
Atomic32 tmp, old;
__asm__ __volatile__(
"1:\n"
"ldrex %1, [%2]\n"
"strex %0, %3, [%2]\n"
"teq %0, #0\n"
"bne 1b"
: "=&r" (tmp), "=&r" (old)
: "r" (ptr), "r" (new_value)
: "cc", "memory");
return old;
}
inline void MemoryBarrier() {
#if !defined(ARMV7)
uint32_t dest = 0;
__asm__ __volatile__("mcr p15,0,%0,c7,c10,5" :"=&r"(dest) : : "memory");
#else
__asm__ __volatile__("dmb" : : : "memory");
#endif
}
inline Atomic32 Acquire_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
Atomic32 old_value = NoBarrier_AtomicExchange(ptr, new_value);
MemoryBarrier();
return old_value;
}
inline Atomic32 Release_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
MemoryBarrier();
return NoBarrier_AtomicExchange(ptr, new_value);
}
inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 value = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
MemoryBarrier();
return value;
}
inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
MemoryBarrier();
return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}
inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
*ptr = value;
}
inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
MemoryBarrier();
*ptr = value;
}
inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
return *ptr;
}
inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
Atomic32 value = *ptr;
MemoryBarrier();
return value;
}
// 64-bit versions are only available if LDREXD and STREXD instructions
// are available.
#ifdef BASE_ATOMICOPS_HAS_LDREXD_AND_STREXD
#define BASE_HAS_ATOMIC64 1
inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
Atomic64 oldval, res;
do {
__asm__ __volatile__(
"ldrexd %1, [%3]\n"
"mov %0, #0\n"
"teq %Q1, %Q4\n"
// The following IT (if-then) instructions are needed for the subsequent
// conditional instructions when compiling in THUMB mode.
// In ARM mode, the compiler/assembler will not generate any code for it.
"it eq\n"
"teqeq %R1, %R4\n"
"it eq\n"
"strexdeq %0, %5, [%3]\n"
: "=&r" (res), "=&r" (oldval), "+Q" (*ptr)
: "r" (ptr), "Ir" (old_value), "r" (new_value)
: "cc");
} while (res);
return oldval;
}
inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
int store_failed;
Atomic64 old;
__asm__ __volatile__(
"1:\n"
"ldrexd %1, [%2]\n"
"strexd %0, %3, [%2]\n"
"teq %0, #0\n"
"bne 1b"
: "=&r" (store_failed), "=&r" (old)
: "r" (ptr), "r" (new_value)
: "cc", "memory");
return old;
}
inline Atomic64 Acquire_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
Atomic64 old_value = NoBarrier_AtomicExchange(ptr, new_value);
MemoryBarrier();
return old_value;
}
inline Atomic64 Release_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
MemoryBarrier();
return NoBarrier_AtomicExchange(ptr, new_value);
}
inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
int store_failed;
Atomic64 dummy;
__asm__ __volatile__(
"1:\n"
// Dummy load to lock cache line.
"ldrexd %1, [%3]\n"
"strexd %0, %2, [%3]\n"
"teq %0, #0\n"
"bne 1b"
: "=&r" (store_failed), "=&r"(dummy)
: "r"(value), "r" (ptr)
: "cc", "memory");
}
inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
Atomic64 res;
__asm__ __volatile__(
"ldrexd %0, [%1]\n"
"clrex\n"
: "=r" (res)
: "r"(ptr), "Q"(*ptr));
return res;
}
#else // BASE_ATOMICOPS_HAS_LDREXD_AND_STREXD
inline void NotImplementedFatalError(const char *function_name) {
fprintf(stderr, "64-bit %s() not implemented on this platform\n",
function_name);
abort();
}
inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
NotImplementedFatalError("NoBarrier_CompareAndSwap");
return 0;
}
inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
NotImplementedFatalError("NoBarrier_AtomicExchange");
return 0;
}
inline Atomic64 Acquire_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
NotImplementedFatalError("Acquire_AtomicExchange");
return 0;
}
inline Atomic64 Release_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
NotImplementedFatalError("Release_AtomicExchange");
return 0;
}
inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
NotImplementedFatalError("NoBarrier_Store");
}
inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
NotImplementedFatalError("NoBarrier_Load");
return 0;
}
#endif // BASE_ATOMICOPS_HAS_LDREXD_AND_STREXD
inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
MemoryBarrier();
NoBarrier_Store(ptr, value);
}
inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
Atomic64 value = NoBarrier_Load(ptr);
MemoryBarrier();
return value;
}
inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
Atomic64 value = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
MemoryBarrier();
return value;
}
inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
MemoryBarrier();
return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}
} // namespace subtle ends
} // namespace base ends
#endif // BASE_ATOMICOPS_INTERNALS_ARM_V6PLUS_H_

View file

@ -0,0 +1,183 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2014, Linaro
// 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: Riku Voipio, riku.voipio@linaro.org
//
// atomic primitives implemented with gcc atomic intrinsics:
// http://gcc.gnu.org/onlinedocs/gcc/_005f_005fatomic-Builtins.html
//
#ifndef BASE_ATOMICOPS_INTERNALS_GCC_GENERIC_H_
#define BASE_ATOMICOPS_INTERNALS_GCC_GENERIC_H_
#include <stdio.h>
#include <stdlib.h>
#include "base/basictypes.h"
typedef int32_t Atomic32;
namespace base {
namespace subtle {
typedef int64_t Atomic64;
inline void MemoryBarrier() {
__sync_synchronize();
}
inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 prev_value = old_value;
__atomic_compare_exchange_n(ptr, &prev_value, new_value,
0, __ATOMIC_RELAXED, __ATOMIC_RELAXED);
return prev_value;
}
inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
return __atomic_exchange_n(const_cast<Atomic32*>(ptr), new_value, __ATOMIC_RELAXED);
}
inline Atomic32 Acquire_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
return __atomic_exchange_n(const_cast<Atomic32*>(ptr), new_value, __ATOMIC_ACQUIRE);
}
inline Atomic32 Release_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
return __atomic_exchange_n(const_cast<Atomic32*>(ptr), new_value, __ATOMIC_RELEASE);
}
inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 prev_value = old_value;
__atomic_compare_exchange_n(ptr, &prev_value, new_value,
0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
return prev_value;
}
inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 prev_value = old_value;
__atomic_compare_exchange_n(ptr, &prev_value, new_value,
0, __ATOMIC_RELEASE, __ATOMIC_RELAXED);
return prev_value;
}
inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
*ptr = value;
}
inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
MemoryBarrier();
*ptr = value;
}
inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
return *ptr;
}
inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
Atomic32 value = *ptr;
MemoryBarrier();
return value;
}
// 64-bit versions
inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
Atomic64 prev_value = old_value;
__atomic_compare_exchange_n(ptr, &prev_value, new_value,
0, __ATOMIC_RELAXED, __ATOMIC_RELAXED);
return prev_value;
}
inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
return __atomic_exchange_n(const_cast<Atomic64*>(ptr), new_value, __ATOMIC_RELAXED);
}
inline Atomic64 Acquire_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
return __atomic_exchange_n(const_cast<Atomic64*>(ptr), new_value, __ATOMIC_ACQUIRE);
}
inline Atomic64 Release_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
return __atomic_exchange_n(const_cast<Atomic64*>(ptr), new_value, __ATOMIC_RELEASE);
}
inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
Atomic64 prev_value = old_value;
__atomic_compare_exchange_n(ptr, &prev_value, new_value,
0, __ATOMIC_ACQUIRE, __ATOMIC_RELAXED);
return prev_value;
}
inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
Atomic64 prev_value = old_value;
__atomic_compare_exchange_n(ptr, &prev_value, new_value,
0, __ATOMIC_RELEASE, __ATOMIC_RELAXED);
return prev_value;
}
inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
*ptr = value;
}
inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
MemoryBarrier();
*ptr = value;
}
inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
return *ptr;
}
inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
Atomic64 value = *ptr;
MemoryBarrier();
return value;
}
} // namespace base::subtle
} // namespace base
#endif // BASE_ATOMICOPS_INTERNALS_GCC_GENERIC_H_

View file

@ -0,0 +1,405 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2008, 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.
*
* ---
*/
// Implementation of atomic operations for ppc-linux. This file should not
// be included directly. Clients should instead include
// "base/atomicops.h".
#ifndef BASE_ATOMICOPS_INTERNALS_LINUXPPC_H_
#define BASE_ATOMICOPS_INTERNALS_LINUXPPC_H_
typedef int32_t Atomic32;
#ifdef __PPC64__
#define BASE_HAS_ATOMIC64 1
#endif
namespace base {
namespace subtle {
static inline void _sync(void) {
__asm__ __volatile__("sync": : : "memory");
}
static inline void _lwsync(void) {
// gcc defines __NO_LWSYNC__ when appropriate; see
// http://gcc.gnu.org/ml/gcc-patches/2006-11/msg01238.html
#ifdef __NO_LWSYNC__
__asm__ __volatile__("msync": : : "memory");
#else
__asm__ __volatile__("lwsync": : : "memory");
#endif
}
static inline void _isync(void) {
__asm__ __volatile__("isync": : : "memory");
}
static inline Atomic32 OSAtomicAdd32(Atomic32 amount, Atomic32 *value) {
Atomic32 t;
__asm__ __volatile__(
"1: lwarx %0,0,%3\n\
add %0,%2,%0\n\
stwcx. %0,0,%3 \n\
bne- 1b"
: "=&r" (t), "+m" (*value)
: "r" (amount), "r" (value)
: "cc");
return t;
}
static inline Atomic32 OSAtomicAdd32Barrier(Atomic32 amount, Atomic32 *value) {
Atomic32 t;
_lwsync();
t = OSAtomicAdd32(amount, value);
// This is based on the code snippet in the architecture manual (Vol
// 2, Appendix B). It's a little tricky: correctness depends on the
// fact that the code right before this (in OSAtomicAdd32) has a
// conditional branch with a data dependency on the update.
// Otherwise, we'd have to use sync.
_isync();
return t;
}
static inline bool OSAtomicCompareAndSwap32(Atomic32 old_value,
Atomic32 new_value,
Atomic32 *value) {
Atomic32 prev;
__asm__ __volatile__(
"1: lwarx %0,0,%2\n\
cmpw 0,%0,%3\n\
bne- 2f\n\
stwcx. %4,0,%2\n\
bne- 1b\n\
2:"
: "=&r" (prev), "+m" (*value)
: "r" (value), "r" (old_value), "r" (new_value)
: "cc");
return prev == old_value;
}
static inline Atomic32 OSAtomicCompareAndSwap32Acquire(Atomic32 old_value,
Atomic32 new_value,
Atomic32 *value) {
Atomic32 t;
t = OSAtomicCompareAndSwap32(old_value, new_value, value);
// This is based on the code snippet in the architecture manual (Vol
// 2, Appendix B). It's a little tricky: correctness depends on the
// fact that the code right before this (in
// OSAtomicCompareAndSwap32) has a conditional branch with a data
// dependency on the update. Otherwise, we'd have to use sync.
_isync();
return t;
}
static inline Atomic32 OSAtomicCompareAndSwap32Release(Atomic32 old_value,
Atomic32 new_value,
Atomic32 *value) {
_lwsync();
return OSAtomicCompareAndSwap32(old_value, new_value, value);
}
typedef int64_t Atomic64;
inline void MemoryBarrier() {
// This can't be _lwsync(); we need to order the immediately
// preceding stores against any load that may follow, but lwsync
// doesn't guarantee that.
_sync();
}
// 32-bit Versions.
inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32 *ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 prev_value;
do {
if (OSAtomicCompareAndSwap32(old_value, new_value,
const_cast<Atomic32*>(ptr))) {
return old_value;
}
prev_value = *ptr;
} while (prev_value == old_value);
return prev_value;
}
inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32 *ptr,
Atomic32 new_value) {
Atomic32 old_value;
do {
old_value = *ptr;
} while (!OSAtomicCompareAndSwap32(old_value, new_value,
const_cast<Atomic32*>(ptr)));
return old_value;
}
inline Atomic32 Acquire_AtomicExchange(volatile Atomic32 *ptr,
Atomic32 new_value) {
Atomic32 old_value;
do {
old_value = *ptr;
} while (!OSAtomicCompareAndSwap32Acquire(old_value, new_value,
const_cast<Atomic32*>(ptr)));
return old_value;
}
inline Atomic32 Release_AtomicExchange(volatile Atomic32 *ptr,
Atomic32 new_value) {
Atomic32 old_value;
do {
old_value = *ptr;
} while (!OSAtomicCompareAndSwap32Release(old_value, new_value,
const_cast<Atomic32*>(ptr)));
return old_value;
}
inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32 *ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 prev_value;
do {
if (OSAtomicCompareAndSwap32Acquire(old_value, new_value,
const_cast<Atomic32*>(ptr))) {
return old_value;
}
prev_value = *ptr;
} while (prev_value == old_value);
return prev_value;
}
inline Atomic32 Release_CompareAndSwap(volatile Atomic32 *ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 prev_value;
do {
if (OSAtomicCompareAndSwap32Release(old_value, new_value,
const_cast<Atomic32*>(ptr))) {
return old_value;
}
prev_value = *ptr;
} while (prev_value == old_value);
return prev_value;
}
#ifdef __PPC64__
// 64-bit Versions.
static inline Atomic64 OSAtomicAdd64(Atomic64 amount, Atomic64 *value) {
Atomic64 t;
__asm__ __volatile__(
"1: ldarx %0,0,%3\n\
add %0,%2,%0\n\
stdcx. %0,0,%3 \n\
bne- 1b"
: "=&r" (t), "+m" (*value)
: "r" (amount), "r" (value)
: "cc");
return t;
}
static inline Atomic64 OSAtomicAdd64Barrier(Atomic64 amount, Atomic64 *value) {
Atomic64 t;
_lwsync();
t = OSAtomicAdd64(amount, value);
// This is based on the code snippet in the architecture manual (Vol
// 2, Appendix B). It's a little tricky: correctness depends on the
// fact that the code right before this (in OSAtomicAdd64) has a
// conditional branch with a data dependency on the update.
// Otherwise, we'd have to use sync.
_isync();
return t;
}
static inline bool OSAtomicCompareAndSwap64(Atomic64 old_value,
Atomic64 new_value,
Atomic64 *value) {
Atomic64 prev;
__asm__ __volatile__(
"1: ldarx %0,0,%2\n\
cmpd 0,%0,%3\n\
bne- 2f\n\
stdcx. %4,0,%2\n\
bne- 1b\n\
2:"
: "=&r" (prev), "+m" (*value)
: "r" (value), "r" (old_value), "r" (new_value)
: "cc");
return prev == old_value;
}
static inline Atomic64 OSAtomicCompareAndSwap64Acquire(Atomic64 old_value,
Atomic64 new_value,
Atomic64 *value) {
Atomic64 t;
t = OSAtomicCompareAndSwap64(old_value, new_value, value);
// This is based on the code snippet in the architecture manual (Vol
// 2, Appendix B). It's a little tricky: correctness depends on the
// fact that the code right before this (in
// OSAtomicCompareAndSwap64) has a conditional branch with a data
// dependency on the update. Otherwise, we'd have to use sync.
_isync();
return t;
}
static inline Atomic64 OSAtomicCompareAndSwap64Release(Atomic64 old_value,
Atomic64 new_value,
Atomic64 *value) {
_lwsync();
return OSAtomicCompareAndSwap64(old_value, new_value, value);
}
inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64 *ptr,
Atomic64 old_value,
Atomic64 new_value) {
Atomic64 prev_value;
do {
if (OSAtomicCompareAndSwap64(old_value, new_value,
const_cast<Atomic64*>(ptr))) {
return old_value;
}
prev_value = *ptr;
} while (prev_value == old_value);
return prev_value;
}
inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64 *ptr,
Atomic64 new_value) {
Atomic64 old_value;
do {
old_value = *ptr;
} while (!OSAtomicCompareAndSwap64(old_value, new_value,
const_cast<Atomic64*>(ptr)));
return old_value;
}
inline Atomic64 Acquire_AtomicExchange(volatile Atomic64 *ptr,
Atomic64 new_value) {
Atomic64 old_value;
do {
old_value = *ptr;
} while (!OSAtomicCompareAndSwap64Acquire(old_value, new_value,
const_cast<Atomic64*>(ptr)));
return old_value;
}
inline Atomic64 Release_AtomicExchange(volatile Atomic64 *ptr,
Atomic64 new_value) {
Atomic64 old_value;
do {
old_value = *ptr;
} while (!OSAtomicCompareAndSwap64Release(old_value, new_value,
const_cast<Atomic64*>(ptr)));
return old_value;
}
inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64 *ptr,
Atomic64 old_value,
Atomic64 new_value) {
Atomic64 prev_value;
do {
if (OSAtomicCompareAndSwap64Acquire(old_value, new_value,
const_cast<Atomic64*>(ptr))) {
return old_value;
}
prev_value = *ptr;
} while (prev_value == old_value);
return prev_value;
}
inline Atomic64 Release_CompareAndSwap(volatile Atomic64 *ptr,
Atomic64 old_value,
Atomic64 new_value) {
Atomic64 prev_value;
do {
if (OSAtomicCompareAndSwap64Release(old_value, new_value,
const_cast<Atomic64*>(ptr))) {
return old_value;
}
prev_value = *ptr;
} while (prev_value == old_value);
return prev_value;
}
#endif
inline void NoBarrier_Store(volatile Atomic32 *ptr, Atomic32 value) {
*ptr = value;
}
inline void Release_Store(volatile Atomic32 *ptr, Atomic32 value) {
_lwsync();
*ptr = value;
}
inline Atomic32 NoBarrier_Load(volatile const Atomic32 *ptr) {
return *ptr;
}
inline Atomic32 Acquire_Load(volatile const Atomic32 *ptr) {
Atomic32 value = *ptr;
_lwsync();
return value;
}
#ifdef __PPC64__
// 64-bit Versions.
inline void NoBarrier_Store(volatile Atomic64 *ptr, Atomic64 value) {
*ptr = value;
}
inline void Release_Store(volatile Atomic64 *ptr, Atomic64 value) {
_lwsync();
*ptr = value;
}
inline Atomic64 NoBarrier_Load(volatile const Atomic64 *ptr) {
return *ptr;
}
inline Atomic64 Acquire_Load(volatile const Atomic64 *ptr) {
Atomic64 value = *ptr;
_lwsync();
return value;
}
#endif
} // namespace base::subtle
} // namespace base
#endif // BASE_ATOMICOPS_INTERNALS_LINUXPPC_H_

View file

@ -0,0 +1,341 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2006, 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.
*/
// Implementation of atomic operations for Mac OS X. This file should not
// be included directly. Clients should instead include
// "base/atomicops.h".
#ifndef BASE_ATOMICOPS_INTERNALS_MACOSX_H_
#define BASE_ATOMICOPS_INTERNALS_MACOSX_H_
typedef int32_t Atomic32;
// MacOS uses long for intptr_t, AtomicWord and Atomic32 are always different
// on the Mac, even when they are the same size. Similarly, on __ppc64__,
// AtomicWord and Atomic64 are always different. Thus, we need explicit
// casting.
#ifdef __LP64__
#define AtomicWordCastType base::subtle::Atomic64
#else
#define AtomicWordCastType Atomic32
#endif
#if defined(__LP64__) || defined(__i386__)
#define BASE_HAS_ATOMIC64 1 // Use only in tests and base/atomic*
#endif
#include <libkern/OSAtomic.h>
namespace base {
namespace subtle {
#if !defined(__LP64__) && defined(__ppc__)
// The Mac 64-bit OSAtomic implementations are not available for 32-bit PowerPC,
// while the underlying assembly instructions are available only some
// implementations of PowerPC.
// The following inline functions will fail with the error message at compile
// time ONLY IF they are called. So it is safe to use this header if user
// code only calls AtomicWord and Atomic32 operations.
//
// NOTE(vchen): Implementation notes to implement the atomic ops below may
// be found in "PowerPC Virtual Environment Architecture, Book II,
// Version 2.02", January 28, 2005, Appendix B, page 46. Unfortunately,
// extra care must be taken to ensure data are properly 8-byte aligned, and
// that data are returned correctly according to Mac OS X ABI specs.
inline int64_t OSAtomicCompareAndSwap64(
int64_t oldValue, int64_t newValue, int64_t *theValue) {
__asm__ __volatile__(
"_OSAtomicCompareAndSwap64_not_supported_for_32_bit_ppc\n\t");
return 0;
}
inline int64_t OSAtomicAdd64(int64_t theAmount, int64_t *theValue) {
__asm__ __volatile__(
"_OSAtomicAdd64_not_supported_for_32_bit_ppc\n\t");
return 0;
}
inline int64_t OSAtomicCompareAndSwap64Barrier(
int64_t oldValue, int64_t newValue, int64_t *theValue) {
int64_t prev = OSAtomicCompareAndSwap64(oldValue, newValue, theValue);
OSMemoryBarrier();
return prev;
}
inline int64_t OSAtomicAdd64Barrier(
int64_t theAmount, int64_t *theValue) {
int64_t new_val = OSAtomicAdd64(theAmount, theValue);
OSMemoryBarrier();
return new_val;
}
#endif
typedef int64_t Atomic64;
inline void MemoryBarrier() {
OSMemoryBarrier();
}
// 32-bit Versions.
inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32 *ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 prev_value;
do {
if (OSAtomicCompareAndSwap32(old_value, new_value,
const_cast<Atomic32*>(ptr))) {
return old_value;
}
prev_value = *ptr;
} while (prev_value == old_value);
return prev_value;
}
inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32 *ptr,
Atomic32 new_value) {
Atomic32 old_value;
do {
old_value = *ptr;
} while (!OSAtomicCompareAndSwap32(old_value, new_value,
const_cast<Atomic32*>(ptr)));
return old_value;
}
inline Atomic32 Acquire_AtomicExchange(volatile Atomic32 *ptr,
Atomic32 new_value) {
Atomic32 old_value;
do {
old_value = *ptr;
} while (!OSAtomicCompareAndSwap32Barrier(old_value, new_value,
const_cast<Atomic32*>(ptr)));
return old_value;
}
inline Atomic32 Release_AtomicExchange(volatile Atomic32 *ptr,
Atomic32 new_value) {
return Acquire_AtomicExchange(ptr, new_value);
}
inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32 *ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 prev_value;
do {
if (OSAtomicCompareAndSwap32Barrier(old_value, new_value,
const_cast<Atomic32*>(ptr))) {
return old_value;
}
prev_value = *ptr;
} while (prev_value == old_value);
return prev_value;
}
inline Atomic32 Release_CompareAndSwap(volatile Atomic32 *ptr,
Atomic32 old_value,
Atomic32 new_value) {
return Acquire_CompareAndSwap(ptr, old_value, new_value);
}
inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
*ptr = value;
}
inline void Release_Store(volatile Atomic32 *ptr, Atomic32 value) {
MemoryBarrier();
*ptr = value;
}
inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
return *ptr;
}
inline Atomic32 Acquire_Load(volatile const Atomic32 *ptr) {
Atomic32 value = *ptr;
MemoryBarrier();
return value;
}
// 64-bit version
inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64 *ptr,
Atomic64 old_value,
Atomic64 new_value) {
Atomic64 prev_value;
do {
if (OSAtomicCompareAndSwap64(old_value, new_value,
const_cast<Atomic64*>(ptr))) {
return old_value;
}
prev_value = *ptr;
} while (prev_value == old_value);
return prev_value;
}
inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64 *ptr,
Atomic64 new_value) {
Atomic64 old_value;
do {
old_value = *ptr;
} while (!OSAtomicCompareAndSwap64(old_value, new_value,
const_cast<Atomic64*>(ptr)));
return old_value;
}
inline Atomic64 Acquire_AtomicExchange(volatile Atomic64 *ptr,
Atomic64 new_value) {
Atomic64 old_value;
do {
old_value = *ptr;
} while (!OSAtomicCompareAndSwap64Barrier(old_value, new_value,
const_cast<Atomic64*>(ptr)));
return old_value;
}
inline Atomic64 Release_AtomicExchange(volatile Atomic64 *ptr,
Atomic64 new_value) {
return Acquire_AtomicExchange(ptr, new_value);
}
inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64 *ptr,
Atomic64 old_value,
Atomic64 new_value) {
Atomic64 prev_value;
do {
if (OSAtomicCompareAndSwap64Barrier(old_value, new_value,
const_cast<Atomic64*>(ptr))) {
return old_value;
}
prev_value = *ptr;
} while (prev_value == old_value);
return prev_value;
}
inline Atomic64 Release_CompareAndSwap(volatile Atomic64 *ptr,
Atomic64 old_value,
Atomic64 new_value) {
// The lib kern interface does not distinguish between
// Acquire and Release memory barriers; they are equivalent.
return Acquire_CompareAndSwap(ptr, old_value, new_value);
}
#ifdef __LP64__
// 64-bit implementation on 64-bit platform
inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
*ptr = value;
}
inline void Release_Store(volatile Atomic64 *ptr, Atomic64 value) {
MemoryBarrier();
*ptr = value;
}
inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
return *ptr;
}
inline Atomic64 Acquire_Load(volatile const Atomic64 *ptr) {
Atomic64 value = *ptr;
MemoryBarrier();
return value;
}
#else
// 64-bit implementation on 32-bit platform
#if defined(__ppc__)
inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
__asm__ __volatile__(
"_NoBarrier_Store_not_supported_for_32_bit_ppc\n\t");
}
inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
__asm__ __volatile__(
"_NoBarrier_Load_not_supported_for_32_bit_ppc\n\t");
return 0;
}
#elif defined(__i386__)
inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
__asm__ __volatile__("movq %1, %%mm0\n\t" // Use mmx reg for 64-bit atomic
"movq %%mm0, %0\n\t" // moves (ptr could be read-only)
"emms\n\t" // Reset FP registers
: "=m" (*ptr)
: "m" (value)
: // mark the FP stack and mmx registers as clobbered
"st", "st(1)", "st(2)", "st(3)", "st(4)",
"st(5)", "st(6)", "st(7)", "mm0", "mm1",
"mm2", "mm3", "mm4", "mm5", "mm6", "mm7");
}
inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
Atomic64 value;
__asm__ __volatile__("movq %1, %%mm0\n\t" // Use mmx reg for 64-bit atomic
"movq %%mm0, %0\n\t" // moves (ptr could be read-only)
"emms\n\t" // Reset FP registers
: "=m" (value)
: "m" (*ptr)
: // mark the FP stack and mmx registers as clobbered
"st", "st(1)", "st(2)", "st(3)", "st(4)",
"st(5)", "st(6)", "st(7)", "mm0", "mm1",
"mm2", "mm3", "mm4", "mm5", "mm6", "mm7");
return value;
}
#endif
inline void Release_Store(volatile Atomic64 *ptr, Atomic64 value) {
MemoryBarrier();
NoBarrier_Store(ptr, value);
}
inline Atomic64 Acquire_Load(volatile const Atomic64 *ptr) {
Atomic64 value = NoBarrier_Load(ptr);
MemoryBarrier();
return value;
}
#endif // __LP64__
} // namespace base::subtle
} // namespace base
#endif // BASE_ATOMICOPS_INTERNALS_MACOSX_H_

View file

@ -0,0 +1,299 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2013, 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: Jovan Zelincevic <jovan.zelincevic@imgtec.com>
// based on atomicops-internals by Sanjay Ghemawat
// This file is an internal atomic implementation, use base/atomicops.h instead.
//
// This code implements MIPS atomics.
#ifndef BASE_ATOMICOPS_INTERNALS_MIPS_H_
#define BASE_ATOMICOPS_INTERNALS_MIPS_H_
#if (_MIPS_ISA == _MIPS_ISA_MIPS64)
#define BASE_HAS_ATOMIC64 1
#endif
typedef int32_t Atomic32;
namespace base {
namespace subtle {
// Atomically execute:
// result = *ptr;
// if (*ptr == old_value)
// *ptr = new_value;
// return result;
//
// I.e., replace "*ptr" with "new_value" if "*ptr" used to be "old_value".
// Always return the old value of "*ptr"
//
// This routine implies no memory barriers.
inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value)
{
Atomic32 prev, tmp;
__asm__ volatile(
".set push \n"
".set noreorder \n"
"1: \n"
"ll %0, %5 \n" // prev = *ptr
"bne %0, %3, 2f \n" // if (prev != old_value) goto 2
" move %2, %4 \n" // tmp = new_value
"sc %2, %1 \n" // *ptr = tmp (with atomic check)
"beqz %2, 1b \n" // start again on atomic error
" nop \n" // delay slot nop
"2: \n"
".set pop \n"
: "=&r" (prev), "=m" (*ptr),
"=&r" (tmp)
: "Ir" (old_value), "r" (new_value),
"m" (*ptr)
: "memory"
);
return prev;
}
// Atomically store new_value into *ptr, returning the previous value held in
// *ptr. This routine implies no memory barriers.
inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value)
{
Atomic32 temp, old;
__asm__ volatile(
".set push \n"
".set noreorder \n"
"1: \n"
"ll %1, %2 \n" // old = *ptr
"move %0, %3 \n" // temp = new_value
"sc %0, %2 \n" // *ptr = temp (with atomic check)
"beqz %0, 1b \n" // start again on atomic error
" nop \n" // delay slot nop
".set pop \n"
: "=&r" (temp), "=&r" (old),
"=m" (*ptr)
: "r" (new_value), "m" (*ptr)
: "memory"
);
return old;
}
inline void MemoryBarrier()
{
__asm__ volatile("sync" : : : "memory");
}
// "Acquire" operations
// ensure that no later memory access can be reordered ahead of the operation.
// "Release" operations ensure that no previous memory access can be reordered
// after the operation. "Barrier" operations have both "Acquire" and "Release"
// semantics. A MemoryBarrier() has "Barrier" semantics, but does no memory
// access.
inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value)
{
Atomic32 res = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
MemoryBarrier();
return res;
}
inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value)
{
MemoryBarrier();
Atomic32 res = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
return res;
}
inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value)
{
*ptr = value;
}
inline Atomic32 Acquire_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value)
{
Atomic32 old_value = NoBarrier_AtomicExchange(ptr, new_value);
MemoryBarrier();
return old_value;
}
inline Atomic32 Release_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value)
{
MemoryBarrier();
return NoBarrier_AtomicExchange(ptr, new_value);
}
inline void Release_Store(volatile Atomic32* ptr, Atomic32 value)
{
MemoryBarrier();
*ptr = value;
}
inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr)
{
return *ptr;
}
inline Atomic32 Acquire_Load(volatile const Atomic32* ptr)
{
Atomic32 value = *ptr;
MemoryBarrier();
return value;
}
#if (_MIPS_ISA == _MIPS_ISA_MIPS64) || (_MIPS_SIM == _MIPS_SIM_ABI64)
typedef int64_t Atomic64;
inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value)
{
Atomic64 prev, tmp;
__asm__ volatile(
".set push \n"
".set noreorder \n"
"1: \n"
"lld %0, %5 \n" // prev = *ptr
"bne %0, %3, 2f \n" // if (prev != old_value) goto 2
" move %2, %4 \n" // tmp = new_value
"scd %2, %1 \n" // *ptr = tmp (with atomic check)
"beqz %2, 1b \n" // start again on atomic error
" nop \n" // delay slot nop
"2: \n"
".set pop \n"
: "=&r" (prev), "=m" (*ptr),
"=&r" (tmp)
: "Ir" (old_value), "r" (new_value),
"m" (*ptr)
: "memory"
);
return prev;
}
inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value)
{
Atomic64 temp, old;
__asm__ volatile(
".set push \n"
".set noreorder \n"
"1: \n"
"lld %1, %2 \n" // old = *ptr
"move %0, %3 \n" // temp = new_value
"scd %0, %2 \n" // *ptr = temp (with atomic check)
"beqz %0, 1b \n" // start again on atomic error
" nop \n" // delay slot nop
".set pop \n"
: "=&r" (temp), "=&r" (old),
"=m" (*ptr)
: "r" (new_value), "m" (*ptr)
: "memory"
);
return old;
}
inline Atomic64 Acquire_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value)
{
Atomic64 old_value = NoBarrier_AtomicExchange(ptr, new_value);
MemoryBarrier();
return old_value;
}
inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value)
{
Atomic64 res = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
MemoryBarrier();
return res;
}
inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value)
{
MemoryBarrier();
Atomic64 res = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
return res;
}
inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value)
{
*ptr = value;
}
inline Atomic64 Release_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value)
{
MemoryBarrier();
return NoBarrier_AtomicExchange(ptr, new_value);
}
inline void Release_Store(volatile Atomic64* ptr, Atomic64 value)
{
MemoryBarrier();
*ptr = value;
}
inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr)
{
return *ptr;
}
inline Atomic64 Acquire_Load(volatile const Atomic64* ptr)
{
Atomic64 value = *ptr;
MemoryBarrier();
return value;
}
#endif
} // namespace base::subtle
} // namespace base
#endif // BASE_ATOMICOPS_INTERNALS_MIPS_H_

View file

@ -0,0 +1,428 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2006, 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
*/
// Implementation of atomic operations using Windows API
// functions. This file should not be included directly. Clients
// should instead include "base/atomicops.h".
#ifndef BASE_ATOMICOPS_INTERNALS_WINDOWS_H_
#define BASE_ATOMICOPS_INTERNALS_WINDOWS_H_
#include <stdio.h>
#include <stdlib.h>
#include "base/basictypes.h" // For COMPILE_ASSERT
typedef int32 Atomic32;
#if defined(_WIN64)
#define BASE_HAS_ATOMIC64 1 // Use only in tests and base/atomic*
#endif
namespace base {
namespace subtle {
typedef int64 Atomic64;
// 32-bit low-level operations on any platform
extern "C" {
// We use windows intrinsics when we can (they seem to be supported
// well on MSVC 8.0 and above). Unfortunately, in some
// environments, <windows.h> and <intrin.h> have conflicting
// declarations of some other intrinsics, breaking compilation:
// http://connect.microsoft.com/VisualStudio/feedback/details/262047
// Therefore, we simply declare the relevant intrinsics ourself.
// MinGW has a bug in the header files where it doesn't indicate the
// first argument is volatile -- they're not up to date. See
// http://readlist.com/lists/lists.sourceforge.net/mingw-users/0/3861.html
// We have to const_cast away the volatile to avoid compiler warnings.
// TODO(csilvers): remove this once MinGW has updated MinGW/include/winbase.h
#if defined(__MINGW32__)
inline LONG FastInterlockedCompareExchange(volatile LONG* ptr,
LONG newval, LONG oldval) {
return ::InterlockedCompareExchange(const_cast<LONG*>(ptr), newval, oldval);
}
inline LONG FastInterlockedExchange(volatile LONG* ptr, LONG newval) {
return ::InterlockedExchange(const_cast<LONG*>(ptr), newval);
}
inline LONG FastInterlockedExchangeAdd(volatile LONG* ptr, LONG increment) {
return ::InterlockedExchangeAdd(const_cast<LONG*>(ptr), increment);
}
#elif _MSC_VER >= 1400 // intrinsics didn't work so well before MSVC 8.0
// Unfortunately, in some environments, <windows.h> and <intrin.h>
// have conflicting declarations of some intrinsics, breaking
// compilation. So we declare the intrinsics we need ourselves. See
// http://connect.microsoft.com/VisualStudio/feedback/details/262047
LONG _InterlockedCompareExchange(volatile LONG* ptr, LONG newval, LONG oldval);
#pragma intrinsic(_InterlockedCompareExchange)
inline LONG FastInterlockedCompareExchange(volatile LONG* ptr,
LONG newval, LONG oldval) {
return _InterlockedCompareExchange(ptr, newval, oldval);
}
LONG _InterlockedExchange(volatile LONG* ptr, LONG newval);
#pragma intrinsic(_InterlockedExchange)
inline LONG FastInterlockedExchange(volatile LONG* ptr, LONG newval) {
return _InterlockedExchange(ptr, newval);
}
LONG _InterlockedExchangeAdd(volatile LONG* ptr, LONG increment);
#pragma intrinsic(_InterlockedExchangeAdd)
inline LONG FastInterlockedExchangeAdd(volatile LONG* ptr, LONG increment) {
return _InterlockedExchangeAdd(ptr, increment);
}
#else
inline LONG FastInterlockedCompareExchange(volatile LONG* ptr,
LONG newval, LONG oldval) {
return ::InterlockedCompareExchange(ptr, newval, oldval);
}
inline LONG FastInterlockedExchange(volatile LONG* ptr, LONG newval) {
return ::InterlockedExchange(ptr, newval);
}
inline LONG FastInterlockedExchangeAdd(volatile LONG* ptr, LONG increment) {
return ::InterlockedExchangeAdd(ptr, increment);
}
#endif // ifdef __MINGW32__
} // extern "C"
inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
LONG result = FastInterlockedCompareExchange(
reinterpret_cast<volatile LONG*>(ptr),
static_cast<LONG>(new_value),
static_cast<LONG>(old_value));
return static_cast<Atomic32>(result);
}
inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
LONG result = FastInterlockedExchange(
reinterpret_cast<volatile LONG*>(ptr),
static_cast<LONG>(new_value));
return static_cast<Atomic32>(result);
}
inline Atomic32 Acquire_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
// FastInterlockedExchange has both acquire and release memory barriers.
return NoBarrier_AtomicExchange(ptr, new_value);
}
inline Atomic32 Release_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
// FastInterlockedExchange has both acquire and release memory barriers.
return NoBarrier_AtomicExchange(ptr, new_value);
}
} // namespace base::subtle
} // namespace base
// In msvc8/vs2005, winnt.h already contains a definition for
// MemoryBarrier in the global namespace. Add it there for earlier
// versions and forward to it from within the namespace.
#if !(defined(_MSC_VER) && _MSC_VER >= 1400)
inline void MemoryBarrier() {
Atomic32 value = 0;
base::subtle::NoBarrier_AtomicExchange(&value, 0);
// actually acts as a barrier in thisd implementation
}
#endif
namespace base {
namespace subtle {
inline void MemoryBarrier() {
::MemoryBarrier();
}
inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}
inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}
inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
*ptr = value;
}
inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
*ptr = value; // works w/o barrier for current Intel chips as of June 2005
// See comments in Atomic64 version of Release_Store() below.
}
inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
return *ptr;
}
inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
Atomic32 value = *ptr;
return value;
}
// 64-bit operations
#if defined(_WIN64) || defined(__MINGW64__)
// 64-bit low-level operations on 64-bit platform.
COMPILE_ASSERT(sizeof(Atomic64) == sizeof(PVOID), atomic_word_is_atomic);
// These are the intrinsics needed for 64-bit operations. Similar to the
// 32-bit case above.
extern "C" {
#if defined(__MINGW64__)
inline PVOID FastInterlockedCompareExchangePointer(volatile PVOID* ptr,
PVOID newval, PVOID oldval) {
return ::InterlockedCompareExchangePointer(const_cast<PVOID*>(ptr),
newval, oldval);
}
inline PVOID FastInterlockedExchangePointer(volatile PVOID* ptr, PVOID newval) {
return ::InterlockedExchangePointer(const_cast<PVOID*>(ptr), newval);
}
inline LONGLONG FastInterlockedExchangeAdd64(volatile LONGLONG* ptr,
LONGLONG increment) {
return ::InterlockedExchangeAdd64(const_cast<LONGLONG*>(ptr), increment);
}
#elif _MSC_VER >= 1400 // intrinsics didn't work so well before MSVC 8.0
// Like above, we need to declare the intrinsics ourselves.
PVOID _InterlockedCompareExchangePointer(volatile PVOID* ptr,
PVOID newval, PVOID oldval);
#pragma intrinsic(_InterlockedCompareExchangePointer)
inline PVOID FastInterlockedCompareExchangePointer(volatile PVOID* ptr,
PVOID newval, PVOID oldval) {
return _InterlockedCompareExchangePointer(const_cast<PVOID*>(ptr),
newval, oldval);
}
PVOID _InterlockedExchangePointer(volatile PVOID* ptr, PVOID newval);
#pragma intrinsic(_InterlockedExchangePointer)
inline PVOID FastInterlockedExchangePointer(volatile PVOID* ptr, PVOID newval) {
return _InterlockedExchangePointer(const_cast<PVOID*>(ptr), newval);
}
LONGLONG _InterlockedExchangeAdd64(volatile LONGLONG* ptr, LONGLONG increment);
#pragma intrinsic(_InterlockedExchangeAdd64)
inline LONGLONG FastInterlockedExchangeAdd64(volatile LONGLONG* ptr,
LONGLONG increment) {
return _InterlockedExchangeAdd64(const_cast<LONGLONG*>(ptr), increment);
}
#else
inline PVOID FastInterlockedCompareExchangePointer(volatile PVOID* ptr,
PVOID newval, PVOID oldval) {
return ::InterlockedCompareExchangePointer(ptr, newval, oldval);
}
inline PVOID FastInterlockedExchangePointer(volatile PVOID* ptr, PVOID newval) {
return ::InterlockedExchangePointer(ptr, newval);
}
inline LONGLONG FastInterlockedExchangeAdd64(volatile LONGLONG* ptr,
LONGLONG increment) {
return ::InterlockedExchangeAdd64(ptr, increment);
}
#endif // ifdef __MINGW64__
} // extern "C"
inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
PVOID result = FastInterlockedCompareExchangePointer(
reinterpret_cast<volatile PVOID*>(ptr),
reinterpret_cast<PVOID>(new_value), reinterpret_cast<PVOID>(old_value));
return reinterpret_cast<Atomic64>(result);
}
inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
PVOID result = FastInterlockedExchangePointer(
reinterpret_cast<volatile PVOID*>(ptr),
reinterpret_cast<PVOID>(new_value));
return reinterpret_cast<Atomic64>(result);
}
inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
*ptr = value;
}
inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
*ptr = value; // works w/o barrier for current Intel chips as of June 2005
// When new chips come out, check:
// IA-32 Intel Architecture Software Developer's Manual, Volume 3:
// System Programming Guide, Chatper 7: Multiple-processor management,
// Section 7.2, Memory Ordering.
// Last seen at:
// http://developer.intel.com/design/pentium4/manuals/index_new.htm
}
inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
return *ptr;
}
inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
Atomic64 value = *ptr;
return value;
}
#else // defined(_WIN64) || defined(__MINGW64__)
// 64-bit low-level operations on 32-bit platform
// TODO(vchen): The GNU assembly below must be converted to MSVC inline
// assembly. Then the file should be renamed to ...-x86-msvc.h, probably.
inline void NotImplementedFatalError(const char *function_name) {
fprintf(stderr, "64-bit %s() not implemented on this platform\n",
function_name);
abort();
}
inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
#if 0 // Not implemented
Atomic64 prev;
__asm__ __volatile__("movl (%3), %%ebx\n\t" // Move 64-bit new_value into
"movl 4(%3), %%ecx\n\t" // ecx:ebx
"lock; cmpxchg8b %1\n\t" // If edx:eax (old_value) same
: "=A" (prev) // as contents of ptr:
: "m" (*ptr), // ecx:ebx => ptr
"0" (old_value), // else:
"r" (&new_value) // old *ptr => edx:eax
: "memory", "%ebx", "%ecx");
return prev;
#else
NotImplementedFatalError("NoBarrier_CompareAndSwap");
return 0;
#endif
}
inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
#if 0 // Not implemented
__asm__ __volatile__(
"movl (%2), %%ebx\n\t" // Move 64-bit new_value into
"movl 4(%2), %%ecx\n\t" // ecx:ebx
"0:\n\t"
"movl %1, %%eax\n\t" // Read contents of ptr into
"movl 4%1, %%edx\n\t" // edx:eax
"lock; cmpxchg8b %1\n\t" // Attempt cmpxchg; if *ptr
"jnz 0b\n\t" // is no longer edx:eax, loop
: "=A" (new_value)
: "m" (*ptr),
"r" (&new_value)
: "memory", "%ebx", "%ecx");
return new_value; // Now it's the previous value.
#else
NotImplementedFatalError("NoBarrier_AtomicExchange");
return 0;
#endif
}
inline void NoBarrier_Store(volatile Atomic64* ptrValue, Atomic64 value)
{
__asm {
movq mm0, value; // Use mmx reg for 64-bit atomic moves
mov eax, ptrValue;
movq [eax], mm0;
emms; // Empty mmx state to enable FP registers
}
}
inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
NoBarrier_Store(ptr, value);
}
inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptrValue)
{
Atomic64 value;
__asm {
mov eax, ptrValue;
movq mm0, [eax]; // Use mmx reg for 64-bit atomic moves
movq value, mm0;
emms; // Empty mmx state to enable FP registers
}
return value;
}
inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
Atomic64 value = NoBarrier_Load(ptr);
return value;
}
#endif // defined(_WIN64) || defined(__MINGW64__)
inline Atomic64 Acquire_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
// FastInterlockedExchange has both acquire and release memory barriers.
return NoBarrier_AtomicExchange(ptr, new_value);
}
inline Atomic64 Release_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
// FastInterlockedExchange has both acquire and release memory barriers.
return NoBarrier_AtomicExchange(ptr, new_value);
}
inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}
inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}
} // namespace base::subtle
} // namespace base
#endif // BASE_ATOMICOPS_INTERNALS_WINDOWS_H_

View file

@ -0,0 +1,112 @@
// -*- 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.
*
* ---
* This module gets enough CPU information to optimize the
* atomicops module on x86.
*/
#include "base/atomicops.h"
#include "base/basictypes.h"
#include "base/googleinit.h"
#include "base/logging.h"
#include <string.h>
// This file only makes sense with atomicops-internals-x86.h -- it
// depends on structs that are defined in that file. If atomicops.h
// doesn't sub-include that file, then we aren't needed, and shouldn't
// try to do anything.
#ifdef BASE_ATOMICOPS_INTERNALS_X86_H_
// Inline cpuid instruction. In PIC compilations, %ebx contains the address
// of the global offset table. To avoid breaking such executables, this code
// must preserve that register's value across cpuid instructions.
#if defined(__i386__)
#define cpuid(a, b, c, d, inp) \
asm ("mov %%ebx, %%edi\n" \
"cpuid\n" \
"xchg %%edi, %%ebx\n" \
: "=a" (a), "=D" (b), "=c" (c), "=d" (d) : "a" (inp))
#elif defined (__x86_64__)
#define cpuid(a, b, c, d, inp) \
asm ("mov %%rbx, %%rdi\n" \
"cpuid\n" \
"xchg %%rdi, %%rbx\n" \
: "=a" (a), "=D" (b), "=c" (c), "=d" (d) : "a" (inp))
#endif
#if defined(cpuid) // initialize the struct only on x86
// Set the flags so that code will run correctly and conservatively
// until InitGoogle() is called.
struct AtomicOps_x86CPUFeatureStruct AtomicOps_Internalx86CPUFeatures = {
false, // no SSE2
false // no cmpxchg16b
};
// Initialize the AtomicOps_Internalx86CPUFeatures struct.
static void AtomicOps_Internalx86CPUFeaturesInit() {
uint32 eax;
uint32 ebx;
uint32 ecx;
uint32 edx;
// Get vendor string (issue CPUID with eax = 0)
cpuid(eax, ebx, ecx, edx, 0);
char vendor[13];
memcpy(vendor, &ebx, 4);
memcpy(vendor + 4, &edx, 4);
memcpy(vendor + 8, &ecx, 4);
vendor[12] = 0;
// get feature flags in ecx/edx, and family/model in eax
cpuid(eax, ebx, ecx, edx, 1);
int family = (eax >> 8) & 0xf; // family and model fields
int model = (eax >> 4) & 0xf;
if (family == 0xf) { // use extended family and model fields
family += (eax >> 20) & 0xff;
model += ((eax >> 16) & 0xf) << 4;
}
// edx bit 26 is SSE2 which we use to tell use whether we can use mfence
AtomicOps_Internalx86CPUFeatures.has_sse2 = ((edx >> 26) & 1);
// ecx bit 13 indicates whether the cmpxchg16b instruction is supported
AtomicOps_Internalx86CPUFeatures.has_cmpxchg16b = ((ecx >> 13) & 1);
}
REGISTER_MODULE_INITIALIZER(atomicops_x86, {
AtomicOps_Internalx86CPUFeaturesInit();
});
#endif
#endif /* ifdef BASE_ATOMICOPS_INTERNALS_X86_H_ */

View file

@ -0,0 +1,353 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2006, 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
*/
// Implementation of atomic operations for x86. This file should not
// be included directly. Clients should instead include
// "base/atomicops.h".
#ifndef BASE_ATOMICOPS_INTERNALS_X86_H_
#define BASE_ATOMICOPS_INTERNALS_X86_H_
#include "base/basictypes.h"
typedef int32_t Atomic32;
#define BASE_HAS_ATOMIC64 1 // Use only in tests and base/atomic*
// NOTE(vchen): x86 does not need to define AtomicWordCastType, because it
// already matches Atomic32 or Atomic64, depending on the platform.
// This struct is not part of the public API of this module; clients may not
// use it.
// Features of this x86. Values may not be correct before main() is run,
// but are set conservatively.
struct AtomicOps_x86CPUFeatureStruct {
bool has_sse2; // Processor has SSE2.
bool has_cmpxchg16b; // Processor supports cmpxchg16b instruction.
};
ATTRIBUTE_VISIBILITY_HIDDEN
extern struct AtomicOps_x86CPUFeatureStruct AtomicOps_Internalx86CPUFeatures;
#define ATOMICOPS_COMPILER_BARRIER() __asm__ __volatile__("" : : : "memory")
namespace base {
namespace subtle {
typedef int64_t Atomic64;
// 32-bit low-level operations on any platform.
inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 prev;
__asm__ __volatile__("lock; cmpxchgl %1,%2"
: "=a" (prev)
: "q" (new_value), "m" (*ptr), "0" (old_value)
: "memory");
return prev;
}
inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
__asm__ __volatile__("xchgl %1,%0" // The lock prefix is implicit for xchg.
: "=r" (new_value)
: "m" (*ptr), "0" (new_value)
: "memory");
return new_value; // Now it's the previous value.
}
inline Atomic32 Acquire_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
Atomic32 old_val = NoBarrier_AtomicExchange(ptr, new_value);
return old_val;
}
inline Atomic32 Release_AtomicExchange(volatile Atomic32* ptr,
Atomic32 new_value) {
// xchgl already has release memory barrier semantics.
return NoBarrier_AtomicExchange(ptr, new_value);
}
inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
Atomic32 x = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
return x;
}
inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}
inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) {
*ptr = value;
}
#if defined(__x86_64__)
// 64-bit implementations of memory barrier can be simpler, because it
// "mfence" is guaranteed to exist.
inline void MemoryBarrier() {
__asm__ __volatile__("mfence" : : : "memory");
}
#else
inline void MemoryBarrier() {
if (AtomicOps_Internalx86CPUFeatures.has_sse2) {
__asm__ __volatile__("mfence" : : : "memory");
} else { // mfence is faster but not present on PIII
Atomic32 x = 0;
Acquire_AtomicExchange(&x, 0);
}
}
#endif
inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
ATOMICOPS_COMPILER_BARRIER();
*ptr = value; // An x86 store acts as a release barrier.
// See comments in Atomic64 version of Release_Store(), below.
}
inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) {
return *ptr;
}
inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
Atomic32 value = *ptr; // An x86 load acts as a acquire barrier.
// See comments in Atomic64 version of Release_Store(), below.
ATOMICOPS_COMPILER_BARRIER();
return value;
}
#if defined(__x86_64__)
// 64-bit low-level operations on 64-bit platform.
inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
Atomic64 prev;
__asm__ __volatile__("lock; cmpxchgq %1,%2"
: "=a" (prev)
: "q" (new_value), "m" (*ptr), "0" (old_value)
: "memory");
return prev;
}
inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
__asm__ __volatile__("xchgq %1,%0" // The lock prefix is implicit for xchg.
: "=r" (new_value)
: "m" (*ptr), "0" (new_value)
: "memory");
return new_value; // Now it's the previous value.
}
inline Atomic64 Acquire_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
Atomic64 old_val = NoBarrier_AtomicExchange(ptr, new_value);
return old_val;
}
inline Atomic64 Release_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_value) {
// xchgq already has release memory barrier semantics.
return NoBarrier_AtomicExchange(ptr, new_value);
}
inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
*ptr = value;
}
inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
ATOMICOPS_COMPILER_BARRIER();
*ptr = value; // An x86 store acts as a release barrier
// for current AMD/Intel chips as of Jan 2008.
// See also Acquire_Load(), below.
// When new chips come out, check:
// IA-32 Intel Architecture Software Developer's Manual, Volume 3:
// System Programming Guide, Chatper 7: Multiple-processor management,
// Section 7.2, Memory Ordering.
// Last seen at:
// http://developer.intel.com/design/pentium4/manuals/index_new.htm
//
// x86 stores/loads fail to act as barriers for a few instructions (clflush
// maskmovdqu maskmovq movntdq movnti movntpd movntps movntq) but these are
// not generated by the compiler, and are rare. Users of these instructions
// need to know about cache behaviour in any case since all of these involve
// either flushing cache lines or non-temporal cache hints.
}
inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
return *ptr;
}
inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
Atomic64 value = *ptr; // An x86 load acts as a acquire barrier,
// for current AMD/Intel chips as of Jan 2008.
// See also Release_Store(), above.
ATOMICOPS_COMPILER_BARRIER();
return value;
}
#else // defined(__x86_64__)
// 64-bit low-level operations on 32-bit platform.
#if !((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1))
// For compilers older than gcc 4.1, we use inline asm.
//
// Potential pitfalls:
//
// 1. %ebx points to Global offset table (GOT) with -fPIC.
// We need to preserve this register.
// 2. When explicit registers are used in inline asm, the
// compiler may not be aware of it and might try to reuse
// the same register for another argument which has constraints
// that allow it ("r" for example).
inline Atomic64 __sync_val_compare_and_swap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
Atomic64 prev;
__asm__ __volatile__("push %%ebx\n\t"
"movl (%3), %%ebx\n\t" // Move 64-bit new_value into
"movl 4(%3), %%ecx\n\t" // ecx:ebx
"lock; cmpxchg8b (%1)\n\t"// If edx:eax (old_value) same
"pop %%ebx\n\t"
: "=A" (prev) // as contents of ptr:
: "D" (ptr), // ecx:ebx => ptr
"0" (old_value), // else:
"S" (&new_value) // old *ptr => edx:eax
: "memory", "%ecx");
return prev;
}
#endif // Compiler < gcc-4.1
inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_val,
Atomic64 new_val) {
return __sync_val_compare_and_swap(ptr, old_val, new_val);
}
inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_val) {
Atomic64 old_val;
do {
old_val = *ptr;
} while (__sync_val_compare_and_swap(ptr, old_val, new_val) != old_val);
return old_val;
}
inline Atomic64 Acquire_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_val) {
Atomic64 old_val = NoBarrier_AtomicExchange(ptr, new_val);
return old_val;
}
inline Atomic64 Release_AtomicExchange(volatile Atomic64* ptr,
Atomic64 new_val) {
return NoBarrier_AtomicExchange(ptr, new_val);
}
inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) {
__asm__ __volatile__("movq %1, %%mm0\n\t" // Use mmx reg for 64-bit atomic
"movq %%mm0, %0\n\t" // moves (ptr could be read-only)
"emms\n\t" // Empty mmx state/Reset FP regs
: "=m" (*ptr)
: "m" (value)
: // mark the FP stack and mmx registers as clobbered
"st", "st(1)", "st(2)", "st(3)", "st(4)",
"st(5)", "st(6)", "st(7)", "mm0", "mm1",
"mm2", "mm3", "mm4", "mm5", "mm6", "mm7");
}
inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) {
ATOMICOPS_COMPILER_BARRIER();
NoBarrier_Store(ptr, value);
}
inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) {
Atomic64 value;
__asm__ __volatile__("movq %1, %%mm0\n\t" // Use mmx reg for 64-bit atomic
"movq %%mm0, %0\n\t" // moves (ptr could be read-only)
"emms\n\t" // Empty mmx state/Reset FP regs
: "=m" (value)
: "m" (*ptr)
: // mark the FP stack and mmx registers as clobbered
"st", "st(1)", "st(2)", "st(3)", "st(4)",
"st(5)", "st(6)", "st(7)", "mm0", "mm1",
"mm2", "mm3", "mm4", "mm5", "mm6", "mm7");
return value;
}
inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) {
Atomic64 value = NoBarrier_Load(ptr);
ATOMICOPS_COMPILER_BARRIER();
return value;
}
#endif // defined(__x86_64__)
inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
Atomic64 x = NoBarrier_CompareAndSwap(ptr, old_value, new_value);
return x;
}
inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value) {
return NoBarrier_CompareAndSwap(ptr, old_value, new_value);
}
} // namespace base::subtle
} // namespace base
#undef ATOMICOPS_COMPILER_BARRIER
#endif // BASE_ATOMICOPS_INTERNALS_X86_H_

View file

@ -0,0 +1,363 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2006, 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
*/
// For atomic operations on statistics counters, see atomic_stats_counter.h.
// For atomic operations on sequence numbers, see atomic_sequence_num.h.
// For atomic operations on reference counts, see atomic_refcount.h.
// Some fast atomic operations -- typically with machine-dependent
// implementations. This file may need editing as Google code is
// ported to different architectures.
// The routines exported by this module are subtle. If you use them, even if
// you get the code right, it will depend on careful reasoning about atomicity
// and memory ordering; it will be less readable, and harder to maintain. If
// you plan to use these routines, you should have a good reason, such as solid
// evidence that performance would otherwise suffer, or there being no
// alternative. You should assume only properties explicitly guaranteed by the
// specifications in this file. You are almost certainly _not_ writing code
// just for the x86; if you assume x86 semantics, x86 hardware bugs and
// implementations on other archtectures will cause your code to break. If you
// do not know what you are doing, avoid these routines, and use a Mutex.
//
// These following lower-level operations are typically useful only to people
// implementing higher-level synchronization operations like spinlocks,
// mutexes, and condition-variables. They combine CompareAndSwap(), a load, or
// a store with appropriate memory-ordering instructions. "Acquire" operations
// ensure that no later memory access can be reordered ahead of the operation.
// "Release" operations ensure that no previous memory access can be reordered
// after the operation. "Barrier" operations have both "Acquire" and "Release"
// semantics. A MemoryBarrier() has "Barrier" semantics, but does no memory
// access.
//
// It is incorrect to make direct assignments to/from an atomic variable.
// You should use one of the Load or Store routines. The NoBarrier
// versions are provided when no barriers are needed:
// NoBarrier_Store()
// NoBarrier_Load()
// Although there are currently no compiler enforcement, you are encouraged
// to use these. Moreover, if you choose to use base::subtle::Atomic64 type,
// you MUST use one of the Load or Store routines to get correct behavior
// on 32-bit platforms.
//
// The intent is eventually to put all of these routines in namespace
// base::subtle
#ifndef THREAD_ATOMICOPS_H_
#define THREAD_ATOMICOPS_H_
#include <config.h>
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
// ------------------------------------------------------------------------
// Include the platform specific implementations of the types
// and operations listed below. Implementations are to provide Atomic32
// and Atomic64 operations. If there is a mismatch between intptr_t and
// the Atomic32 or Atomic64 types for a platform, the platform-specific header
// should define the macro, AtomicWordCastType in a clause similar to the
// following:
// #if ...pointers are 64 bits...
// # define AtomicWordCastType base::subtle::Atomic64
// #else
// # define AtomicWordCastType Atomic32
// #endif
// TODO(csilvers): figure out ARCH_PIII/ARCH_K8 (perhaps via ./configure?)
// ------------------------------------------------------------------------
#include "base/arm_instruction_set_select.h"
#define GCC_VERSION (__GNUC__ * 10000 \
+ __GNUC_MINOR__ * 100 \
+ __GNUC_PATCHLEVEL__)
#define CLANG_VERSION (__clang_major__ * 10000 \
+ __clang_minor__ * 100 \
+ __clang_patchlevel__)
#if defined(TCMALLOC_PREFER_GCC_ATOMICS) && defined(__GNUC__) && GCC_VERSION >= 40700
#include "base/atomicops-internals-gcc.h"
#elif defined(TCMALLOC_PREFER_GCC_ATOMICS) && defined(__clang__) && CLANG_VERSION >= 30400
#include "base/atomicops-internals-gcc.h"
#elif defined(__MACH__) && defined(__APPLE__)
#include "base/atomicops-internals-macosx.h"
#elif defined(__GNUC__) && defined(ARMV6)
#include "base/atomicops-internals-arm-v6plus.h"
#elif defined(ARMV3)
#include "base/atomicops-internals-arm-generic.h"
#elif defined(__GNUC__) && (defined(__i386) || defined(__x86_64__))
#include "base/atomicops-internals-x86.h"
#elif defined(_WIN32)
#include "base/atomicops-internals-windows.h"
#elif defined(__linux__) && defined(__PPC__)
#include "base/atomicops-internals-linuxppc.h"
#elif defined(__GNUC__) && defined(__mips__)
#include "base/atomicops-internals-mips.h"
#elif defined(__GNUC__) && GCC_VERSION >= 40700
#include "base/atomicops-internals-gcc.h"
#elif defined(__clang__) && CLANG_VERSION >= 30400
#include "base/atomicops-internals-gcc.h"
#else
#error You need to implement atomic operations for this architecture
#endif
// Signed type that can hold a pointer and supports the atomic ops below, as
// well as atomic loads and stores. Instances must be naturally-aligned.
typedef intptr_t AtomicWord;
#ifdef AtomicWordCastType
// ------------------------------------------------------------------------
// This section is needed only when explicit type casting is required to
// cast AtomicWord to one of the basic atomic types (Atomic64 or Atomic32).
// It also serves to document the AtomicWord interface.
// ------------------------------------------------------------------------
namespace base {
namespace subtle {
// Atomically execute:
// result = *ptr;
// if (*ptr == old_value)
// *ptr = new_value;
// return result;
//
// I.e., replace "*ptr" with "new_value" if "*ptr" used to be "old_value".
// Always return the old value of "*ptr"
//
// This routine implies no memory barriers.
inline AtomicWord NoBarrier_CompareAndSwap(volatile AtomicWord* ptr,
AtomicWord old_value,
AtomicWord new_value) {
return NoBarrier_CompareAndSwap(
reinterpret_cast<volatile AtomicWordCastType*>(ptr),
old_value, new_value);
}
// Atomically store new_value into *ptr, returning the previous value held in
// *ptr. This routine implies no memory barriers.
inline AtomicWord NoBarrier_AtomicExchange(volatile AtomicWord* ptr,
AtomicWord new_value) {
return NoBarrier_AtomicExchange(
reinterpret_cast<volatile AtomicWordCastType*>(ptr), new_value);
}
inline AtomicWord Acquire_AtomicExchange(volatile AtomicWord* ptr,
AtomicWord new_value) {
return Acquire_AtomicExchange(
reinterpret_cast<volatile AtomicWordCastType*>(ptr), new_value);
}
inline AtomicWord Release_AtomicExchange(volatile AtomicWord* ptr,
AtomicWord new_value) {
return Release_AtomicExchange(
reinterpret_cast<volatile AtomicWordCastType*>(ptr), new_value);
}
inline AtomicWord Acquire_CompareAndSwap(volatile AtomicWord* ptr,
AtomicWord old_value,
AtomicWord new_value) {
return base::subtle::Acquire_CompareAndSwap(
reinterpret_cast<volatile AtomicWordCastType*>(ptr),
old_value, new_value);
}
inline AtomicWord Release_CompareAndSwap(volatile AtomicWord* ptr,
AtomicWord old_value,
AtomicWord new_value) {
return base::subtle::Release_CompareAndSwap(
reinterpret_cast<volatile AtomicWordCastType*>(ptr),
old_value, new_value);
}
inline void NoBarrier_Store(volatile AtomicWord *ptr, AtomicWord value) {
NoBarrier_Store(
reinterpret_cast<volatile AtomicWordCastType*>(ptr), value);
}
inline void Release_Store(volatile AtomicWord* ptr, AtomicWord value) {
return base::subtle::Release_Store(
reinterpret_cast<volatile AtomicWordCastType*>(ptr), value);
}
inline AtomicWord NoBarrier_Load(volatile const AtomicWord *ptr) {
return NoBarrier_Load(
reinterpret_cast<volatile const AtomicWordCastType*>(ptr));
}
inline AtomicWord Acquire_Load(volatile const AtomicWord* ptr) {
return base::subtle::Acquire_Load(
reinterpret_cast<volatile const AtomicWordCastType*>(ptr));
}
} // namespace base::subtle
} // namespace base
#endif // AtomicWordCastType
// ------------------------------------------------------------------------
// Commented out type definitions and method declarations for documentation
// of the interface provided by this module.
// ------------------------------------------------------------------------
#if 0
// Signed 32-bit type that supports the atomic ops below, as well as atomic
// loads and stores. Instances must be naturally aligned. This type differs
// from AtomicWord in 64-bit binaries where AtomicWord is 64-bits.
typedef int32_t Atomic32;
// Corresponding operations on Atomic32
namespace base {
namespace subtle {
// Signed 64-bit type that supports the atomic ops below, as well as atomic
// loads and stores. Instances must be naturally aligned. This type differs
// from AtomicWord in 32-bit binaries where AtomicWord is 32-bits.
typedef int64_t Atomic64;
Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value);
Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, Atomic32 new_value);
Atomic32 Acquire_AtomicExchange(volatile Atomic32* ptr, Atomic32 new_value);
Atomic32 Release_AtomicExchange(volatile Atomic32* ptr, Atomic32 new_value);
Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value);
Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value);
void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value);
void Release_Store(volatile Atomic32* ptr, Atomic32 value);
Atomic32 NoBarrier_Load(volatile const Atomic32* ptr);
Atomic32 Acquire_Load(volatile const Atomic32* ptr);
// Corresponding operations on Atomic64
Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value);
Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, Atomic64 new_value);
Atomic64 Acquire_AtomicExchange(volatile Atomic64* ptr, Atomic64 new_value);
Atomic64 Release_AtomicExchange(volatile Atomic64* ptr, Atomic64 new_value);
Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value);
Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr,
Atomic64 old_value,
Atomic64 new_value);
void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value);
void Release_Store(volatile Atomic64* ptr, Atomic64 value);
Atomic64 NoBarrier_Load(volatile const Atomic64* ptr);
Atomic64 Acquire_Load(volatile const Atomic64* ptr);
} // namespace base::subtle
} // namespace base
void MemoryBarrier();
#endif // 0
// ------------------------------------------------------------------------
// The following are to be deprecated when all uses have been changed to
// use the base::subtle namespace.
// ------------------------------------------------------------------------
#ifdef AtomicWordCastType
// AtomicWord versions to be deprecated
inline AtomicWord Acquire_CompareAndSwap(volatile AtomicWord* ptr,
AtomicWord old_value,
AtomicWord new_value) {
return base::subtle::Acquire_CompareAndSwap(ptr, old_value, new_value);
}
inline AtomicWord Release_CompareAndSwap(volatile AtomicWord* ptr,
AtomicWord old_value,
AtomicWord new_value) {
return base::subtle::Release_CompareAndSwap(ptr, old_value, new_value);
}
inline void Release_Store(volatile AtomicWord* ptr, AtomicWord value) {
return base::subtle::Release_Store(ptr, value);
}
inline AtomicWord Acquire_Load(volatile const AtomicWord* ptr) {
return base::subtle::Acquire_Load(ptr);
}
#endif // AtomicWordCastType
// 32-bit Acquire/Release operations to be deprecated.
inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
return base::subtle::Acquire_CompareAndSwap(ptr, old_value, new_value);
}
inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr,
Atomic32 old_value,
Atomic32 new_value) {
return base::subtle::Release_CompareAndSwap(ptr, old_value, new_value);
}
inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) {
return base::subtle::Release_Store(ptr, value);
}
inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) {
return base::subtle::Acquire_Load(ptr);
}
#ifdef BASE_HAS_ATOMIC64
// 64-bit Acquire/Release operations to be deprecated.
inline base::subtle::Atomic64 Acquire_CompareAndSwap(
volatile base::subtle::Atomic64* ptr,
base::subtle::Atomic64 old_value, base::subtle::Atomic64 new_value) {
return base::subtle::Acquire_CompareAndSwap(ptr, old_value, new_value);
}
inline base::subtle::Atomic64 Release_CompareAndSwap(
volatile base::subtle::Atomic64* ptr,
base::subtle::Atomic64 old_value, base::subtle::Atomic64 new_value) {
return base::subtle::Release_CompareAndSwap(ptr, old_value, new_value);
}
inline void Release_Store(
volatile base::subtle::Atomic64* ptr, base::subtle::Atomic64 value) {
return base::subtle::Release_Store(ptr, value);
}
inline base::subtle::Atomic64 Acquire_Load(
volatile const base::subtle::Atomic64* ptr) {
return base::subtle::Acquire_Load(ptr);
}
#endif // BASE_HAS_ATOMIC64
#endif // THREAD_ATOMICOPS_H_

View file

@ -0,0 +1,440 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2005, 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.
#ifndef _BASICTYPES_H_
#define _BASICTYPES_H_
#include <config.h>
#include <string.h> // for memcpy()
#ifdef HAVE_INTTYPES_H
#include <inttypes.h> // gets us PRId64, etc
#endif
// To use this in an autoconf setting, make sure you run the following
// autoconf macros:
// AC_HEADER_STDC /* for stdint_h and inttypes_h */
// AC_CHECK_TYPES([__int64]) /* defined in some windows platforms */
#ifdef HAVE_INTTYPES_H
#include <inttypes.h> // uint16_t might be here; PRId64 too.
#endif
#ifdef HAVE_STDINT_H
#include <stdint.h> // to get uint16_t (ISO naming madness)
#endif
#include <sys/types.h> // our last best hope for uint16_t
// Standard typedefs
// All Google code is compiled with -funsigned-char to make "char"
// unsigned. Google code therefore doesn't need a "uchar" type.
// TODO(csilvers): how do we make sure unsigned-char works on non-gcc systems?
typedef signed char schar;
typedef int8_t int8;
typedef int16_t int16;
typedef int32_t int32;
typedef int64_t int64;
// NOTE: unsigned types are DANGEROUS in loops and other arithmetical
// places. Use the signed types unless your variable represents a bit
// pattern (eg a hash value) or you really need the extra bit. Do NOT
// use 'unsigned' to express "this value should always be positive";
// use assertions for this.
typedef uint8_t uint8;
typedef uint16_t uint16;
typedef uint32_t uint32;
typedef uint64_t uint64;
const uint16 kuint16max = ( (uint16) 0xFFFF);
const uint32 kuint32max = ( (uint32) 0xFFFFFFFF);
const uint64 kuint64max = ( (((uint64) kuint32max) << 32) | kuint32max );
const int8 kint8max = ( ( int8) 0x7F);
const int16 kint16max = ( ( int16) 0x7FFF);
const int32 kint32max = ( ( int32) 0x7FFFFFFF);
const int64 kint64max = ( ((( int64) kint32max) << 32) | kuint32max );
const int8 kint8min = ( ( int8) 0x80);
const int16 kint16min = ( ( int16) 0x8000);
const int32 kint32min = ( ( int32) 0x80000000);
const int64 kint64min = ( (((uint64) kint32min) << 32) | 0 );
// Define the "portable" printf and scanf macros, if they're not
// already there (via the inttypes.h we #included above, hopefully).
// Mostly it's old systems that don't support inttypes.h, so we assume
// they're 32 bit.
#ifndef PRIx64
#define PRIx64 "llx"
#endif
#ifndef SCNx64
#define SCNx64 "llx"
#endif
#ifndef PRId64
#define PRId64 "lld"
#endif
#ifndef SCNd64
#define SCNd64 "lld"
#endif
#ifndef PRIu64
#define PRIu64 "llu"
#endif
#ifndef PRIxPTR
#define PRIxPTR "lx"
#endif
// Also allow for printing of a pthread_t.
#define GPRIuPTHREAD "lu"
#define GPRIxPTHREAD "lx"
#if defined(__CYGWIN__) || defined(__CYGWIN32__) || defined(__APPLE__) || defined(__FreeBSD__)
#define PRINTABLE_PTHREAD(pthreadt) reinterpret_cast<uintptr_t>(pthreadt)
#else
#define PRINTABLE_PTHREAD(pthreadt) pthreadt
#endif
#if defined(__GNUC__)
#define PREDICT_TRUE(x) __builtin_expect(!!(x), 1)
#define PREDICT_FALSE(x) __builtin_expect(!!(x), 0)
#else
#define PREDICT_TRUE(x) (x)
#define PREDICT_FALSE(x) (x)
#endif
// A macro to disallow the evil copy constructor and operator= functions
// This should be used in the private: declarations for a class
#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) \
TypeName(const TypeName&); \
void operator=(const TypeName&)
// An alternate name that leaves out the moral judgment... :-)
#define DISALLOW_COPY_AND_ASSIGN(TypeName) DISALLOW_EVIL_CONSTRUCTORS(TypeName)
// The COMPILE_ASSERT macro can be used to verify that a compile time
// expression is true. For example, you could use it to verify the
// size of a static array:
//
// COMPILE_ASSERT(sizeof(num_content_type_names) == sizeof(int),
// content_type_names_incorrect_size);
//
// or to make sure a struct is smaller than a certain size:
//
// COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large);
//
// The second argument to the macro is the name of the variable. If
// the expression is false, most compilers will issue a warning/error
// containing the name of the variable.
//
// Implementation details of COMPILE_ASSERT:
//
// - COMPILE_ASSERT works by defining an array type that has -1
// elements (and thus is invalid) when the expression is false.
//
// - The simpler definition
//
// #define COMPILE_ASSERT(expr, msg) typedef char msg[(expr) ? 1 : -1]
//
// does not work, as gcc supports variable-length arrays whose sizes
// are determined at run-time (this is gcc's extension and not part
// of the C++ standard). As a result, gcc fails to reject the
// following code with the simple definition:
//
// int foo;
// COMPILE_ASSERT(foo, msg); // not supposed to compile as foo is
// // not a compile-time constant.
//
// - By using the type CompileAssert<(bool(expr))>, we ensures that
// expr is a compile-time constant. (Template arguments must be
// determined at compile-time.)
//
// - The outter parentheses in CompileAssert<(bool(expr))> are necessary
// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written
//
// CompileAssert<bool(expr)>
//
// instead, these compilers will refuse to compile
//
// COMPILE_ASSERT(5 > 0, some_message);
//
// (They seem to think the ">" in "5 > 0" marks the end of the
// template argument list.)
//
// - The array size is (bool(expr) ? 1 : -1), instead of simply
//
// ((expr) ? 1 : -1).
//
// This is to avoid running into a bug in MS VC 7.1, which
// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1.
template <bool>
struct CompileAssert {
};
#ifdef HAVE___ATTRIBUTE__
# define ATTRIBUTE_UNUSED __attribute__((unused))
#else
# define ATTRIBUTE_UNUSED
#endif
#if defined(HAVE___ATTRIBUTE__) && defined(HAVE_TLS)
#define ATTR_INITIAL_EXEC __attribute__ ((tls_model ("initial-exec")))
#else
#define ATTR_INITIAL_EXEC
#endif
#define COMPILE_ASSERT(expr, msg) \
typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1] ATTRIBUTE_UNUSED
#define arraysize(a) (sizeof(a) / sizeof(*(a)))
#define OFFSETOF_MEMBER(strct, field) \
(reinterpret_cast<char*>(&reinterpret_cast<strct*>(16)->field) - \
reinterpret_cast<char*>(16))
// bit_cast<Dest,Source> implements the equivalent of
// "*reinterpret_cast<Dest*>(&source)".
//
// The reinterpret_cast method would produce undefined behavior
// according to ISO C++ specification section 3.10 -15 -.
// bit_cast<> calls memcpy() which is blessed by the standard,
// especially by the example in section 3.9.
//
// Fortunately memcpy() is very fast. In optimized mode, with a
// constant size, gcc 2.95.3, gcc 4.0.1, and msvc 7.1 produce inline
// code with the minimal amount of data movement. On a 32-bit system,
// memcpy(d,s,4) compiles to one load and one store, and memcpy(d,s,8)
// compiles to two loads and two stores.
template <class Dest, class Source>
inline Dest bit_cast(const Source& source) {
COMPILE_ASSERT(sizeof(Dest) == sizeof(Source), bitcasting_unequal_sizes);
Dest dest;
memcpy(&dest, &source, sizeof(dest));
return dest;
}
// bit_store<Dest,Source> implements the equivalent of
// "dest = *reinterpret_cast<Dest*>(&source)".
//
// This prevents undefined behavior when the dest pointer is unaligned.
template <class Dest, class Source>
inline void bit_store(Dest *dest, const Source *source) {
COMPILE_ASSERT(sizeof(Dest) == sizeof(Source), bitcasting_unequal_sizes);
memcpy(dest, source, sizeof(Dest));
}
#ifdef HAVE___ATTRIBUTE__
# define ATTRIBUTE_WEAK __attribute__((weak))
# define ATTRIBUTE_NOINLINE __attribute__((noinline))
#else
# define ATTRIBUTE_WEAK
# define ATTRIBUTE_NOINLINE
#endif
#if defined(HAVE___ATTRIBUTE__) && defined(__ELF__)
# define ATTRIBUTE_VISIBILITY_HIDDEN __attribute__((visibility("hidden")))
#else
# define ATTRIBUTE_VISIBILITY_HIDDEN
#endif
// Section attributes are supported for both ELF and Mach-O, but in
// very different ways. Here's the API we provide:
// 1) ATTRIBUTE_SECTION: put this with the declaration of all functions
// you want to be in the same linker section
// 2) DEFINE_ATTRIBUTE_SECTION_VARS: must be called once per unique
// name. You want to make sure this is executed before any
// DECLARE_ATTRIBUTE_SECTION_VARS; the easiest way is to put them
// in the same .cc file. Put this call at the global level.
// 3) INIT_ATTRIBUTE_SECTION_VARS: you can scatter calls to this in
// multiple places to help ensure execution before any
// DECLARE_ATTRIBUTE_SECTION_VARS. You must have at least one
// DEFINE, but you can have many INITs. Put each in its own scope.
// 4) DECLARE_ATTRIBUTE_SECTION_VARS: must be called before using
// ATTRIBUTE_SECTION_START or ATTRIBUTE_SECTION_STOP on a name.
// Put this call at the global level.
// 5) ATTRIBUTE_SECTION_START/ATTRIBUTE_SECTION_STOP: call this to say
// where in memory a given section is. All functions declared with
// ATTRIBUTE_SECTION are guaranteed to be between START and STOP.
#if defined(HAVE___ATTRIBUTE__) && defined(__ELF__)
# define ATTRIBUTE_SECTION(name) __attribute__ ((section (#name))) __attribute__((noinline))
// Weak section declaration to be used as a global declaration
// for ATTRIBUTE_SECTION_START|STOP(name) to compile and link
// even without functions with ATTRIBUTE_SECTION(name).
# define DECLARE_ATTRIBUTE_SECTION_VARS(name) \
extern char __start_##name[] ATTRIBUTE_WEAK; \
extern char __stop_##name[] ATTRIBUTE_WEAK
# define INIT_ATTRIBUTE_SECTION_VARS(name) // no-op for ELF
# define DEFINE_ATTRIBUTE_SECTION_VARS(name) // no-op for ELF
// Return void* pointers to start/end of a section of code with functions
// having ATTRIBUTE_SECTION(name), or 0 if no such function exists.
// One must DECLARE_ATTRIBUTE_SECTION(name) for this to compile and link.
# define ATTRIBUTE_SECTION_START(name) (reinterpret_cast<void*>(__start_##name))
# define ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast<void*>(__stop_##name))
# define HAVE_ATTRIBUTE_SECTION_START 1
#elif defined(HAVE___ATTRIBUTE__) && defined(__MACH__)
# define ATTRIBUTE_SECTION(name) __attribute__ ((section ("__TEXT, " #name)))
#include <mach-o/getsect.h>
#include <mach-o/dyld.h>
class AssignAttributeStartEnd {
public:
AssignAttributeStartEnd(const char* name, char** pstart, char** pend) {
// Find out what dynamic library name is defined in
if (_dyld_present()) {
for (int i = _dyld_image_count() - 1; i >= 0; --i) {
const mach_header* hdr = _dyld_get_image_header(i);
#ifdef MH_MAGIC_64
if (hdr->magic == MH_MAGIC_64) {
uint64_t len;
*pstart = getsectdatafromheader_64((mach_header_64*)hdr,
"__TEXT", name, &len);
if (*pstart) { // NULL if not defined in this dynamic library
*pstart += _dyld_get_image_vmaddr_slide(i); // correct for reloc
*pend = *pstart + len;
return;
}
}
#endif
if (hdr->magic == MH_MAGIC) {
uint32_t len;
*pstart = getsectdatafromheader(hdr, "__TEXT", name, &len);
if (*pstart) { // NULL if not defined in this dynamic library
*pstart += _dyld_get_image_vmaddr_slide(i); // correct for reloc
*pend = *pstart + len;
return;
}
}
}
}
// If we get here, not defined in a dll at all. See if defined statically.
unsigned long len; // don't ask me why this type isn't uint32_t too...
*pstart = getsectdata("__TEXT", name, &len);
*pend = *pstart + len;
}
};
#define DECLARE_ATTRIBUTE_SECTION_VARS(name) \
extern char* __start_##name; \
extern char* __stop_##name
#define INIT_ATTRIBUTE_SECTION_VARS(name) \
DECLARE_ATTRIBUTE_SECTION_VARS(name); \
static const AssignAttributeStartEnd __assign_##name( \
#name, &__start_##name, &__stop_##name)
#define DEFINE_ATTRIBUTE_SECTION_VARS(name) \
char* __start_##name, *__stop_##name; \
INIT_ATTRIBUTE_SECTION_VARS(name)
# define ATTRIBUTE_SECTION_START(name) (reinterpret_cast<void*>(__start_##name))
# define ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast<void*>(__stop_##name))
# define HAVE_ATTRIBUTE_SECTION_START 1
#else // not HAVE___ATTRIBUTE__ && __ELF__, nor HAVE___ATTRIBUTE__ && __MACH__
# define ATTRIBUTE_SECTION(name)
# define DECLARE_ATTRIBUTE_SECTION_VARS(name)
# define INIT_ATTRIBUTE_SECTION_VARS(name)
# define DEFINE_ATTRIBUTE_SECTION_VARS(name)
# define ATTRIBUTE_SECTION_START(name) (reinterpret_cast<void*>(0))
# define ATTRIBUTE_SECTION_STOP(name) (reinterpret_cast<void*>(0))
#endif // HAVE___ATTRIBUTE__ and __ELF__ or __MACH__
#if defined(HAVE___ATTRIBUTE__)
# if (defined(__i386__) || defined(__x86_64__))
# define CACHELINE_ALIGNED __attribute__((aligned(64)))
# elif (defined(__PPC__) || defined(__PPC64__))
# define CACHELINE_ALIGNED __attribute__((aligned(16)))
# elif (defined(__arm__))
# define CACHELINE_ALIGNED __attribute__((aligned(64)))
// some ARMs have shorter cache lines (ARM1176JZF-S is 32 bytes for example) but obviously 64-byte aligned implies 32-byte aligned
# elif (defined(__mips__))
# define CACHELINE_ALIGNED __attribute__((aligned(128)))
# elif (defined(__aarch64__))
# define CACHELINE_ALIGNED __attribute__((aligned(64)))
// implementation specific, Cortex-A53 and 57 should have 64 bytes
# elif (defined(__s390__))
# define CACHELINE_ALIGNED __attribute__((aligned(256)))
# elif (defined(__riscv) && __riscv_xlen == 64)
# define CACHELINE_ALIGNED __attribute__((aligned(64)))
# elif (defined(__e2k__))
# define CACHELINE_ALIGNED __attribute__((aligned(64)))
# else
# error Could not determine cache line length - unknown architecture
# endif
#else
# define CACHELINE_ALIGNED
#endif // defined(HAVE___ATTRIBUTE__)
#if defined(HAVE___ATTRIBUTE__ALIGNED_FN)
# define CACHELINE_ALIGNED_FN CACHELINE_ALIGNED
#else
# define CACHELINE_ALIGNED_FN
#endif
// Structure for discovering alignment
union MemoryAligner {
void* p;
double d;
size_t s;
} CACHELINE_ALIGNED;
#if defined(HAVE___ATTRIBUTE__) && defined(__ELF__)
#define ATTRIBUTE_HIDDEN __attribute__((visibility("hidden")))
#else
#define ATTRIBUTE_HIDDEN
#endif
#if defined(__GNUC__)
#define ATTRIBUTE_ALWAYS_INLINE __attribute__((always_inline))
#elif defined(_MSC_VER)
#define ATTRIBUTE_ALWAYS_INLINE __forceinline
#else
#define ATTRIBUTE_ALWAYS_INLINE
#endif
// The following enum should be used only as a constructor argument to indicate
// that the variable has static storage class, and that the constructor should
// do nothing to its state. It indicates to the reader that it is legal to
// declare a static nistance of the class, provided the constructor is given
// the base::LINKER_INITIALIZED argument. Normally, it is unsafe to declare a
// static variable that has a constructor or a destructor because invocation
// order is undefined. However, IF the type can be initialized by filling with
// zeroes (which the loader does for static variables), AND the destructor also
// does nothing to the storage, then a constructor declared as
// explicit MyClass(base::LinkerInitialized x) {}
// and invoked as
// static MyClass my_variable_name(base::LINKER_INITIALIZED);
namespace base {
enum LinkerInitialized { LINKER_INITIALIZED };
}
#endif // _BASICTYPES_H_

View file

@ -0,0 +1,175 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2005, 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.
// ---
// This file is a compatibility layer that defines Google's version of
// command line flags that are used for configuration.
//
// We put flags into their own namespace. It is purposefully
// named in an opaque way that people should have trouble typing
// directly. The idea is that DEFINE puts the flag in the weird
// namespace, and DECLARE imports the flag from there into the
// current namespace. The net result is to force people to use
// DECLARE to get access to a flag, rather than saying
// extern bool FLAGS_logtostderr;
// or some such instead. We want this so we can put extra
// functionality (like sanity-checking) in DECLARE if we want,
// and make sure it is picked up everywhere.
//
// We also put the type of the variable in the namespace, so that
// people can't DECLARE_int32 something that they DEFINE_bool'd
// elsewhere.
#ifndef BASE_COMMANDLINEFLAGS_H_
#define BASE_COMMANDLINEFLAGS_H_
#include <config.h>
#include <string>
#include <string.h> // for memchr
#include <stdlib.h> // for getenv
#include "base/basictypes.h"
#define DECLARE_VARIABLE(type, name) \
namespace FLAG__namespace_do_not_use_directly_use_DECLARE_##type##_instead { \
extern PERFTOOLS_DLL_DECL type FLAGS_##name; \
} \
using FLAG__namespace_do_not_use_directly_use_DECLARE_##type##_instead::FLAGS_##name
#define DEFINE_VARIABLE(type, name, value, meaning) \
namespace FLAG__namespace_do_not_use_directly_use_DECLARE_##type##_instead { \
PERFTOOLS_DLL_DECL type FLAGS_##name(value); \
char FLAGS_no##name; \
} \
using FLAG__namespace_do_not_use_directly_use_DECLARE_##type##_instead::FLAGS_##name
// bool specialization
#define DECLARE_bool(name) \
DECLARE_VARIABLE(bool, name)
#define DEFINE_bool(name, value, meaning) \
DEFINE_VARIABLE(bool, name, value, meaning)
// int32 specialization
#define DECLARE_int32(name) \
DECLARE_VARIABLE(int32, name)
#define DEFINE_int32(name, value, meaning) \
DEFINE_VARIABLE(int32, name, value, meaning)
// int64 specialization
#define DECLARE_int64(name) \
DECLARE_VARIABLE(int64, name)
#define DEFINE_int64(name, value, meaning) \
DEFINE_VARIABLE(int64, name, value, meaning)
#define DECLARE_uint64(name) \
DECLARE_VARIABLE(uint64, name)
#define DEFINE_uint64(name, value, meaning) \
DEFINE_VARIABLE(uint64, name, value, meaning)
// double specialization
#define DECLARE_double(name) \
DECLARE_VARIABLE(double, name)
#define DEFINE_double(name, value, meaning) \
DEFINE_VARIABLE(double, name, value, meaning)
// Special case for string, because we have to specify the namespace
// std::string, which doesn't play nicely with our FLAG__namespace hackery.
#define DECLARE_string(name) \
namespace FLAG__namespace_do_not_use_directly_use_DECLARE_string_instead { \
extern std::string FLAGS_##name; \
} \
using FLAG__namespace_do_not_use_directly_use_DECLARE_string_instead::FLAGS_##name
#define DEFINE_string(name, value, meaning) \
namespace FLAG__namespace_do_not_use_directly_use_DECLARE_string_instead { \
std::string FLAGS_##name(value); \
char FLAGS_no##name; \
} \
using FLAG__namespace_do_not_use_directly_use_DECLARE_string_instead::FLAGS_##name
// implemented in sysinfo.cc
namespace tcmalloc {
namespace commandlineflags {
inline bool StringToBool(const char *value, bool def) {
if (!value) {
return def;
}
switch (value[0]) {
case 't':
case 'T':
case 'y':
case 'Y':
case '1':
case '\0':
return true;
}
return false;
}
inline int StringToInt(const char *value, int def) {
if (!value) {
return def;
}
return strtol(value, NULL, 10);
}
inline long long StringToLongLong(const char *value, long long def) {
if (!value) {
return def;
}
return strtoll(value, NULL, 10);
}
inline double StringToDouble(const char *value, double def) {
if (!value) {
return def;
}
return strtod(value, NULL);
}
}
}
// These macros (could be functions, but I don't want to bother with a .cc
// file), make it easier to initialize flags from the environment.
#define EnvToString(envname, dflt) \
(!getenv(envname) ? (dflt) : getenv(envname))
#define EnvToBool(envname, dflt) \
tcmalloc::commandlineflags::StringToBool(getenv(envname), dflt)
#define EnvToInt(envname, dflt) \
tcmalloc::commandlineflags::StringToInt(getenv(envname), dflt)
#define EnvToInt64(envname, dflt) \
tcmalloc::commandlineflags::StringToLongLong(getenv(envname), dflt)
#define EnvToDouble(envname, dflt) \
tcmalloc::commandlineflags::StringToDouble(getenv(envname), dflt)
#endif // BASE_COMMANDLINEFLAGS_H_

View file

@ -0,0 +1,64 @@
// -*- Mode: c; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2008-2009, 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: Kostya Serebryany
*/
#ifdef __cplusplus
# error "This file should be built as pure C to avoid name mangling"
#endif
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include "base/dynamic_annotations.h"
#include "getenv_safe.h" // for TCMallocGetenvSafe
static int GetRunningOnValgrind(void) {
#ifdef RUNNING_ON_VALGRIND
if (RUNNING_ON_VALGRIND) return 1;
#endif
const char *running_on_valgrind_str = TCMallocGetenvSafe("RUNNING_ON_VALGRIND");
if (running_on_valgrind_str) {
return strcmp(running_on_valgrind_str, "0") != 0;
}
return 0;
}
/* See the comments in dynamic_annotations.h */
int RunningOnValgrind(void) {
static volatile int running_on_valgrind = -1;
int local_running_on_valgrind = running_on_valgrind;
if (local_running_on_valgrind == -1)
running_on_valgrind = local_running_on_valgrind = GetRunningOnValgrind();
return local_running_on_valgrind;
}

View file

@ -0,0 +1,86 @@
/* -*- Mode: c; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/* Copyright (c) 2008, 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: Kostya Serebryany
*/
/* This file defines dynamic annotations for use with dynamic analysis
tool such as valgrind, PIN, etc.
Dynamic annotation is a source code annotation that affects
the generated code (that is, the annotation is not a comment).
Each such annotation is attached to a particular
instruction and/or to a particular object (address) in the program.
The annotations that should be used by users are macros in all upper-case
(e.g., ANNOTATE_NEW_MEMORY).
Actual implementation of these macros may differ depending on the
dynamic analysis tool being used.
See http://code.google.com/p/data-race-test/ for more information.
This file supports the following dynamic analysis tools:
- None (DYNAMIC_ANNOTATIONS_ENABLED is not defined or zero).
Macros are defined empty.
- ThreadSanitizer, Helgrind, DRD (DYNAMIC_ANNOTATIONS_ENABLED is 1).
Macros are defined as calls to non-inlinable empty functions
that are intercepted by Valgrind. */
#ifndef BASE_DYNAMIC_ANNOTATIONS_H_
#define BASE_DYNAMIC_ANNOTATIONS_H_
#ifdef __cplusplus
extern "C" {
#endif
/* Return non-zero value if running under valgrind.
If "valgrind.h" is included into dynamic_annotations.c,
the regular valgrind mechanism will be used.
See http://valgrind.org/docs/manual/manual-core-adv.html about
RUNNING_ON_VALGRIND and other valgrind "client requests".
The file "valgrind.h" may be obtained by doing
svn co svn://svn.valgrind.org/valgrind/trunk/include
If for some reason you can't use "valgrind.h" or want to fake valgrind,
there are two ways to make this function return non-zero:
- Use environment variable: export RUNNING_ON_VALGRIND=1
- Make your tool intercept the function RunningOnValgrind() and
change its return value.
*/
int RunningOnValgrind(void);
#ifdef __cplusplus
}
#endif
#endif /* BASE_DYNAMIC_ANNOTATIONS_H_ */

View file

@ -0,0 +1,434 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2008, 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: Paul Pluzhnikov
//
// Allow dynamic symbol lookup in an in-memory Elf image.
//
#include "base/elf_mem_image.h"
#ifdef HAVE_ELF_MEM_IMAGE // defined in elf_mem_image.h
#include <stddef.h> // for size_t, ptrdiff_t
#include "base/logging.h"
// From binutils/include/elf/common.h (this doesn't appear to be documented
// anywhere else).
//
// /* This flag appears in a Versym structure. It means that the symbol
// is hidden, and is only visible with an explicit version number.
// This is a GNU extension. */
// #define VERSYM_HIDDEN 0x8000
//
// /* This is the mask for the rest of the Versym information. */
// #define VERSYM_VERSION 0x7fff
#define VERSYM_VERSION 0x7fff
namespace base {
namespace {
template <int N> class ElfClass {
public:
static const int kElfClass = -1;
static int ElfBind(const ElfW(Sym) *) {
CHECK(false); // << "Unexpected word size";
return 0;
}
static int ElfType(const ElfW(Sym) *) {
CHECK(false); // << "Unexpected word size";
return 0;
}
};
template <> class ElfClass<32> {
public:
static const int kElfClass = ELFCLASS32;
static int ElfBind(const ElfW(Sym) *symbol) {
return ELF32_ST_BIND(symbol->st_info);
}
static int ElfType(const ElfW(Sym) *symbol) {
return ELF32_ST_TYPE(symbol->st_info);
}
};
template <> class ElfClass<64> {
public:
static const int kElfClass = ELFCLASS64;
static int ElfBind(const ElfW(Sym) *symbol) {
return ELF64_ST_BIND(symbol->st_info);
}
static int ElfType(const ElfW(Sym) *symbol) {
return ELF64_ST_TYPE(symbol->st_info);
}
};
typedef ElfClass<__WORDSIZE> CurrentElfClass;
// Extract an element from one of the ELF tables, cast it to desired type.
// This is just a simple arithmetic and a glorified cast.
// Callers are responsible for bounds checking.
template <class T>
const T* GetTableElement(const ElfW(Ehdr) *ehdr,
ElfW(Off) table_offset,
ElfW(Word) element_size,
size_t index) {
return reinterpret_cast<const T*>(reinterpret_cast<const char *>(ehdr)
+ table_offset
+ index * element_size);
}
} // namespace
const void *const ElfMemImage::kInvalidBase =
reinterpret_cast<const void *>(~0L);
ElfMemImage::ElfMemImage(const void *base) {
CHECK(base != kInvalidBase);
Init(base);
}
int ElfMemImage::GetNumSymbols() const {
if (!hash_) {
return 0;
}
// See http://www.caldera.com/developers/gabi/latest/ch5.dynamic.html#hash
return hash_[1];
}
const ElfW(Sym) *ElfMemImage::GetDynsym(int index) const {
CHECK_LT(index, GetNumSymbols());
return dynsym_ + index;
}
const ElfW(Versym) *ElfMemImage::GetVersym(int index) const {
CHECK_LT(index, GetNumSymbols());
return versym_ + index;
}
const ElfW(Phdr) *ElfMemImage::GetPhdr(int index) const {
CHECK_LT(index, ehdr_->e_phnum);
return GetTableElement<ElfW(Phdr)>(ehdr_,
ehdr_->e_phoff,
ehdr_->e_phentsize,
index);
}
const char *ElfMemImage::GetDynstr(ElfW(Word) offset) const {
CHECK_LT(offset, strsize_);
return dynstr_ + offset;
}
const void *ElfMemImage::GetSymAddr(const ElfW(Sym) *sym) const {
if (sym->st_shndx == SHN_UNDEF || sym->st_shndx >= SHN_LORESERVE) {
// Symbol corresponds to "special" (e.g. SHN_ABS) section.
return reinterpret_cast<const void *>(sym->st_value);
}
CHECK_LT(link_base_, sym->st_value);
return GetTableElement<char>(ehdr_, 0, 1, sym->st_value) - link_base_;
}
const ElfW(Verdef) *ElfMemImage::GetVerdef(int index) const {
CHECK_LE(index, verdefnum_);
const ElfW(Verdef) *version_definition = verdef_;
while (version_definition->vd_ndx < index && version_definition->vd_next) {
const char *const version_definition_as_char =
reinterpret_cast<const char *>(version_definition);
version_definition =
reinterpret_cast<const ElfW(Verdef) *>(version_definition_as_char +
version_definition->vd_next);
}
return version_definition->vd_ndx == index ? version_definition : NULL;
}
const ElfW(Verdaux) *ElfMemImage::GetVerdefAux(
const ElfW(Verdef) *verdef) const {
return reinterpret_cast<const ElfW(Verdaux) *>(verdef+1);
}
const char *ElfMemImage::GetVerstr(ElfW(Word) offset) const {
CHECK_LT(offset, strsize_);
return dynstr_ + offset;
}
void ElfMemImage::Init(const void *base) {
ehdr_ = NULL;
dynsym_ = NULL;
dynstr_ = NULL;
versym_ = NULL;
verdef_ = NULL;
hash_ = NULL;
strsize_ = 0;
verdefnum_ = 0;
link_base_ = ~0L; // Sentinel: PT_LOAD .p_vaddr can't possibly be this.
if (!base) {
return;
}
const intptr_t base_as_uintptr_t = reinterpret_cast<uintptr_t>(base);
// Fake VDSO has low bit set.
const bool fake_vdso = ((base_as_uintptr_t & 1) != 0);
base = reinterpret_cast<const void *>(base_as_uintptr_t & ~1);
const char *const base_as_char = reinterpret_cast<const char *>(base);
if (base_as_char[EI_MAG0] != ELFMAG0 || base_as_char[EI_MAG1] != ELFMAG1 ||
base_as_char[EI_MAG2] != ELFMAG2 || base_as_char[EI_MAG3] != ELFMAG3) {
RAW_DCHECK(false, "no ELF magic"); // at %p", base);
return;
}
int elf_class = base_as_char[EI_CLASS];
if (elf_class != CurrentElfClass::kElfClass) {
DCHECK_EQ(elf_class, CurrentElfClass::kElfClass);
return;
}
switch (base_as_char[EI_DATA]) {
case ELFDATA2LSB: {
if (__LITTLE_ENDIAN != __BYTE_ORDER) {
DCHECK_EQ(__LITTLE_ENDIAN, __BYTE_ORDER); // << ": wrong byte order";
return;
}
break;
}
case ELFDATA2MSB: {
if (__BIG_ENDIAN != __BYTE_ORDER) {
DCHECK_EQ(__BIG_ENDIAN, __BYTE_ORDER); // << ": wrong byte order";
return;
}
break;
}
default: {
RAW_DCHECK(false, "unexpected data encoding"); // << base_as_char[EI_DATA];
return;
}
}
ehdr_ = reinterpret_cast<const ElfW(Ehdr) *>(base);
const ElfW(Phdr) *dynamic_program_header = NULL;
for (int i = 0; i < ehdr_->e_phnum; ++i) {
const ElfW(Phdr) *const program_header = GetPhdr(i);
switch (program_header->p_type) {
case PT_LOAD:
if (link_base_ == ~0L) {
link_base_ = program_header->p_vaddr;
}
break;
case PT_DYNAMIC:
dynamic_program_header = program_header;
break;
}
}
if (link_base_ == ~0L || !dynamic_program_header) {
RAW_DCHECK(~0L != link_base_, "no PT_LOADs in VDSO");
RAW_DCHECK(dynamic_program_header, "no PT_DYNAMIC in VDSO");
// Mark this image as not present. Can not recur infinitely.
Init(0);
return;
}
ptrdiff_t relocation =
base_as_char - reinterpret_cast<const char *>(link_base_);
ElfW(Dyn) *dynamic_entry =
reinterpret_cast<ElfW(Dyn) *>(dynamic_program_header->p_vaddr +
relocation);
for (; dynamic_entry->d_tag != DT_NULL; ++dynamic_entry) {
ElfW(Xword) value = dynamic_entry->d_un.d_val;
if (fake_vdso) {
// A complication: in the real VDSO, dynamic entries are not relocated
// (it wasn't loaded by a dynamic loader). But when testing with a
// "fake" dlopen()ed vdso library, the loader relocates some (but
// not all!) of them before we get here.
if (dynamic_entry->d_tag == DT_VERDEF) {
// The only dynamic entry (of the ones we care about) libc-2.3.6
// loader doesn't relocate.
value += relocation;
}
} else {
// Real VDSO. Everything needs to be relocated.
value += relocation;
}
switch (dynamic_entry->d_tag) {
case DT_HASH:
hash_ = reinterpret_cast<ElfW(Word) *>(value);
break;
case DT_SYMTAB:
dynsym_ = reinterpret_cast<ElfW(Sym) *>(value);
break;
case DT_STRTAB:
dynstr_ = reinterpret_cast<const char *>(value);
break;
case DT_VERSYM:
versym_ = reinterpret_cast<ElfW(Versym) *>(value);
break;
case DT_VERDEF:
verdef_ = reinterpret_cast<ElfW(Verdef) *>(value);
break;
case DT_VERDEFNUM:
verdefnum_ = dynamic_entry->d_un.d_val;
break;
case DT_STRSZ:
strsize_ = dynamic_entry->d_un.d_val;
break;
default:
// Unrecognized entries explicitly ignored.
break;
}
}
if (!hash_ || !dynsym_ || !dynstr_ || !versym_ ||
!verdef_ || !verdefnum_ || !strsize_) {
RAW_DCHECK(hash_, "invalid VDSO (no DT_HASH)");
RAW_DCHECK(dynsym_, "invalid VDSO (no DT_SYMTAB)");
RAW_DCHECK(dynstr_, "invalid VDSO (no DT_STRTAB)");
RAW_DCHECK(versym_, "invalid VDSO (no DT_VERSYM)");
RAW_DCHECK(verdef_, "invalid VDSO (no DT_VERDEF)");
RAW_DCHECK(verdefnum_, "invalid VDSO (no DT_VERDEFNUM)");
RAW_DCHECK(strsize_, "invalid VDSO (no DT_STRSZ)");
// Mark this image as not present. Can not recur infinitely.
Init(0);
return;
}
}
bool ElfMemImage::LookupSymbol(const char *name,
const char *version,
int type,
SymbolInfo *info) const {
for (SymbolIterator it = begin(); it != end(); ++it) {
if (strcmp(it->name, name) == 0 && strcmp(it->version, version) == 0 &&
CurrentElfClass::ElfType(it->symbol) == type) {
if (info) {
*info = *it;
}
return true;
}
}
return false;
}
bool ElfMemImage::LookupSymbolByAddress(const void *address,
SymbolInfo *info_out) const {
for (SymbolIterator it = begin(); it != end(); ++it) {
const char *const symbol_start =
reinterpret_cast<const char *>(it->address);
const char *const symbol_end = symbol_start + it->symbol->st_size;
if (symbol_start <= address && address < symbol_end) {
if (info_out) {
// Client wants to know details for that symbol (the usual case).
if (CurrentElfClass::ElfBind(it->symbol) == STB_GLOBAL) {
// Strong symbol; just return it.
*info_out = *it;
return true;
} else {
// Weak or local. Record it, but keep looking for a strong one.
*info_out = *it;
}
} else {
// Client only cares if there is an overlapping symbol.
return true;
}
}
}
return false;
}
ElfMemImage::SymbolIterator::SymbolIterator(const void *const image, int index)
: index_(index), image_(image) {
}
const ElfMemImage::SymbolInfo *ElfMemImage::SymbolIterator::operator->() const {
return &info_;
}
const ElfMemImage::SymbolInfo& ElfMemImage::SymbolIterator::operator*() const {
return info_;
}
bool ElfMemImage::SymbolIterator::operator==(const SymbolIterator &rhs) const {
return this->image_ == rhs.image_ && this->index_ == rhs.index_;
}
bool ElfMemImage::SymbolIterator::operator!=(const SymbolIterator &rhs) const {
return !(*this == rhs);
}
ElfMemImage::SymbolIterator &ElfMemImage::SymbolIterator::operator++() {
this->Update(1);
return *this;
}
ElfMemImage::SymbolIterator ElfMemImage::begin() const {
SymbolIterator it(this, 0);
it.Update(0);
return it;
}
ElfMemImage::SymbolIterator ElfMemImage::end() const {
return SymbolIterator(this, GetNumSymbols());
}
void ElfMemImage::SymbolIterator::Update(int increment) {
const ElfMemImage *image = reinterpret_cast<const ElfMemImage *>(image_);
CHECK(image->IsPresent() || increment == 0);
if (!image->IsPresent()) {
return;
}
index_ += increment;
if (index_ >= image->GetNumSymbols()) {
index_ = image->GetNumSymbols();
return;
}
const ElfW(Sym) *symbol = image->GetDynsym(index_);
const ElfW(Versym) *version_symbol = image->GetVersym(index_);
CHECK(symbol && version_symbol);
const char *const symbol_name = image->GetDynstr(symbol->st_name);
const ElfW(Versym) version_index = version_symbol[0] & VERSYM_VERSION;
const ElfW(Verdef) *version_definition = NULL;
const char *version_name = "";
if (symbol->st_shndx == SHN_UNDEF) {
// Undefined symbols reference DT_VERNEED, not DT_VERDEF, and
// version_index could well be greater than verdefnum_, so calling
// GetVerdef(version_index) may trigger assertion.
} else {
version_definition = image->GetVerdef(version_index);
}
if (version_definition) {
// I am expecting 1 or 2 auxiliary entries: 1 for the version itself,
// optional 2nd if the version has a parent.
CHECK_LE(1, version_definition->vd_cnt);
CHECK_LE(version_definition->vd_cnt, 2);
const ElfW(Verdaux) *version_aux = image->GetVerdefAux(version_definition);
version_name = image->GetVerstr(version_aux->vda_name);
}
info_.name = symbol_name;
info_.version = version_name;
info_.address = image->GetSymAddr(symbol);
info_.symbol = symbol;
}
} // namespace base
#endif // HAVE_ELF_MEM_IMAGE

View file

@ -0,0 +1,135 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2008, 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: Paul Pluzhnikov
//
// Allow dynamic symbol lookup for in-memory Elf images.
#ifndef BASE_ELF_MEM_IMAGE_H_
#define BASE_ELF_MEM_IMAGE_H_
#include <config.h>
#ifdef HAVE_FEATURES_H
#include <features.h> // for __GLIBC__
#endif
// Maybe one day we can rewrite this file not to require the elf
// symbol extensions in glibc, but for right now we need them.
#if defined(__ELF__) && defined(__GLIBC__) && !defined(__native_client__)
#define HAVE_ELF_MEM_IMAGE 1
#include <stdlib.h>
#include <link.h> // for ElfW
namespace base {
// An in-memory ELF image (may not exist on disk).
class ElfMemImage {
public:
// Sentinel: there could never be an elf image at this address.
static const void *const kInvalidBase;
// Information about a single vdso symbol.
// All pointers are into .dynsym, .dynstr, or .text of the VDSO.
// Do not free() them or modify through them.
struct SymbolInfo {
const char *name; // E.g. "__vdso_getcpu"
const char *version; // E.g. "LINUX_2.6", could be ""
// for unversioned symbol.
const void *address; // Relocated symbol address.
const ElfW(Sym) *symbol; // Symbol in the dynamic symbol table.
};
// Supports iteration over all dynamic symbols.
class SymbolIterator {
public:
friend class ElfMemImage;
const SymbolInfo *operator->() const;
const SymbolInfo &operator*() const;
SymbolIterator& operator++();
bool operator!=(const SymbolIterator &rhs) const;
bool operator==(const SymbolIterator &rhs) const;
private:
SymbolIterator(const void *const image, int index);
void Update(int incr);
SymbolInfo info_;
int index_;
const void *const image_;
};
explicit ElfMemImage(const void *base);
void Init(const void *base);
bool IsPresent() const { return ehdr_ != NULL; }
const ElfW(Phdr)* GetPhdr(int index) const;
const ElfW(Sym)* GetDynsym(int index) const;
const ElfW(Versym)* GetVersym(int index) const;
const ElfW(Verdef)* GetVerdef(int index) const;
const ElfW(Verdaux)* GetVerdefAux(const ElfW(Verdef) *verdef) const;
const char* GetDynstr(ElfW(Word) offset) const;
const void* GetSymAddr(const ElfW(Sym) *sym) const;
const char* GetVerstr(ElfW(Word) offset) const;
int GetNumSymbols() const;
SymbolIterator begin() const;
SymbolIterator end() const;
// Look up versioned dynamic symbol in the image.
// Returns false if image is not present, or doesn't contain given
// symbol/version/type combination.
// If info_out != NULL, additional details are filled in.
bool LookupSymbol(const char *name, const char *version,
int symbol_type, SymbolInfo *info_out) const;
// Find info about symbol (if any) which overlaps given address.
// Returns true if symbol was found; false if image isn't present
// or doesn't have a symbol overlapping given address.
// If info_out != NULL, additional details are filled in.
bool LookupSymbolByAddress(const void *address, SymbolInfo *info_out) const;
private:
const ElfW(Ehdr) *ehdr_;
const ElfW(Sym) *dynsym_;
const ElfW(Versym) *versym_;
const ElfW(Verdef) *verdef_;
const ElfW(Word) *hash_;
const char *dynstr_;
size_t strsize_;
size_t verdefnum_;
ElfW(Addr) link_base_; // Link-time base (p_vaddr of first PT_LOAD).
};
} // namespace base
#endif // __ELF__ and __GLIBC__ and !__native_client__
#endif // BASE_ELF_MEM_IMAGE_H_

View file

@ -0,0 +1,135 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2005-2008, 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: Markus Gutschke, Carl Crous
*/
#ifndef _ELFCORE_H
#define _ELFCORE_H
#ifdef __cplusplus
extern "C" {
#endif
/* We currently only support x86-32, x86-64, ARM, MIPS, PPC on Linux.
* Porting to other related platforms should not be difficult.
*/
#if (defined(__i386__) || defined(__x86_64__) || defined(__arm__) || \
defined(__mips__) || defined(__PPC__)) && defined(__linux)
#include "config.h"
#include <stdarg.h>
#include <stdint.h>
#include <sys/types.h>
/* Define the DUMPER symbol to make sure that there is exactly one
* core dumper built into the library.
*/
#define DUMPER "ELF"
/* By the time that we get a chance to read CPU registers in the
* calling thread, they are already in a not particularly useful
* state. Besides, there will be multiple frames on the stack that are
* just making the core file confusing. To fix this problem, we take a
* snapshot of the frame pointer, stack pointer, and instruction
* pointer at an earlier time, and then insert these values into the
* core file.
*/
#if defined(__i386__) || defined(__x86_64__)
typedef struct i386_regs { /* Normal (non-FPU) CPU registers */
#ifdef __x86_64__
#define BP rbp
#define SP rsp
#define IP rip
uint64_t r15,r14,r13,r12,rbp,rbx,r11,r10;
uint64_t r9,r8,rax,rcx,rdx,rsi,rdi,orig_rax;
uint64_t rip,cs,eflags;
uint64_t rsp,ss;
uint64_t fs_base, gs_base;
uint64_t ds,es,fs,gs;
#else
#define BP ebp
#define SP esp
#define IP eip
uint32_t ebx, ecx, edx, esi, edi, ebp, eax;
uint16_t ds, __ds, es, __es;
uint16_t fs, __fs, gs, __gs;
uint32_t orig_eax, eip;
uint16_t cs, __cs;
uint32_t eflags, esp;
uint16_t ss, __ss;
#endif
} i386_regs;
#elif defined(__arm__)
typedef struct arm_regs { /* General purpose registers */
#define BP uregs[11] /* Frame pointer */
#define SP uregs[13] /* Stack pointer */
#define IP uregs[15] /* Program counter */
#define LR uregs[14] /* Link register */
long uregs[18];
} arm_regs;
#elif defined(__mips__)
typedef struct mips_regs {
unsigned long pad[6]; /* Unused padding to match kernel structures */
unsigned long uregs[32]; /* General purpose registers. */
unsigned long hi; /* Used for multiplication and division. */
unsigned long lo;
unsigned long cp0_epc; /* Program counter. */
unsigned long cp0_badvaddr;
unsigned long cp0_status;
unsigned long cp0_cause;
unsigned long unused;
} mips_regs;
#elif defined (__PPC__)
typedef struct ppc_regs {
#define SP uregs[1] /* Stack pointer */
#define IP rip /* Program counter */
#define LR lr /* Link register */
unsigned long uregs[32]; /* General Purpose Registers - r0-r31. */
double fpr[32]; /* Floating-Point Registers - f0-f31. */
unsigned long rip; /* Program counter. */
unsigned long msr;
unsigned long ccr;
unsigned long lr;
unsigned long ctr;
unsigned long xeq;
unsigned long mq;
} ppc_regs;
#endif
#endif // __linux and various arches
#ifdef __cplusplus
}
#endif
#endif /* _ELFCORE_H */

View file

@ -0,0 +1,74 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2005, 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: Jacob Hoffman-Andrews
#ifndef _GOOGLEINIT_H
#define _GOOGLEINIT_H
#include "base/logging.h"
class GoogleInitializer {
public:
typedef void (*VoidFunction)(void);
GoogleInitializer(const char* name, VoidFunction ctor, VoidFunction dtor)
: name_(name), destructor_(dtor) {
RAW_VLOG(10, "<GoogleModuleObject> constructing: %s\n", name_);
if (ctor)
ctor();
}
~GoogleInitializer() {
RAW_VLOG(10, "<GoogleModuleObject> destroying: %s\n", name_);
if (destructor_)
destructor_();
}
private:
const char* const name_;
const VoidFunction destructor_;
};
#define REGISTER_MODULE_INITIALIZER(name, body) \
namespace { \
static void google_init_module_##name () { body; } \
GoogleInitializer google_initializer_module_##name(#name, \
google_init_module_##name, NULL); \
}
#define REGISTER_MODULE_DESTRUCTOR(name, body) \
namespace { \
static void google_destruct_module_##name () { body; } \
GoogleInitializer google_destructor_module_##name(#name, \
NULL, google_destruct_module_##name); \
}
#endif /* _GOOGLEINIT_H */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,707 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2005-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: Markus Gutschke
*/
#include "base/linuxthreads.h"
#ifdef THREADS
#ifdef __cplusplus
extern "C" {
#endif
#include <sched.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <sys/prctl.h>
#include <semaphore.h>
#include "base/linux_syscall_support.h"
#include "base/thread_lister.h"
#ifndef CLONE_UNTRACED
#define CLONE_UNTRACED 0x00800000
#endif
/* Synchronous signals that should not be blocked while in the lister thread.
*/
static const int sync_signals[] = { SIGABRT, SIGILL, SIGFPE, SIGSEGV, SIGBUS,
SIGXCPU, SIGXFSZ };
/* itoa() is not a standard function, and we cannot safely call printf()
* after suspending threads. So, we just implement our own copy. A
* recursive approach is the easiest here.
*/
static char *local_itoa(char *buf, int i) {
if (i < 0) {
*buf++ = '-';
return local_itoa(buf, -i);
} else {
if (i >= 10)
buf = local_itoa(buf, i/10);
*buf++ = (i%10) + '0';
*buf = '\000';
return buf;
}
}
/* Wrapper around clone() that runs "fn" on the same stack as the
* caller! Unlike fork(), the cloned thread shares the same address space.
* The caller must be careful to use only minimal amounts of stack until
* the cloned thread has returned.
* There is a good chance that the cloned thread and the caller will share
* the same copy of errno!
*/
#ifdef __GNUC__
#if __GNUC__ == 3 && __GNUC_MINOR__ >= 1 || __GNUC__ > 3
/* Try to force this function into a separate stack frame, and make sure
* that arguments are passed on the stack.
*/
static int local_clone (int (*fn)(void *), void *arg, ...)
__attribute__ ((noinline));
#endif
#endif
/* To avoid the gap cross page boundaries, increase by the large parge
* size mostly PowerPC system uses. */
#ifdef __PPC64__
#define CLONE_STACK_SIZE 65536
#else
#define CLONE_STACK_SIZE 4096
#endif
static int local_clone (int (*fn)(void *), void *arg, ...) {
/* Leave 4kB of gap between the callers stack and the new clone. This
* should be more than sufficient for the caller to call waitpid() until
* the cloned thread terminates.
*
* It is important that we set the CLONE_UNTRACED flag, because newer
* versions of "gdb" otherwise attempt to attach to our thread, and will
* attempt to reap its status codes. This subsequently results in the
* caller hanging indefinitely in waitpid(), waiting for a change in
* status that will never happen. By setting the CLONE_UNTRACED flag, we
* prevent "gdb" from stealing events, but we still expect the thread
* lister to fail, because it cannot PTRACE_ATTACH to the process that
* is being debugged. This is OK and the error code will be reported
* correctly.
*/
return sys_clone(fn, (char *)&arg - CLONE_STACK_SIZE,
CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_UNTRACED, arg, 0, 0, 0);
}
/* Local substitute for the atoi() function, which is not necessarily safe
* to call once threads are suspended (depending on whether libc looks up
* locale information, when executing atoi()).
*/
static int local_atoi(const char *s) {
int n = 0;
int neg = *s == '-';
if (neg)
s++;
while (*s >= '0' && *s <= '9')
n = 10*n + (*s++ - '0');
return neg ? -n : n;
}
/* Re-runs fn until it doesn't cause EINTR
*/
#define NO_INTR(fn) do {} while ((fn) < 0 && errno == EINTR)
/* Wrap a class around system calls, in order to give us access to
* a private copy of errno. This only works in C++, but it has the
* advantage of not needing nested functions, which are a non-standard
* language extension.
*/
#ifdef __cplusplus
namespace {
class SysCalls {
public:
#define SYS_CPLUSPLUS
#define SYS_ERRNO my_errno
#define SYS_INLINE inline
#define SYS_PREFIX -1
#undef SYS_LINUX_SYSCALL_SUPPORT_H
#include "linux_syscall_support.h"
SysCalls() : my_errno(0) { }
int my_errno;
};
}
#define ERRNO sys.my_errno
#else
#define ERRNO my_errno
#endif
/* Wrapper for open() which is guaranteed to never return EINTR.
*/
static int c_open(const char *fname, int flags, int mode) {
ssize_t rc;
NO_INTR(rc = sys_open(fname, flags, mode));
return rc;
}
/* abort() is not safely reentrant, and changes it's behavior each time
* it is called. This means, if the main application ever called abort()
* we cannot safely call it again. This would happen if we were called
* from a SIGABRT signal handler in the main application. So, document
* that calling SIGABRT from the thread lister makes it not signal safe
* (and vice-versa).
* Also, since we share address space with the main application, we
* cannot call abort() from the callback and expect the main application
* to behave correctly afterwards. In fact, the only thing we can do, is
* to terminate the main application with extreme prejudice (aka
* PTRACE_KILL).
* We set up our own SIGABRT handler to do this.
* In order to find the main application from the signal handler, we
* need to store information about it in global variables. This is
* safe, because the main application should be suspended at this
* time. If the callback ever called TCMalloc_ResumeAllProcessThreads(), then
* we are running a higher risk, though. So, try to avoid calling
* abort() after calling TCMalloc_ResumeAllProcessThreads.
*/
static volatile int *sig_pids, sig_num_threads, sig_proc, sig_marker;
/* Signal handler to help us recover from dying while we are attached to
* other threads.
*/
static void SignalHandler(int signum, siginfo_t *si, void *data) {
if (sig_pids != NULL) {
if (signum == SIGABRT) {
while (sig_num_threads-- > 0) {
/* Not sure if sched_yield is really necessary here, but it does not */
/* hurt, and it might be necessary for the same reasons that we have */
/* to do so in sys_ptrace_detach(). */
sys_sched_yield();
sys_ptrace(PTRACE_KILL, sig_pids[sig_num_threads], 0, 0);
}
} else if (sig_num_threads > 0) {
TCMalloc_ResumeAllProcessThreads(sig_num_threads, (int *)sig_pids);
}
}
sig_pids = NULL;
if (sig_marker >= 0)
NO_INTR(sys_close(sig_marker));
sig_marker = -1;
if (sig_proc >= 0)
NO_INTR(sys_close(sig_proc));
sig_proc = -1;
sys__exit(signum == SIGABRT ? 1 : 2);
}
/* Try to dirty the stack, and hope that the compiler is not smart enough
* to optimize this function away. Or worse, the compiler could inline the
* function and permanently allocate the data on the stack.
*/
static void DirtyStack(size_t amount) {
char buf[amount];
memset(buf, 0, amount);
sys_read(-1, buf, amount);
}
/* Data structure for passing arguments to the lister thread.
*/
#define ALT_STACKSIZE (MINSIGSTKSZ + 4096)
struct ListerParams {
int result, err;
char *altstack_mem;
ListAllProcessThreadsCallBack callback;
void *parameter;
va_list ap;
sem_t *lock;
};
static void ListerThread(struct ListerParams *args) {
int found_parent = 0;
pid_t clone_pid = sys_gettid(), ppid = sys_getppid();
char proc_self_task[80], marker_name[48], *marker_path;
const char *proc_paths[3];
const char *const *proc_path = proc_paths;
int proc = -1, marker = -1, num_threads = 0;
int max_threads = 0, sig;
struct kernel_stat marker_sb, proc_sb;
stack_t altstack;
/* Wait for parent thread to set appropriate permissions
* to allow ptrace activity
*/
if (sem_wait(args->lock) < 0) {
goto failure;
}
/* Create "marker" that we can use to detect threads sharing the same
* address space and the same file handles. By setting the FD_CLOEXEC flag
* we minimize the risk of misidentifying child processes as threads;
* and since there is still a race condition, we will filter those out
* later, anyway.
*/
if ((marker = sys_socket(PF_LOCAL, SOCK_DGRAM, 0)) < 0 ||
sys_fcntl(marker, F_SETFD, FD_CLOEXEC) < 0) {
failure:
args->result = -1;
args->err = errno;
if (marker >= 0)
NO_INTR(sys_close(marker));
sig_marker = marker = -1;
if (proc >= 0)
NO_INTR(sys_close(proc));
sig_proc = proc = -1;
sys__exit(1);
}
/* Compute search paths for finding thread directories in /proc */
local_itoa(strrchr(strcpy(proc_self_task, "/proc/"), '\000'), ppid);
strcpy(marker_name, proc_self_task);
marker_path = marker_name + strlen(marker_name);
strcat(proc_self_task, "/task/");
proc_paths[0] = proc_self_task; /* /proc/$$/task/ */
proc_paths[1] = "/proc/"; /* /proc/ */
proc_paths[2] = NULL;
/* Compute path for marker socket in /proc */
local_itoa(strcpy(marker_path, "/fd/") + 4, marker);
if (sys_stat(marker_name, &marker_sb) < 0) {
goto failure;
}
/* Catch signals on an alternate pre-allocated stack. This way, we can
* safely execute the signal handler even if we ran out of memory.
*/
memset(&altstack, 0, sizeof(altstack));
altstack.ss_sp = args->altstack_mem;
altstack.ss_flags = 0;
altstack.ss_size = ALT_STACKSIZE;
sys_sigaltstack(&altstack, (const stack_t *)NULL);
/* Some kernels forget to wake up traced processes, when the
* tracer dies. So, intercept synchronous signals and make sure
* that we wake up our tracees before dying. It is the caller's
* responsibility to ensure that asynchronous signals do not
* interfere with this function.
*/
sig_marker = marker;
sig_proc = -1;
for (sig = 0; sig < sizeof(sync_signals)/sizeof(*sync_signals); sig++) {
struct kernel_sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction_ = SignalHandler;
sys_sigfillset(&sa.sa_mask);
sa.sa_flags = SA_ONSTACK|SA_SIGINFO|SA_RESETHAND;
sys_sigaction(sync_signals[sig], &sa, (struct kernel_sigaction *)NULL);
}
/* Read process directories in /proc/... */
for (;;) {
/* Some kernels know about threads, and hide them in "/proc"
* (although they are still there, if you know the process
* id). Threads are moved into a separate "task" directory. We
* check there first, and then fall back on the older naming
* convention if necessary.
*/
if ((sig_proc = proc = c_open(*proc_path, O_RDONLY|O_DIRECTORY, 0)) < 0) {
if (*++proc_path != NULL)
continue;
goto failure;
}
if (sys_fstat(proc, &proc_sb) < 0)
goto failure;
/* Since we are suspending threads, we cannot call any libc
* functions that might acquire locks. Most notably, we cannot
* call malloc(). So, we have to allocate memory on the stack,
* instead. Since we do not know how much memory we need, we
* make a best guess. And if we guessed incorrectly we retry on
* a second iteration (by jumping to "detach_threads").
*
* Unless the number of threads is increasing very rapidly, we
* should never need to do so, though, as our guestimate is very
* conservative.
*/
if (max_threads < proc_sb.st_nlink + 100)
max_threads = proc_sb.st_nlink + 100;
/* scope */ {
pid_t pids[max_threads];
int added_entries = 0;
sig_num_threads = num_threads;
sig_pids = pids;
for (;;) {
struct KERNEL_DIRENT *entry;
char buf[4096];
ssize_t nbytes = GETDENTS(proc, (struct KERNEL_DIRENT *)buf,
sizeof(buf));
if (nbytes < 0)
goto failure;
else if (nbytes == 0) {
if (added_entries) {
/* Need to keep iterating over "/proc" in multiple
* passes until we no longer find any more threads. This
* algorithm eventually completes, when all threads have
* been suspended.
*/
added_entries = 0;
sys_lseek(proc, 0, SEEK_SET);
continue;
}
break;
}
for (entry = (struct KERNEL_DIRENT *)buf;
entry < (struct KERNEL_DIRENT *)&buf[nbytes];
entry = (struct KERNEL_DIRENT *)((char *)entry+entry->d_reclen)) {
if (entry->d_ino != 0) {
const char *ptr = entry->d_name;
pid_t pid;
/* Some kernels hide threads by preceding the pid with a '.' */
if (*ptr == '.')
ptr++;
/* If the directory is not numeric, it cannot be a
* process/thread
*/
if (*ptr < '0' || *ptr > '9')
continue;
pid = local_atoi(ptr);
/* Attach (and suspend) all threads */
if (pid && pid != clone_pid) {
struct kernel_stat tmp_sb;
char fname[entry->d_reclen + 48];
strcat(strcat(strcpy(fname, "/proc/"),
entry->d_name), marker_path);
/* Check if the marker is identical to the one we created */
if (sys_stat(fname, &tmp_sb) >= 0 &&
marker_sb.st_ino == tmp_sb.st_ino) {
long i, j;
/* Found one of our threads, make sure it is no duplicate */
for (i = 0; i < num_threads; i++) {
/* Linear search is slow, but should not matter much for
* the typically small number of threads.
*/
if (pids[i] == pid) {
/* Found a duplicate; most likely on second pass */
goto next_entry;
}
}
/* Check whether data structure needs growing */
if (num_threads >= max_threads) {
/* Back to square one, this time with more memory */
NO_INTR(sys_close(proc));
goto detach_threads;
}
/* Attaching to thread suspends it */
pids[num_threads++] = pid;
sig_num_threads = num_threads;
if (sys_ptrace(PTRACE_ATTACH, pid, (void *)0,
(void *)0) < 0) {
/* If operation failed, ignore thread. Maybe it
* just died? There might also be a race
* condition with a concurrent core dumper or
* with a debugger. In that case, we will just
* make a best effort, rather than failing
* entirely.
*/
num_threads--;
sig_num_threads = num_threads;
goto next_entry;
}
while (sys_waitpid(pid, (int *)0, __WALL) < 0) {
if (errno != EINTR) {
sys_ptrace_detach(pid);
num_threads--;
sig_num_threads = num_threads;
goto next_entry;
}
}
if (sys_ptrace(PTRACE_PEEKDATA, pid, &i, &j) || i++ != j ||
sys_ptrace(PTRACE_PEEKDATA, pid, &i, &j) || i != j) {
/* Address spaces are distinct, even though both
* processes show the "marker". This is probably
* a forked child process rather than a thread.
*/
sys_ptrace_detach(pid);
num_threads--;
sig_num_threads = num_threads;
} else {
found_parent |= pid == ppid;
added_entries++;
}
}
}
}
next_entry:;
}
}
NO_INTR(sys_close(proc));
sig_proc = proc = -1;
/* If we failed to find any threads, try looking somewhere else in
* /proc. Maybe, threads are reported differently on this system.
*/
if (num_threads > 1 || !*++proc_path) {
NO_INTR(sys_close(marker));
sig_marker = marker = -1;
/* If we never found the parent process, something is very wrong.
* Most likely, we are running in debugger. Any attempt to operate
* on the threads would be very incomplete. Let's just report an
* error to the caller.
*/
if (!found_parent) {
TCMalloc_ResumeAllProcessThreads(num_threads, pids);
sys__exit(3);
}
/* Now we are ready to call the callback,
* which takes care of resuming the threads for us.
*/
args->result = args->callback(args->parameter, num_threads,
pids, args->ap);
args->err = errno;
/* Callback should have resumed threads, but better safe than sorry */
if (TCMalloc_ResumeAllProcessThreads(num_threads, pids)) {
/* Callback forgot to resume at least one thread, report error */
args->err = EINVAL;
args->result = -1;
}
sys__exit(0);
}
detach_threads:
/* Resume all threads prior to retrying the operation */
TCMalloc_ResumeAllProcessThreads(num_threads, pids);
sig_pids = NULL;
num_threads = 0;
sig_num_threads = num_threads;
max_threads += 100;
}
}
}
/* This function gets the list of all linux threads of the current process
* passes them to the 'callback' along with the 'parameter' pointer; at the
* call back call time all the threads are paused via
* PTRACE_ATTACH.
* The callback is executed from a separate thread which shares only the
* address space, the filesystem, and the filehandles with the caller. Most
* notably, it does not share the same pid and ppid; and if it terminates,
* the rest of the application is still there. 'callback' is supposed to do
* or arrange for TCMalloc_ResumeAllProcessThreads. This happens automatically, if
* the thread raises a synchronous signal (e.g. SIGSEGV); asynchronous
* signals are blocked. If the 'callback' decides to unblock them, it must
* ensure that they cannot terminate the application, or that
* TCMalloc_ResumeAllProcessThreads will get called.
* It is an error for the 'callback' to make any library calls that could
* acquire locks. Most notably, this means that most system calls have to
* avoid going through libc. Also, this means that it is not legal to call
* exit() or abort().
* We return -1 on error and the return value of 'callback' on success.
*/
int TCMalloc_ListAllProcessThreads(void *parameter,
ListAllProcessThreadsCallBack callback, ...) {
char altstack_mem[ALT_STACKSIZE];
struct ListerParams args;
pid_t clone_pid;
int dumpable = 1, sig;
struct kernel_sigset_t sig_blocked, sig_old;
sem_t lock;
va_start(args.ap, callback);
/* If we are short on virtual memory, initializing the alternate stack
* might trigger a SIGSEGV. Let's do this early, before it could get us
* into more trouble (i.e. before signal handlers try to use the alternate
* stack, and before we attach to other threads).
*/
memset(altstack_mem, 0, sizeof(altstack_mem));
/* Some of our cleanup functions could conceivable use more stack space.
* Try to touch the stack right now. This could be defeated by the compiler
* being too smart for it's own good, so try really hard.
*/
DirtyStack(32768);
/* Make this process "dumpable". This is necessary in order to ptrace()
* after having called setuid().
*/
dumpable = sys_prctl(PR_GET_DUMPABLE, 0);
if (!dumpable)
sys_prctl(PR_SET_DUMPABLE, 1);
/* Fill in argument block for dumper thread */
args.result = -1;
args.err = 0;
args.altstack_mem = altstack_mem;
args.parameter = parameter;
args.callback = callback;
args.lock = &lock;
/* Before cloning the thread lister, block all asynchronous signals, as we */
/* are not prepared to handle them. */
sys_sigfillset(&sig_blocked);
for (sig = 0; sig < sizeof(sync_signals)/sizeof(*sync_signals); sig++) {
sys_sigdelset(&sig_blocked, sync_signals[sig]);
}
if (sys_sigprocmask(SIG_BLOCK, &sig_blocked, &sig_old)) {
args.err = errno;
args.result = -1;
goto failed;
}
/* scope */ {
/* After cloning, both the parent and the child share the same instance
* of errno. We must make sure that at least one of these processes
* (in our case, the parent) uses modified syscall macros that update
* a local copy of errno, instead.
*/
#ifdef __cplusplus
#define sys0_sigprocmask sys.sigprocmask
#define sys0_waitpid sys.waitpid
SysCalls sys;
#else
int my_errno;
#define SYS_ERRNO my_errno
#define SYS_INLINE inline
#define SYS_PREFIX 0
#undef SYS_LINUX_SYSCALL_SUPPORT_H
#include "linux_syscall_support.h"
#endif
/* Lock before clone so that parent can set
* ptrace permissions (if necessary) prior
* to ListerThread actually executing
*/
if (sem_init(&lock, 0, 0) == 0) {
int clone_errno;
clone_pid = local_clone((int (*)(void *))ListerThread, &args);
clone_errno = errno;
sys_sigprocmask(SIG_SETMASK, &sig_old, &sig_old);
if (clone_pid >= 0) {
#ifdef PR_SET_PTRACER
/* In newer versions of glibc permission must explicitly
* be given to allow for ptrace.
*/
prctl(PR_SET_PTRACER, clone_pid, 0, 0, 0);
#endif
/* Releasing the lock here allows the
* ListerThread to execute and ptrace us.
*/
sem_post(&lock);
int status, rc;
while ((rc = sys0_waitpid(clone_pid, &status, __WALL)) < 0 &&
ERRNO == EINTR) {
/* Keep waiting */
}
if (rc < 0) {
args.err = ERRNO;
args.result = -1;
} else if (WIFEXITED(status)) {
switch (WEXITSTATUS(status)) {
case 0: break; /* Normal process termination */
case 2: args.err = EFAULT; /* Some fault (e.g. SIGSEGV) detected */
args.result = -1;
break;
case 3: args.err = EPERM; /* Process is already being traced */
args.result = -1;
break;
default:args.err = ECHILD; /* Child died unexpectedly */
args.result = -1;
break;
}
} else if (!WIFEXITED(status)) {
args.err = EFAULT; /* Terminated due to an unhandled signal*/
args.result = -1;
}
sem_destroy(&lock);
} else {
args.result = -1;
args.err = clone_errno;
}
} else {
args.result = -1;
args.err = errno;
}
}
/* Restore the "dumpable" state of the process */
failed:
if (!dumpable)
sys_prctl(PR_SET_DUMPABLE, dumpable);
va_end(args.ap);
errno = args.err;
return args.result;
}
/* This function resumes the list of all linux threads that
* TCMalloc_ListAllProcessThreads pauses before giving to its callback.
* The function returns non-zero if at least one thread was
* suspended and has now been resumed.
*/
int TCMalloc_ResumeAllProcessThreads(int num_threads, pid_t *thread_pids) {
int detached_at_least_one = 0;
while (num_threads-- > 0) {
detached_at_least_one |= sys_ptrace_detach(thread_pids[num_threads]) >= 0;
}
return detached_at_least_one;
}
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,55 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2005-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: Markus Gutschke
*/
#ifndef _LINUXTHREADS_H
#define _LINUXTHREADS_H
/* Include thread_lister.h to get the interface that we implement for linux.
*/
/* We currently only support certain platforms on Linux. Porting to other
* related platforms should not be difficult.
*/
#if (defined(__i386__) || defined(__x86_64__) || defined(__arm__) || \
defined(__mips__) || defined(__PPC__) || defined(__aarch64__) || \
defined(__s390__)) && defined(__linux)
/* Define the THREADS symbol to make sure that there is exactly one core dumper
* built into the library.
*/
#define THREADS "Linux /proc"
#endif
#endif /* _LINUXTHREADS_H */

View file

@ -0,0 +1,108 @@
// -*- 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.
// ---
// This file just provides storage for FLAGS_verbose.
#include <config.h>
#include "base/logging.h"
#include "base/commandlineflags.h"
DEFINE_int32(verbose, EnvToInt("PERFTOOLS_VERBOSE", 0),
"Set to numbers >0 for more verbose output, or <0 for less. "
"--verbose == -4 means we log fatal errors only.");
#if defined(_WIN32) || defined(__CYGWIN__) || defined(__CYGWIN32__)
// While windows does have a POSIX-compatible API
// (_open/_write/_close), it acquires memory. Using this lower-level
// windows API is the closest we can get to being "raw".
RawFD RawOpenForWriting(const char* filename) {
// CreateFile allocates memory if file_name isn't absolute, so if
// that ever becomes a problem then we ought to compute the absolute
// path on its behalf (perhaps the ntdll/kernel function isn't aware
// of the working directory?)
RawFD fd = CreateFileA(filename, GENERIC_WRITE, 0, NULL,
CREATE_ALWAYS, 0, NULL);
if (fd != kIllegalRawFD && GetLastError() == ERROR_ALREADY_EXISTS)
SetEndOfFile(fd); // truncate the existing file
return fd;
}
void RawWrite(RawFD handle, const char* buf, size_t len) {
while (len > 0) {
DWORD wrote;
BOOL ok = WriteFile(handle, buf, len, &wrote, NULL);
// We do not use an asynchronous file handle, so ok==false means an error
if (!ok) break;
buf += wrote;
len -= wrote;
}
}
void RawClose(RawFD handle) {
CloseHandle(handle);
}
#else // _WIN32 || __CYGWIN__ || __CYGWIN32__
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
// Re-run fn until it doesn't cause EINTR.
#define NO_INTR(fn) do {} while ((fn) < 0 && errno == EINTR)
RawFD RawOpenForWriting(const char* filename) {
return open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0664);
}
void RawWrite(RawFD fd, const char* buf, size_t len) {
while (len > 0) {
ssize_t r;
NO_INTR(r = write(fd, buf, len));
if (r <= 0) break;
buf += r;
len -= r;
}
}
void RawClose(RawFD fd) {
NO_INTR(close(fd));
}
#endif // _WIN32 || __CYGWIN__ || __CYGWIN32__

View file

@ -0,0 +1,259 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2005, 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.
// ---
// This file contains #include information about logging-related stuff.
// Pretty much everybody needs to #include this file so that they can
// log various happenings.
//
#ifndef _LOGGING_H_
#define _LOGGING_H_
#include <config.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h> // for write()
#endif
#include <string.h> // for strlen(), strcmp()
#include <assert.h>
#include <errno.h> // for errno
#include "base/commandlineflags.h"
// On some systems (like freebsd), we can't call write() at all in a
// global constructor, perhaps because errno hasn't been set up.
// (In windows, we can't call it because it might call malloc.)
// Calling the write syscall is safer (it doesn't set errno), so we
// prefer that. Note we don't care about errno for logging: we just
// do logging on a best-effort basis.
#if defined(_MSC_VER)
#define WRITE_TO_STDERR(buf, len) WriteToStderr(buf, len); // in port.cc
#elif defined(HAVE_SYS_SYSCALL_H)
#include <sys/syscall.h>
#define WRITE_TO_STDERR(buf, len) syscall(SYS_write, STDERR_FILENO, buf, len)
#else
#define WRITE_TO_STDERR(buf, len) write(STDERR_FILENO, buf, len)
#endif
// MSVC and mingw define their own, safe version of vnsprintf (the
// windows one in broken) in port.cc. Everyone else can use the
// version here. We had to give it a unique name for windows.
#ifndef _WIN32
# define perftools_vsnprintf vsnprintf
#endif
// We log all messages at this log-level and below.
// INFO == -1, WARNING == -2, ERROR == -3, FATAL == -4
DECLARE_int32(verbose);
// CHECK dies with a fatal error if condition is not true. It is *not*
// controlled by NDEBUG, so the check will be executed regardless of
// compilation mode. Therefore, it is safe to do things like:
// CHECK(fp->Write(x) == 4)
// Note we use write instead of printf/puts to avoid the risk we'll
// call malloc().
#define CHECK(condition) \
do { \
if (!(condition)) { \
WRITE_TO_STDERR("Check failed: " #condition "\n", \
sizeof("Check failed: " #condition "\n")-1); \
abort(); \
} \
} while (0)
// This takes a message to print. The name is historical.
#define RAW_CHECK(condition, message) \
do { \
if (!(condition)) { \
WRITE_TO_STDERR("Check failed: " #condition ": " message "\n", \
sizeof("Check failed: " #condition ": " message "\n")-1);\
abort(); \
} \
} while (0)
// This is like RAW_CHECK, but only in debug-mode
#ifdef NDEBUG
enum { DEBUG_MODE = 0 };
#define RAW_DCHECK(condition, message)
#else
enum { DEBUG_MODE = 1 };
#define RAW_DCHECK(condition, message) RAW_CHECK(condition, message)
#endif
// This prints errno as well. Note we use write instead of printf/puts to
// avoid the risk we'll call malloc().
#define PCHECK(condition) \
do { \
if (!(condition)) { \
const int err_no = errno; \
WRITE_TO_STDERR("Check failed: " #condition ": ", \
sizeof("Check failed: " #condition ": ")-1); \
WRITE_TO_STDERR(strerror(err_no), strlen(strerror(err_no))); \
WRITE_TO_STDERR("\n", sizeof("\n")-1); \
abort(); \
} \
} while (0)
// Helper macro for binary operators; prints the two values on error
// Don't use this macro directly in your code, use CHECK_EQ et al below
// WARNING: These don't compile correctly if one of the arguments is a pointer
// and the other is NULL. To work around this, simply static_cast NULL to the
// type of the desired pointer.
// TODO(jandrews): Also print the values in case of failure. Requires some
// sort of type-sensitive ToString() function.
#define CHECK_OP(op, val1, val2) \
do { \
if (!((val1) op (val2))) { \
fprintf(stderr, "Check failed: %s %s %s\n", #val1, #op, #val2); \
abort(); \
} \
} while (0)
#define CHECK_EQ(val1, val2) CHECK_OP(==, val1, val2)
#define CHECK_NE(val1, val2) CHECK_OP(!=, val1, val2)
#define CHECK_LE(val1, val2) CHECK_OP(<=, val1, val2)
#define CHECK_LT(val1, val2) CHECK_OP(< , val1, val2)
#define CHECK_GE(val1, val2) CHECK_OP(>=, val1, val2)
#define CHECK_GT(val1, val2) CHECK_OP(> , val1, val2)
// Synonyms for CHECK_* that are used in some unittests.
#define EXPECT_EQ(val1, val2) CHECK_EQ(val1, val2)
#define EXPECT_NE(val1, val2) CHECK_NE(val1, val2)
#define EXPECT_LE(val1, val2) CHECK_LE(val1, val2)
#define EXPECT_LT(val1, val2) CHECK_LT(val1, val2)
#define EXPECT_GE(val1, val2) CHECK_GE(val1, val2)
#define EXPECT_GT(val1, val2) CHECK_GT(val1, val2)
#define ASSERT_EQ(val1, val2) EXPECT_EQ(val1, val2)
#define ASSERT_NE(val1, val2) EXPECT_NE(val1, val2)
#define ASSERT_LE(val1, val2) EXPECT_LE(val1, val2)
#define ASSERT_LT(val1, val2) EXPECT_LT(val1, val2)
#define ASSERT_GE(val1, val2) EXPECT_GE(val1, val2)
#define ASSERT_GT(val1, val2) EXPECT_GT(val1, val2)
// As are these variants.
#define EXPECT_TRUE(cond) CHECK(cond)
#define EXPECT_FALSE(cond) CHECK(!(cond))
#define EXPECT_STREQ(a, b) CHECK(strcmp(a, b) == 0)
#define ASSERT_TRUE(cond) EXPECT_TRUE(cond)
#define ASSERT_FALSE(cond) EXPECT_FALSE(cond)
#define ASSERT_STREQ(a, b) EXPECT_STREQ(a, b)
// Used for (libc) functions that return -1 and set errno
#define CHECK_ERR(invocation) PCHECK((invocation) != -1)
// A few more checks that only happen in debug mode
#ifdef NDEBUG
#define DCHECK_EQ(val1, val2)
#define DCHECK_NE(val1, val2)
#define DCHECK_LE(val1, val2)
#define DCHECK_LT(val1, val2)
#define DCHECK_GE(val1, val2)
#define DCHECK_GT(val1, val2)
#else
#define DCHECK_EQ(val1, val2) CHECK_EQ(val1, val2)
#define DCHECK_NE(val1, val2) CHECK_NE(val1, val2)
#define DCHECK_LE(val1, val2) CHECK_LE(val1, val2)
#define DCHECK_LT(val1, val2) CHECK_LT(val1, val2)
#define DCHECK_GE(val1, val2) CHECK_GE(val1, val2)
#define DCHECK_GT(val1, val2) CHECK_GT(val1, val2)
#endif
#ifdef ERROR
#undef ERROR // may conflict with ERROR macro on windows
#endif
enum LogSeverity {INFO = -1, WARNING = -2, ERROR = -3, FATAL = -4};
// NOTE: we add a newline to the end of the output if it's not there already
inline void LogPrintf(int severity, const char* pat, va_list ap) {
// We write directly to the stderr file descriptor and avoid FILE
// buffering because that may invoke malloc()
char buf[600];
perftools_vsnprintf(buf, sizeof(buf)-1, pat, ap);
if (buf[0] != '\0' && buf[strlen(buf)-1] != '\n') {
assert(strlen(buf)+1 < sizeof(buf));
strcat(buf, "\n");
}
WRITE_TO_STDERR(buf, strlen(buf));
if ((severity) == FATAL)
abort(); // LOG(FATAL) indicates a big problem, so don't run atexit() calls
}
// Note that since the order of global constructors is unspecified,
// global code that calls RAW_LOG may execute before FLAGS_verbose is set.
// Such code will run with verbosity == 0 no matter what.
#define VLOG_IS_ON(severity) (FLAGS_verbose >= severity)
// In a better world, we'd use __VA_ARGS__, but VC++ 7 doesn't support it.
#define LOG_PRINTF(severity, pat) do { \
if (VLOG_IS_ON(severity)) { \
va_list ap; \
va_start(ap, pat); \
LogPrintf(severity, pat, ap); \
va_end(ap); \
} \
} while (0)
// RAW_LOG is the main function; some synonyms are used in unittests.
inline void RAW_LOG(int lvl, const char* pat, ...) { LOG_PRINTF(lvl, pat); }
inline void RAW_VLOG(int lvl, const char* pat, ...) { LOG_PRINTF(lvl, pat); }
inline void LOG(int lvl, const char* pat, ...) { LOG_PRINTF(lvl, pat); }
inline void VLOG(int lvl, const char* pat, ...) { LOG_PRINTF(lvl, pat); }
inline void LOG_IF(int lvl, bool cond, const char* pat, ...) {
if (cond) LOG_PRINTF(lvl, pat);
}
// This isn't technically logging, but it's also IO and also is an
// attempt to be "raw" -- that is, to not use any higher-level libc
// routines that might allocate memory or (ideally) try to allocate
// locks. We use an opaque file handle (not necessarily an int)
// to allow even more low-level stuff in the future.
// Like other "raw" routines, these functions are best effort, and
// thus don't return error codes (except RawOpenForWriting()).
#if defined(_WIN32) || defined(__CYGWIN__) || defined(__CYGWIN32__)
#ifndef NOMINMAX
#define NOMINMAX // @#!$& windows
#endif
#include <windows.h>
typedef HANDLE RawFD;
const RawFD kIllegalRawFD = INVALID_HANDLE_VALUE;
#else
typedef int RawFD;
const RawFD kIllegalRawFD = -1; // what open returns if it fails
#endif // defined(_WIN32) || defined(__CYGWIN__) || defined(__CYGWIN32__)
RawFD RawOpenForWriting(const char* filename); // uses default permissions
void RawWrite(RawFD fd, const char* buf, size_t len);
void RawClose(RawFD fd);
#endif // _LOGGING_H_

View file

@ -0,0 +1,580 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2006, 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.
*/
// A low-level allocator that can be used by other low-level
// modules without introducing dependency cycles.
// This allocator is slow and wasteful of memory;
// it should not be used when performance is key.
#include "base/low_level_alloc.h"
#include "base/dynamic_annotations.h"
#include "base/spinlock.h"
#include "base/logging.h"
#include "malloc_hook-inl.h"
#include <gperftools/malloc_hook.h>
#include <errno.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_MMAP
#include <sys/mman.h>
#endif
#include <new> // for placement-new
// On systems (like freebsd) that don't define MAP_ANONYMOUS, use the old
// form of the name instead.
#ifndef MAP_ANONYMOUS
# define MAP_ANONYMOUS MAP_ANON
#endif
// A first-fit allocator with amortized logarithmic free() time.
LowLevelAlloc::PagesAllocator::~PagesAllocator() {
}
// ---------------------------------------------------------------------------
static const int kMaxLevel = 30;
// We put this class-only struct in a namespace to avoid polluting the
// global namespace with this struct name (thus risking an ODR violation).
namespace low_level_alloc_internal {
// This struct describes one allocated block, or one free block.
struct AllocList {
struct Header {
intptr_t size; // size of entire region, including this field. Must be
// first. Valid in both allocated and unallocated blocks
intptr_t magic; // kMagicAllocated or kMagicUnallocated xor this
LowLevelAlloc::Arena *arena; // pointer to parent arena
void *dummy_for_alignment; // aligns regions to 0 mod 2*sizeof(void*)
} header;
// Next two fields: in unallocated blocks: freelist skiplist data
// in allocated blocks: overlaps with client data
int levels; // levels in skiplist used
AllocList *next[kMaxLevel]; // actually has levels elements.
// The AllocList node may not have room for
// all kMaxLevel entries. See max_fit in
// LLA_SkiplistLevels()
};
}
using low_level_alloc_internal::AllocList;
// ---------------------------------------------------------------------------
// A trivial skiplist implementation. This is used to keep the freelist
// in address order while taking only logarithmic time per insert and delete.
// An integer approximation of log2(size/base)
// Requires size >= base.
static int IntLog2(size_t size, size_t base) {
int result = 0;
for (size_t i = size; i > base; i >>= 1) { // i == floor(size/2**result)
result++;
}
// floor(size / 2**result) <= base < floor(size / 2**(result-1))
// => log2(size/(base+1)) <= result < 1+log2(size/base)
// => result ~= log2(size/base)
return result;
}
// Return a random integer n: p(n)=1/(2**n) if 1 <= n; p(n)=0 if n < 1.
static int Random() {
static uint32 r = 1; // no locking---it's not critical
int result = 1;
while ((((r = r*1103515245 + 12345) >> 30) & 1) == 0) {
result++;
}
return result;
}
// Return a number of skiplist levels for a node of size bytes, where
// base is the minimum node size. Compute level=log2(size / base)+n
// where n is 1 if random is false and otherwise a random number generated with
// the standard distribution for a skiplist: See Random() above.
// Bigger nodes tend to have more skiplist levels due to the log2(size / base)
// term, so first-fit searches touch fewer nodes. "level" is clipped so
// level<kMaxLevel and next[level-1] will fit in the node.
// 0 < LLA_SkiplistLevels(x,y,false) <= LLA_SkiplistLevels(x,y,true) < kMaxLevel
static int LLA_SkiplistLevels(size_t size, size_t base, bool random) {
// max_fit is the maximum number of levels that will fit in a node for the
// given size. We can't return more than max_fit, no matter what the
// random number generator says.
int max_fit = (size-OFFSETOF_MEMBER(AllocList, next)) / sizeof (AllocList *);
int level = IntLog2(size, base) + (random? Random() : 1);
if (level > max_fit) level = max_fit;
if (level > kMaxLevel-1) level = kMaxLevel - 1;
RAW_CHECK(level >= 1, "block not big enough for even one level");
return level;
}
// Return "atleast", the first element of AllocList *head s.t. *atleast >= *e.
// For 0 <= i < head->levels, set prev[i] to "no_greater", where no_greater
// points to the last element at level i in the AllocList less than *e, or is
// head if no such element exists.
static AllocList *LLA_SkiplistSearch(AllocList *head,
AllocList *e, AllocList **prev) {
AllocList *p = head;
for (int level = head->levels - 1; level >= 0; level--) {
for (AllocList *n; (n = p->next[level]) != 0 && n < e; p = n) {
}
prev[level] = p;
}
return (head->levels == 0) ? 0 : prev[0]->next[0];
}
// Insert element *e into AllocList *head. Set prev[] as LLA_SkiplistSearch.
// Requires that e->levels be previously set by the caller (using
// LLA_SkiplistLevels())
static void LLA_SkiplistInsert(AllocList *head, AllocList *e,
AllocList **prev) {
LLA_SkiplistSearch(head, e, prev);
for (; head->levels < e->levels; head->levels++) { // extend prev pointers
prev[head->levels] = head; // to all *e's levels
}
for (int i = 0; i != e->levels; i++) { // add element to list
e->next[i] = prev[i]->next[i];
prev[i]->next[i] = e;
}
}
// Remove element *e from AllocList *head. Set prev[] as LLA_SkiplistSearch().
// Requires that e->levels be previous set by the caller (using
// LLA_SkiplistLevels())
static void LLA_SkiplistDelete(AllocList *head, AllocList *e,
AllocList **prev) {
AllocList *found = LLA_SkiplistSearch(head, e, prev);
RAW_CHECK(e == found, "element not in freelist");
for (int i = 0; i != e->levels && prev[i]->next[i] == e; i++) {
prev[i]->next[i] = e->next[i];
}
while (head->levels > 0 && head->next[head->levels - 1] == 0) {
head->levels--; // reduce head->levels if level unused
}
}
// ---------------------------------------------------------------------------
// Arena implementation
struct LowLevelAlloc::Arena {
Arena() : mu(SpinLock::LINKER_INITIALIZED) {} // does nothing; for static init
explicit Arena(int) : pagesize(0) {} // set pagesize to zero explicitly
// for non-static init
SpinLock mu; // protects freelist, allocation_count,
// pagesize, roundup, min_size
AllocList freelist; // head of free list; sorted by addr (under mu)
int32 allocation_count; // count of allocated blocks (under mu)
int32 flags; // flags passed to NewArena (ro after init)
size_t pagesize; // ==getpagesize() (init under mu, then ro)
size_t roundup; // lowest power of 2 >= max(16,sizeof (AllocList))
// (init under mu, then ro)
size_t min_size; // smallest allocation block size
// (init under mu, then ro)
PagesAllocator *allocator;
};
// The default arena, which is used when 0 is passed instead of an Arena
// pointer.
static struct LowLevelAlloc::Arena default_arena;
// Non-malloc-hooked arenas: used only to allocate metadata for arenas that
// do not want malloc hook reporting, so that for them there's no malloc hook
// reporting even during arena creation.
static struct LowLevelAlloc::Arena unhooked_arena;
static struct LowLevelAlloc::Arena unhooked_async_sig_safe_arena;
namespace {
class DefaultPagesAllocator : public LowLevelAlloc::PagesAllocator {
public:
virtual ~DefaultPagesAllocator() {};
virtual void *MapPages(int32 flags, size_t size);
virtual void UnMapPages(int32 flags, void *addr, size_t size);
};
}
// magic numbers to identify allocated and unallocated blocks
static const intptr_t kMagicAllocated = 0x4c833e95;
static const intptr_t kMagicUnallocated = ~kMagicAllocated;
namespace {
class SCOPED_LOCKABLE ArenaLock {
public:
explicit ArenaLock(LowLevelAlloc::Arena *arena)
EXCLUSIVE_LOCK_FUNCTION(arena->mu)
: left_(false), mask_valid_(false), arena_(arena) {
if ((arena->flags & LowLevelAlloc::kAsyncSignalSafe) != 0) {
// We've decided not to support async-signal-safe arena use until
// there a demonstrated need. Here's how one could do it though
// (would need to be made more portable).
#if 0
sigset_t all;
sigfillset(&all);
this->mask_valid_ =
(pthread_sigmask(SIG_BLOCK, &all, &this->mask_) == 0);
#else
RAW_CHECK(false, "We do not yet support async-signal-safe arena.");
#endif
}
this->arena_->mu.Lock();
}
~ArenaLock() { RAW_CHECK(this->left_, "haven't left Arena region"); }
void Leave() UNLOCK_FUNCTION() {
this->arena_->mu.Unlock();
#if 0
if (this->mask_valid_) {
pthread_sigmask(SIG_SETMASK, &this->mask_, 0);
}
#endif
this->left_ = true;
}
private:
bool left_; // whether left region
bool mask_valid_;
#if 0
sigset_t mask_; // old mask of blocked signals
#endif
LowLevelAlloc::Arena *arena_;
DISALLOW_COPY_AND_ASSIGN(ArenaLock);
};
} // anonymous namespace
// create an appropriate magic number for an object at "ptr"
// "magic" should be kMagicAllocated or kMagicUnallocated
inline static intptr_t Magic(intptr_t magic, AllocList::Header *ptr) {
return magic ^ reinterpret_cast<intptr_t>(ptr);
}
// Initialize the fields of an Arena
static void ArenaInit(LowLevelAlloc::Arena *arena) {
if (arena->pagesize == 0) {
arena->pagesize = getpagesize();
// Round up block sizes to a power of two close to the header size.
arena->roundup = 16;
while (arena->roundup < sizeof (arena->freelist.header)) {
arena->roundup += arena->roundup;
}
// Don't allocate blocks less than twice the roundup size to avoid tiny
// free blocks.
arena->min_size = 2 * arena->roundup;
arena->freelist.header.size = 0;
arena->freelist.header.magic =
Magic(kMagicUnallocated, &arena->freelist.header);
arena->freelist.header.arena = arena;
arena->freelist.levels = 0;
memset(arena->freelist.next, 0, sizeof (arena->freelist.next));
arena->allocation_count = 0;
if (arena == &default_arena) {
// Default arena should be hooked, e.g. for heap-checker to trace
// pointer chains through objects in the default arena.
arena->flags = LowLevelAlloc::kCallMallocHook;
} else if (arena == &unhooked_async_sig_safe_arena) {
arena->flags = LowLevelAlloc::kAsyncSignalSafe;
} else {
arena->flags = 0; // other arenas' flags may be overridden by client,
// but unhooked_arena will have 0 in 'flags'.
}
arena->allocator = LowLevelAlloc::GetDefaultPagesAllocator();
}
}
// L < meta_data_arena->mu
LowLevelAlloc::Arena *LowLevelAlloc::NewArena(int32 flags,
Arena *meta_data_arena) {
return NewArenaWithCustomAlloc(flags, meta_data_arena, NULL);
}
// L < meta_data_arena->mu
LowLevelAlloc::Arena *LowLevelAlloc::NewArenaWithCustomAlloc(int32 flags,
Arena *meta_data_arena,
PagesAllocator *allocator) {
RAW_CHECK(meta_data_arena != 0, "must pass a valid arena");
if (meta_data_arena == &default_arena) {
if ((flags & LowLevelAlloc::kAsyncSignalSafe) != 0) {
meta_data_arena = &unhooked_async_sig_safe_arena;
} else if ((flags & LowLevelAlloc::kCallMallocHook) == 0) {
meta_data_arena = &unhooked_arena;
}
}
// Arena(0) uses the constructor for non-static contexts
Arena *result =
new (AllocWithArena(sizeof (*result), meta_data_arena)) Arena(0);
ArenaInit(result);
result->flags = flags;
if (allocator) {
result->allocator = allocator;
}
return result;
}
// L < arena->mu, L < arena->arena->mu
bool LowLevelAlloc::DeleteArena(Arena *arena) {
RAW_CHECK(arena != 0 && arena != &default_arena && arena != &unhooked_arena,
"may not delete default arena");
ArenaLock section(arena);
bool empty = (arena->allocation_count == 0);
section.Leave();
if (empty) {
while (arena->freelist.next[0] != 0) {
AllocList *region = arena->freelist.next[0];
size_t size = region->header.size;
arena->freelist.next[0] = region->next[0];
RAW_CHECK(region->header.magic ==
Magic(kMagicUnallocated, &region->header),
"bad magic number in DeleteArena()");
RAW_CHECK(region->header.arena == arena,
"bad arena pointer in DeleteArena()");
RAW_CHECK(size % arena->pagesize == 0,
"empty arena has non-page-aligned block size");
RAW_CHECK(reinterpret_cast<intptr_t>(region) % arena->pagesize == 0,
"empty arena has non-page-aligned block");
int munmap_result;
if ((arena->flags & LowLevelAlloc::kAsyncSignalSafe) == 0) {
munmap_result = munmap(region, size);
} else {
munmap_result = MallocHook::UnhookedMUnmap(region, size);
}
RAW_CHECK(munmap_result == 0,
"LowLevelAlloc::DeleteArena: munmap failed address");
}
Free(arena);
}
return empty;
}
// ---------------------------------------------------------------------------
// Return value rounded up to next multiple of align.
// align must be a power of two.
static intptr_t RoundUp(intptr_t addr, intptr_t align) {
return (addr + align - 1) & ~(align - 1);
}
// Equivalent to "return prev->next[i]" but with sanity checking
// that the freelist is in the correct order, that it
// consists of regions marked "unallocated", and that no two regions
// are adjacent in memory (they should have been coalesced).
// L < arena->mu
static AllocList *Next(int i, AllocList *prev, LowLevelAlloc::Arena *arena) {
RAW_CHECK(i < prev->levels, "too few levels in Next()");
AllocList *next = prev->next[i];
if (next != 0) {
RAW_CHECK(next->header.magic == Magic(kMagicUnallocated, &next->header),
"bad magic number in Next()");
RAW_CHECK(next->header.arena == arena,
"bad arena pointer in Next()");
if (prev != &arena->freelist) {
RAW_CHECK(prev < next, "unordered freelist");
RAW_CHECK(reinterpret_cast<char *>(prev) + prev->header.size <
reinterpret_cast<char *>(next), "malformed freelist");
}
}
return next;
}
// Coalesce list item "a" with its successor if they are adjacent.
static void Coalesce(AllocList *a) {
AllocList *n = a->next[0];
if (n != 0 && reinterpret_cast<char *>(a) + a->header.size ==
reinterpret_cast<char *>(n)) {
LowLevelAlloc::Arena *arena = a->header.arena;
a->header.size += n->header.size;
n->header.magic = 0;
n->header.arena = 0;
AllocList *prev[kMaxLevel];
LLA_SkiplistDelete(&arena->freelist, n, prev);
LLA_SkiplistDelete(&arena->freelist, a, prev);
a->levels = LLA_SkiplistLevels(a->header.size, arena->min_size, true);
LLA_SkiplistInsert(&arena->freelist, a, prev);
}
}
// Adds block at location "v" to the free list
// L >= arena->mu
static void AddToFreelist(void *v, LowLevelAlloc::Arena *arena) {
AllocList *f = reinterpret_cast<AllocList *>(
reinterpret_cast<char *>(v) - sizeof (f->header));
RAW_CHECK(f->header.magic == Magic(kMagicAllocated, &f->header),
"bad magic number in AddToFreelist()");
RAW_CHECK(f->header.arena == arena,
"bad arena pointer in AddToFreelist()");
f->levels = LLA_SkiplistLevels(f->header.size, arena->min_size, true);
AllocList *prev[kMaxLevel];
LLA_SkiplistInsert(&arena->freelist, f, prev);
f->header.magic = Magic(kMagicUnallocated, &f->header);
Coalesce(f); // maybe coalesce with successor
Coalesce(prev[0]); // maybe coalesce with predecessor
}
// Frees storage allocated by LowLevelAlloc::Alloc().
// L < arena->mu
void LowLevelAlloc::Free(void *v) {
if (v != 0) {
AllocList *f = reinterpret_cast<AllocList *>(
reinterpret_cast<char *>(v) - sizeof (f->header));
RAW_CHECK(f->header.magic == Magic(kMagicAllocated, &f->header),
"bad magic number in Free()");
LowLevelAlloc::Arena *arena = f->header.arena;
if ((arena->flags & kCallMallocHook) != 0) {
MallocHook::InvokeDeleteHook(v);
}
ArenaLock section(arena);
AddToFreelist(v, arena);
RAW_CHECK(arena->allocation_count > 0, "nothing in arena to free");
arena->allocation_count--;
section.Leave();
}
}
// allocates and returns a block of size bytes, to be freed with Free()
// L < arena->mu
static void *DoAllocWithArena(size_t request, LowLevelAlloc::Arena *arena) {
void *result = 0;
if (request != 0) {
AllocList *s; // will point to region that satisfies request
ArenaLock section(arena);
ArenaInit(arena);
// round up with header
size_t req_rnd = RoundUp(request + sizeof (s->header), arena->roundup);
for (;;) { // loop until we find a suitable region
// find the minimum levels that a block of this size must have
int i = LLA_SkiplistLevels(req_rnd, arena->min_size, false) - 1;
if (i < arena->freelist.levels) { // potential blocks exist
AllocList *before = &arena->freelist; // predecessor of s
while ((s = Next(i, before, arena)) != 0 && s->header.size < req_rnd) {
before = s;
}
if (s != 0) { // we found a region
break;
}
}
// we unlock before mmap() both because mmap() may call a callback hook,
// and because it may be slow.
arena->mu.Unlock();
// mmap generous 64K chunks to decrease
// the chances/impact of fragmentation:
size_t new_pages_size = RoundUp(req_rnd, arena->pagesize * 16);
void *new_pages = arena->allocator->MapPages(arena->flags, new_pages_size);
arena->mu.Lock();
s = reinterpret_cast<AllocList *>(new_pages);
s->header.size = new_pages_size;
// Pretend the block is allocated; call AddToFreelist() to free it.
s->header.magic = Magic(kMagicAllocated, &s->header);
s->header.arena = arena;
AddToFreelist(&s->levels, arena); // insert new region into free list
}
AllocList *prev[kMaxLevel];
LLA_SkiplistDelete(&arena->freelist, s, prev); // remove from free list
// s points to the first free region that's big enough
if (req_rnd + arena->min_size <= s->header.size) { // big enough to split
AllocList *n = reinterpret_cast<AllocList *>
(req_rnd + reinterpret_cast<char *>(s));
n->header.size = s->header.size - req_rnd;
n->header.magic = Magic(kMagicAllocated, &n->header);
n->header.arena = arena;
s->header.size = req_rnd;
AddToFreelist(&n->levels, arena);
}
s->header.magic = Magic(kMagicAllocated, &s->header);
RAW_CHECK(s->header.arena == arena, "");
arena->allocation_count++;
section.Leave();
result = &s->levels;
}
return result;
}
void *LowLevelAlloc::Alloc(size_t request) {
void *result = DoAllocWithArena(request, &default_arena);
if ((default_arena.flags & kCallMallocHook) != 0) {
// this call must be directly in the user-called allocator function
// for MallocHook::GetCallerStackTrace to work properly
MallocHook::InvokeNewHook(result, request);
}
return result;
}
void *LowLevelAlloc::AllocWithArena(size_t request, Arena *arena) {
RAW_CHECK(arena != 0, "must pass a valid arena");
void *result = DoAllocWithArena(request, arena);
if ((arena->flags & kCallMallocHook) != 0) {
// this call must be directly in the user-called allocator function
// for MallocHook::GetCallerStackTrace to work properly
MallocHook::InvokeNewHook(result, request);
}
return result;
}
LowLevelAlloc::Arena *LowLevelAlloc::DefaultArena() {
return &default_arena;
}
static DefaultPagesAllocator *default_pages_allocator;
static union {
char chars[sizeof(DefaultPagesAllocator)];
void *ptr;
} debug_pages_allocator_space;
LowLevelAlloc::PagesAllocator *LowLevelAlloc::GetDefaultPagesAllocator(void) {
if (default_pages_allocator) {
return default_pages_allocator;
}
default_pages_allocator = new (debug_pages_allocator_space.chars) DefaultPagesAllocator();
return default_pages_allocator;
}
void *DefaultPagesAllocator::MapPages(int32 flags, size_t size) {
void *new_pages;
if ((flags & LowLevelAlloc::kAsyncSignalSafe) != 0) {
new_pages = MallocHook::UnhookedMMap(0, size,
PROT_WRITE|PROT_READ,
MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
} else {
new_pages = mmap(0, size,
PROT_WRITE|PROT_READ,
MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
}
RAW_CHECK(new_pages != MAP_FAILED, "mmap error");
return new_pages;
}
void DefaultPagesAllocator::UnMapPages(int32 flags, void *region, size_t size) {
int munmap_result;
if ((flags & LowLevelAlloc::kAsyncSignalSafe) == 0) {
munmap_result = munmap(region, size);
} else {
munmap_result = MallocHook::UnhookedMUnmap(region, size);
}
RAW_CHECK(munmap_result == 0,
"LowLevelAlloc::DeleteArena: munmap failed address");
}

View file

@ -0,0 +1,120 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2006, 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.
*/
#if !defined(_BASE_LOW_LEVEL_ALLOC_H_)
#define _BASE_LOW_LEVEL_ALLOC_H_
// A simple thread-safe memory allocator that does not depend on
// mutexes or thread-specific data. It is intended to be used
// sparingly, and only when malloc() would introduce an unwanted
// dependency, such as inside the heap-checker.
#include <config.h>
#include <stddef.h> // for size_t
#include "base/basictypes.h"
class LowLevelAlloc {
public:
class PagesAllocator {
public:
virtual ~PagesAllocator();
virtual void *MapPages(int32 flags, size_t size) = 0;
virtual void UnMapPages(int32 flags, void *addr, size_t size) = 0;
};
static PagesAllocator *GetDefaultPagesAllocator(void);
struct Arena; // an arena from which memory may be allocated
// Returns a pointer to a block of at least "request" bytes
// that have been newly allocated from the specific arena.
// for Alloc() call the DefaultArena() is used.
// Returns 0 if passed request==0.
// Does not return 0 under other circumstances; it crashes if memory
// is not available.
static void *Alloc(size_t request)
ATTRIBUTE_SECTION(malloc_hook);
static void *AllocWithArena(size_t request, Arena *arena)
ATTRIBUTE_SECTION(malloc_hook);
// Deallocates a region of memory that was previously allocated with
// Alloc(). Does nothing if passed 0. "s" must be either 0,
// or must have been returned from a call to Alloc() and not yet passed to
// Free() since that call to Alloc(). The space is returned to the arena
// from which it was allocated.
static void Free(void *s) ATTRIBUTE_SECTION(malloc_hook);
// ATTRIBUTE_SECTION(malloc_hook) for Alloc* and Free
// are to put all callers of MallocHook::Invoke* in this module
// into special section,
// so that MallocHook::GetCallerStackTrace can function accurately.
// Create a new arena.
// The root metadata for the new arena is allocated in the
// meta_data_arena; the DefaultArena() can be passed for meta_data_arena.
// These values may be ored into flags:
enum {
// Report calls to Alloc() and Free() via the MallocHook interface.
// Set in the DefaultArena.
kCallMallocHook = 0x0001,
// Make calls to Alloc(), Free() be async-signal-safe. Not set in
// DefaultArena().
kAsyncSignalSafe = 0x0002,
// When used with DefaultArena(), the NewArena() and DeleteArena() calls
// obey the flags given explicitly in the NewArena() call, even if those
// flags differ from the settings in DefaultArena(). So the call
// NewArena(kAsyncSignalSafe, DefaultArena()) is itself async-signal-safe,
// as well as generatating an arena that provides async-signal-safe
// Alloc/Free.
};
static Arena *NewArena(int32 flags, Arena *meta_data_arena);
// note: pages allocator will never be destroyed and allocated pages will never be freed
// When allocator is NULL, it's same as NewArena
static Arena *NewArenaWithCustomAlloc(int32 flags, Arena *meta_data_arena, PagesAllocator *allocator);
// Destroys an arena allocated by NewArena and returns true,
// provided no allocated blocks remain in the arena.
// If allocated blocks remain in the arena, does nothing and
// returns false.
// It is illegal to attempt to destroy the DefaultArena().
static bool DeleteArena(Arena *arena);
// The default arena that always exists.
static Arena *DefaultArena();
private:
LowLevelAlloc(); // no instances
};
#endif

View file

@ -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: Craig Silverstein.
//
// A simple mutex wrapper, supporting locks and read-write locks.
// You should assume the locks are *not* re-entrant.
//
// To use: you should define the following macros in your configure.ac:
// ACX_PTHREAD
// AC_RWLOCK
// The latter is defined in ../autoconf.
//
// This class is meant to be internal-only and should be wrapped by an
// internal namespace. Before you use this module, please give the
// name of your internal namespace for this module. Or, if you want
// to expose it, you'll want to move it to the Google namespace. We
// cannot put this class in global namespace because there can be some
// problems when we have multiple versions of Mutex in each shared object.
//
// NOTE: TryLock() is broken for NO_THREADS mode, at least in NDEBUG
// mode.
//
// CYGWIN NOTE: Cygwin support for rwlock seems to be buggy:
// http://www.cygwin.com/ml/cygwin/2008-12/msg00017.html
// Because of that, we might as well use windows locks for
// cygwin. They seem to be more reliable than the cygwin pthreads layer.
//
// TRICKY IMPLEMENTATION NOTE:
// This class is designed to be safe to use during
// dynamic-initialization -- that is, by global constructors that are
// run before main() starts. The issue in this case is that
// dynamic-initialization happens in an unpredictable order, and it
// could be that someone else's dynamic initializer could call a
// function that tries to acquire this mutex -- but that all happens
// before this mutex's constructor has run. (This can happen even if
// the mutex and the function that uses the mutex are in the same .cc
// file.) Basically, because Mutex does non-trivial work in its
// constructor, it's not, in the naive implementation, safe to use
// before dynamic initialization has run on it.
//
// The solution used here is to pair the actual mutex primitive with a
// bool that is set to true when the mutex is dynamically initialized.
// (Before that it's false.) Then we modify all mutex routines to
// look at the bool, and not try to lock/unlock until the bool makes
// it to true (which happens after the Mutex constructor has run.)
//
// This works because before main() starts -- particularly, during
// dynamic initialization -- there are no threads, so a) it's ok that
// the mutex operations are a no-op, since we don't need locking then
// anyway; and b) we can be quite confident our bool won't change
// state between a call to Lock() and a call to Unlock() (that would
// require a global constructor in one translation unit to call Lock()
// and another global constructor in another translation unit to call
// Unlock() later, which is pretty perverse).
//
// That said, it's tricky, and can conceivably fail; it's safest to
// avoid trying to acquire a mutex in a global constructor, if you
// can. One way it can fail is that a really smart compiler might
// initialize the bool to true at static-initialization time (too
// early) rather than at dynamic-initialization time. To discourage
// that, we set is_safe_ to true in code (not the constructor
// colon-initializer) and set it to true via a function that always
// evaluates to true, but that the compiler can't know always
// evaluates to true. This should be good enough.
//
// A related issue is code that could try to access the mutex
// after it's been destroyed in the global destructors (because
// the Mutex global destructor runs before some other global
// destructor, that tries to acquire the mutex). The way we
// deal with this is by taking a constructor arg that global
// mutexes should pass in, that causes the destructor to do no
// work. We still depend on the compiler not doing anything
// weird to a Mutex's memory after it is destroyed, but for a
// static global variable, that's pretty safe.
#ifndef GOOGLE_MUTEX_H_
#define GOOGLE_MUTEX_H_
#include <config.h>
#if defined(NO_THREADS)
typedef int MutexType; // to keep a lock-count
#elif defined(_WIN32) || defined(__CYGWIN__) || defined(__CYGWIN32__)
# ifndef WIN32_LEAN_AND_MEAN
# define WIN32_LEAN_AND_MEAN // We only need minimal includes
# endif
// We need Windows NT or later for TryEnterCriticalSection(). If you
// don't need that functionality, you can remove these _WIN32_WINNT
// lines, and change TryLock() to assert(0) or something.
# ifndef _WIN32_WINNT
# define _WIN32_WINNT 0x0400
# endif
# include <windows.h>
typedef CRITICAL_SECTION MutexType;
#elif defined(HAVE_PTHREAD) && defined(HAVE_RWLOCK)
// Needed for pthread_rwlock_*. If it causes problems, you could take it
// out, but then you'd have to unset HAVE_RWLOCK (at least on linux -- it
// *does* cause problems for FreeBSD, or MacOSX, but isn't needed
// for locking there.)
# ifdef __linux__
# define _XOPEN_SOURCE 500 // may be needed to get the rwlock calls
# endif
# include <pthread.h>
typedef pthread_rwlock_t MutexType;
#elif defined(HAVE_PTHREAD)
# include <pthread.h>
typedef pthread_mutex_t MutexType;
#else
# error Need to implement mutex.h for your architecture, or #define NO_THREADS
#endif
#include <assert.h>
#include <stdlib.h> // for abort()
#define MUTEX_NAMESPACE perftools_mutex_namespace
namespace MUTEX_NAMESPACE {
class Mutex {
public:
// This is used for the single-arg constructor
enum LinkerInitialized { LINKER_INITIALIZED };
// Create a Mutex that is not held by anybody. This constructor is
// typically used for Mutexes allocated on the heap or the stack.
inline Mutex();
// This constructor should be used for global, static Mutex objects.
// It inhibits work being done by the destructor, which makes it
// safer for code that tries to acqiure this mutex in their global
// destructor.
inline Mutex(LinkerInitialized);
// Destructor
inline ~Mutex();
inline void Lock(); // Block if needed until free then acquire exclusively
inline void Unlock(); // Release a lock acquired via Lock()
inline bool TryLock(); // If free, Lock() and return true, else return false
// Note that on systems that don't support read-write locks, these may
// be implemented as synonyms to Lock() and Unlock(). So you can use
// these for efficiency, but don't use them anyplace where being able
// to do shared reads is necessary to avoid deadlock.
inline void ReaderLock(); // Block until free or shared then acquire a share
inline void ReaderUnlock(); // Release a read share of this Mutex
inline void WriterLock() { Lock(); } // Acquire an exclusive lock
inline void WriterUnlock() { Unlock(); } // Release a lock from WriterLock()
private:
MutexType mutex_;
// We want to make sure that the compiler sets is_safe_ to true only
// when we tell it to, and never makes assumptions is_safe_ is
// always true. volatile is the most reliable way to do that.
volatile bool is_safe_;
// This indicates which constructor was called.
bool destroy_;
inline void SetIsSafe() { is_safe_ = true; }
// Catch the error of writing Mutex when intending MutexLock.
Mutex(Mutex* /*ignored*/) {}
// Disallow "evil" constructors
Mutex(const Mutex&);
void operator=(const Mutex&);
};
// Now the implementation of Mutex for various systems
#if defined(NO_THREADS)
// When we don't have threads, we can be either reading or writing,
// but not both. We can have lots of readers at once (in no-threads
// mode, that's most likely to happen in recursive function calls),
// but only one writer. We represent this by having mutex_ be -1 when
// writing and a number > 0 when reading (and 0 when no lock is held).
//
// In debug mode, we assert these invariants, while in non-debug mode
// we do nothing, for efficiency. That's why everything is in an
// assert.
Mutex::Mutex() : mutex_(0) { }
Mutex::Mutex(Mutex::LinkerInitialized) : mutex_(0) { }
Mutex::~Mutex() { assert(mutex_ == 0); }
void Mutex::Lock() { assert(--mutex_ == -1); }
void Mutex::Unlock() { assert(mutex_++ == -1); }
bool Mutex::TryLock() { if (mutex_) return false; Lock(); return true; }
void Mutex::ReaderLock() { assert(++mutex_ > 0); }
void Mutex::ReaderUnlock() { assert(mutex_-- > 0); }
#elif defined(_WIN32) || defined(__CYGWIN__) || defined(__CYGWIN32__)
Mutex::Mutex() : destroy_(true) {
InitializeCriticalSection(&mutex_);
SetIsSafe();
}
Mutex::Mutex(LinkerInitialized) : destroy_(false) {
InitializeCriticalSection(&mutex_);
SetIsSafe();
}
Mutex::~Mutex() { if (destroy_) DeleteCriticalSection(&mutex_); }
void Mutex::Lock() { if (is_safe_) EnterCriticalSection(&mutex_); }
void Mutex::Unlock() { if (is_safe_) LeaveCriticalSection(&mutex_); }
bool Mutex::TryLock() { return is_safe_ ?
TryEnterCriticalSection(&mutex_) != 0 : true; }
void Mutex::ReaderLock() { Lock(); } // we don't have read-write locks
void Mutex::ReaderUnlock() { Unlock(); }
#elif defined(HAVE_PTHREAD) && defined(HAVE_RWLOCK)
#define SAFE_PTHREAD(fncall) do { /* run fncall if is_safe_ is true */ \
if (is_safe_ && fncall(&mutex_) != 0) abort(); \
} while (0)
Mutex::Mutex() : destroy_(true) {
SetIsSafe();
if (is_safe_ && pthread_rwlock_init(&mutex_, NULL) != 0) abort();
}
Mutex::Mutex(Mutex::LinkerInitialized) : destroy_(false) {
SetIsSafe();
if (is_safe_ && pthread_rwlock_init(&mutex_, NULL) != 0) abort();
}
Mutex::~Mutex() { if (destroy_) SAFE_PTHREAD(pthread_rwlock_destroy); }
void Mutex::Lock() { SAFE_PTHREAD(pthread_rwlock_wrlock); }
void Mutex::Unlock() { SAFE_PTHREAD(pthread_rwlock_unlock); }
bool Mutex::TryLock() { return is_safe_ ?
pthread_rwlock_trywrlock(&mutex_) == 0 : true; }
void Mutex::ReaderLock() { SAFE_PTHREAD(pthread_rwlock_rdlock); }
void Mutex::ReaderUnlock() { SAFE_PTHREAD(pthread_rwlock_unlock); }
#undef SAFE_PTHREAD
#elif defined(HAVE_PTHREAD)
#define SAFE_PTHREAD(fncall) do { /* run fncall if is_safe_ is true */ \
if (is_safe_ && fncall(&mutex_) != 0) abort(); \
} while (0)
Mutex::Mutex() : destroy_(true) {
SetIsSafe();
if (is_safe_ && pthread_mutex_init(&mutex_, NULL) != 0) abort();
}
Mutex::Mutex(Mutex::LinkerInitialized) : destroy_(false) {
SetIsSafe();
if (is_safe_ && pthread_mutex_init(&mutex_, NULL) != 0) abort();
}
Mutex::~Mutex() { if (destroy_) SAFE_PTHREAD(pthread_mutex_destroy); }
void Mutex::Lock() { SAFE_PTHREAD(pthread_mutex_lock); }
void Mutex::Unlock() { SAFE_PTHREAD(pthread_mutex_unlock); }
bool Mutex::TryLock() { return is_safe_ ?
pthread_mutex_trylock(&mutex_) == 0 : true; }
void Mutex::ReaderLock() { Lock(); }
void Mutex::ReaderUnlock() { Unlock(); }
#undef SAFE_PTHREAD
#endif
// --------------------------------------------------------------------------
// Some helper classes
// MutexLock(mu) acquires mu when constructed and releases it when destroyed.
class MutexLock {
public:
explicit MutexLock(Mutex *mu) : mu_(mu) { mu_->Lock(); }
~MutexLock() { mu_->Unlock(); }
private:
Mutex * const mu_;
// Disallow "evil" constructors
MutexLock(const MutexLock&);
void operator=(const MutexLock&);
};
// ReaderMutexLock and WriterMutexLock do the same, for rwlocks
class ReaderMutexLock {
public:
explicit ReaderMutexLock(Mutex *mu) : mu_(mu) { mu_->ReaderLock(); }
~ReaderMutexLock() { mu_->ReaderUnlock(); }
private:
Mutex * const mu_;
// Disallow "evil" constructors
ReaderMutexLock(const ReaderMutexLock&);
void operator=(const ReaderMutexLock&);
};
class WriterMutexLock {
public:
explicit WriterMutexLock(Mutex *mu) : mu_(mu) { mu_->WriterLock(); }
~WriterMutexLock() { mu_->WriterUnlock(); }
private:
Mutex * const mu_;
// Disallow "evil" constructors
WriterMutexLock(const WriterMutexLock&);
void operator=(const WriterMutexLock&);
};
// Catch bug where variable name is omitted, e.g. MutexLock (&mu);
#define MutexLock(x) COMPILE_ASSERT(0, mutex_lock_decl_missing_var_name)
#define ReaderMutexLock(x) COMPILE_ASSERT(0, rmutex_lock_decl_missing_var_name)
#define WriterMutexLock(x) COMPILE_ASSERT(0, wmutex_lock_decl_missing_var_name)
} // namespace MUTEX_NAMESPACE
using namespace MUTEX_NAMESPACE;
#undef MUTEX_NAMESPACE
#endif /* #define GOOGLE_SIMPLE_MUTEX_H_ */

View file

@ -0,0 +1,129 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2006, 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
*/
#include <config.h>
#include "base/spinlock.h"
#include "base/spinlock_internal.h"
#include "base/sysinfo.h" /* for GetSystemCPUsCount() */
// NOTE on the Lock-state values:
//
// kSpinLockFree represents the unlocked state
// kSpinLockHeld represents the locked state with no waiters
// kSpinLockSleeper represents the locked state with waiters
static int adaptive_spin_count = 0;
const base::LinkerInitialized SpinLock::LINKER_INITIALIZED =
base::LINKER_INITIALIZED;
namespace {
struct SpinLock_InitHelper {
SpinLock_InitHelper() {
// On multi-cpu machines, spin for longer before yielding
// the processor or sleeping. Reduces idle time significantly.
if (GetSystemCPUsCount() > 1) {
adaptive_spin_count = 1000;
}
}
};
// Hook into global constructor execution:
// We do not do adaptive spinning before that,
// but nothing lock-intensive should be going on at that time.
static SpinLock_InitHelper init_helper;
inline void SpinlockPause(void) {
#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
__asm__ __volatile__("rep; nop" : : );
#endif
}
} // unnamed namespace
// Monitor the lock to see if its value changes within some time
// period (adaptive_spin_count loop iterations). The last value read
// from the lock is returned from the method.
Atomic32 SpinLock::SpinLoop() {
int c = adaptive_spin_count;
while (base::subtle::NoBarrier_Load(&lockword_) != kSpinLockFree && --c > 0) {
SpinlockPause();
}
return base::subtle::Acquire_CompareAndSwap(&lockword_, kSpinLockFree,
kSpinLockSleeper);
}
void SpinLock::SlowLock() {
Atomic32 lock_value = SpinLoop();
int lock_wait_call_count = 0;
while (lock_value != kSpinLockFree) {
// If the lock is currently held, but not marked as having a sleeper, mark
// it as having a sleeper.
if (lock_value == kSpinLockHeld) {
// Here, just "mark" that the thread is going to sleep. Don't store the
// lock wait time in the lock as that will cause the current lock
// owner to think it experienced contention.
lock_value = base::subtle::Acquire_CompareAndSwap(&lockword_,
kSpinLockHeld,
kSpinLockSleeper);
if (lock_value == kSpinLockHeld) {
// Successfully transitioned to kSpinLockSleeper. Pass
// kSpinLockSleeper to the SpinLockDelay routine to properly indicate
// the last lock_value observed.
lock_value = kSpinLockSleeper;
} else if (lock_value == kSpinLockFree) {
// Lock is free again, so try and acquire it before sleeping. The
// new lock state will be the number of cycles this thread waited if
// this thread obtains the lock.
lock_value = base::subtle::Acquire_CompareAndSwap(&lockword_,
kSpinLockFree,
kSpinLockSleeper);
continue; // skip the delay at the end of the loop
}
}
// Wait for an OS specific delay.
base::internal::SpinLockDelay(&lockword_, lock_value,
++lock_wait_call_count);
// Spin again after returning from the wait routine to give this thread
// some chance of obtaining the lock.
lock_value = SpinLoop();
}
}
void SpinLock::SlowUnlock() {
// wake waiter if necessary
base::internal::SpinLockWake(&lockword_, false);
}

View file

@ -0,0 +1,132 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2006, 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
*/
// SpinLock is async signal safe.
// If used within a signal handler, all lock holders
// should block the signal even outside the signal handler.
#ifndef BASE_SPINLOCK_H_
#define BASE_SPINLOCK_H_
#include <config.h>
#include "base/atomicops.h"
#include "base/basictypes.h"
#include "base/dynamic_annotations.h"
#include "base/thread_annotations.h"
class LOCKABLE SpinLock {
public:
SpinLock() : lockword_(kSpinLockFree) { }
// Special constructor for use with static SpinLock objects. E.g.,
//
// static SpinLock lock(base::LINKER_INITIALIZED);
//
// When intialized using this constructor, we depend on the fact
// that the linker has already initialized the memory appropriately.
// A SpinLock constructed like this can be freely used from global
// initializers without worrying about the order in which global
// initializers run.
explicit SpinLock(base::LinkerInitialized /*x*/) {
// Does nothing; lockword_ is already initialized
}
// Acquire this SpinLock.
inline void Lock() EXCLUSIVE_LOCK_FUNCTION() {
if (base::subtle::Acquire_CompareAndSwap(&lockword_, kSpinLockFree,
kSpinLockHeld) != kSpinLockFree) {
SlowLock();
}
}
// Try to acquire this SpinLock without blocking and return true if the
// acquisition was successful. If the lock was not acquired, false is
// returned. If this SpinLock is free at the time of the call, TryLock
// will return true with high probability.
inline bool TryLock() EXCLUSIVE_TRYLOCK_FUNCTION(true) {
bool res =
(base::subtle::Acquire_CompareAndSwap(&lockword_, kSpinLockFree,
kSpinLockHeld) == kSpinLockFree);
return res;
}
// Release this SpinLock, which must be held by the calling thread.
inline void Unlock() UNLOCK_FUNCTION() {
uint64 prev_value = static_cast<uint64>(
base::subtle::Release_AtomicExchange(&lockword_, kSpinLockFree));
if (prev_value != kSpinLockHeld) {
// Speed the wakeup of any waiter.
SlowUnlock();
}
}
// Determine if the lock is held. When the lock is held by the invoking
// thread, true will always be returned. Intended to be used as
// CHECK(lock.IsHeld()).
inline bool IsHeld() const {
return base::subtle::NoBarrier_Load(&lockword_) != kSpinLockFree;
}
static const base::LinkerInitialized LINKER_INITIALIZED; // backwards compat
private:
enum { kSpinLockFree = 0 };
enum { kSpinLockHeld = 1 };
enum { kSpinLockSleeper = 2 };
volatile Atomic32 lockword_;
void SlowLock();
void SlowUnlock();
Atomic32 SpinLoop();
DISALLOW_COPY_AND_ASSIGN(SpinLock);
};
// Corresponding locker object that arranges to acquire a spinlock for
// the duration of a C++ scope.
class SCOPED_LOCKABLE SpinLockHolder {
private:
SpinLock* lock_;
public:
inline explicit SpinLockHolder(SpinLock* l) EXCLUSIVE_LOCK_FUNCTION(l)
: lock_(l) {
l->Lock();
}
inline ~SpinLockHolder() UNLOCK_FUNCTION() { lock_->Unlock(); }
};
// Catch bug where variable name is omitted, e.g. SpinLockHolder (&lock);
#define SpinLockHolder(x) COMPILE_ASSERT(0, spin_lock_decl_missing_var_name)
#endif // BASE_SPINLOCK_H_

View file

@ -0,0 +1,102 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2010, 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.
*/
// The OS-specific header included below must provide two calls:
// base::internal::SpinLockDelay() and base::internal::SpinLockWake().
// See spinlock_internal.h for the spec of SpinLockWake().
// void SpinLockDelay(volatile Atomic32 *w, int32 value, int loop)
// SpinLockDelay() generates an apprproate spin delay on iteration "loop" of a
// spin loop on location *w, whose previously observed value was "value".
// SpinLockDelay() may do nothing, may yield the CPU, may sleep a clock tick,
// or may wait for a delay that can be truncated by a call to SpinlockWake(w).
// In all cases, it must return in bounded time even if SpinlockWake() is not
// called.
#include "base/spinlock_internal.h"
// forward declaration for use by spinlock_*-inl.h
namespace base { namespace internal { static int SuggestedDelayNS(int loop); }}
#if defined(_WIN32)
#include "base/spinlock_win32-inl.h"
#elif defined(__linux__)
#include "base/spinlock_linux-inl.h"
#else
#include "base/spinlock_posix-inl.h"
#endif
namespace base {
namespace internal {
// Return a suggested delay in nanoseconds for iteration number "loop"
static int SuggestedDelayNS(int loop) {
// Weak pseudo-random number generator to get some spread between threads
// when many are spinning.
#ifdef BASE_HAS_ATOMIC64
static base::subtle::Atomic64 rand;
uint64 r = base::subtle::NoBarrier_Load(&rand);
r = 0x5deece66dLL * r + 0xb; // numbers from nrand48()
base::subtle::NoBarrier_Store(&rand, r);
r <<= 16; // 48-bit random number now in top 48-bits.
if (loop < 0 || loop > 32) { // limit loop to 0..32
loop = 32;
}
// loop>>3 cannot exceed 4 because loop cannot exceed 32.
// Select top 20..24 bits of lower 48 bits,
// giving approximately 0ms to 16ms.
// Mean is exponential in loop for first 32 iterations, then 8ms.
// The futex path multiplies this by 16, since we expect explicit wakeups
// almost always on that path.
return r >> (44 - (loop >> 3));
#else
static Atomic32 rand;
uint32 r = base::subtle::NoBarrier_Load(&rand);
r = 0x343fd * r + 0x269ec3; // numbers from MSVC++
base::subtle::NoBarrier_Store(&rand, r);
r <<= 1; // 31-bit random number now in top 31-bits.
if (loop < 0 || loop > 32) { // limit loop to 0..32
loop = 32;
}
// loop>>3 cannot exceed 4 because loop cannot exceed 32.
// Select top 20..24 bits of lower 31 bits,
// giving approximately 0ms to 16ms.
// Mean is exponential in loop for first 32 iterations, then 8ms.
// The futex path multiplies this by 16, since we expect explicit wakeups
// almost always on that path.
return r >> (12 - (loop >> 3));
#endif
}
} // namespace internal
} // namespace base

View file

@ -0,0 +1,51 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2010, 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.
*
* ---
* This file is an internal part spinlock.cc and once.cc
* It may not be used directly by code outside of //base.
*/
#ifndef BASE_SPINLOCK_INTERNAL_H_
#define BASE_SPINLOCK_INTERNAL_H_
#include <config.h>
#include "base/basictypes.h"
#include "base/atomicops.h"
namespace base {
namespace internal {
void SpinLockWake(volatile Atomic32 *w, bool all);
void SpinLockDelay(volatile Atomic32 *w, int32 value, int loop);
} // namespace internal
} // namespace base
#endif

View file

@ -0,0 +1,103 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2009, 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.
*
* ---
* This file is a Linux-specific part of spinlock_internal.cc
*/
#include <errno.h>
#include <limits.h>
#include <sched.h>
#include <sys/syscall.h>
#include <time.h>
#include <unistd.h>
#define FUTEX_WAIT 0
#define FUTEX_WAKE 1
#define FUTEX_PRIVATE_FLAG 128
// Note: Instead of making direct system calls that are inlined, we rely
// on the syscall() function in glibc to do the right thing.
static bool have_futex;
static int futex_private_flag = FUTEX_PRIVATE_FLAG;
namespace {
static struct InitModule {
InitModule() {
int x = 0;
// futexes are ints, so we can use them only when
// that's the same size as the lockword_ in SpinLock.
have_futex = (sizeof(Atomic32) == sizeof(int) &&
syscall(__NR_futex, &x, FUTEX_WAKE, 1, NULL, NULL, 0) >= 0);
if (have_futex && syscall(__NR_futex, &x, FUTEX_WAKE | futex_private_flag,
1, NULL, NULL, 0) < 0) {
futex_private_flag = 0;
}
}
} init_module;
} // anonymous namespace
namespace base {
namespace internal {
void SpinLockDelay(volatile Atomic32 *w, int32 value, int loop) {
if (loop != 0) {
int save_errno = errno;
struct timespec tm;
tm.tv_sec = 0;
if (have_futex) {
tm.tv_nsec = base::internal::SuggestedDelayNS(loop);
} else {
tm.tv_nsec = 2000001; // above 2ms so linux 2.4 doesn't spin
}
if (have_futex) {
tm.tv_nsec *= 16; // increase the delay; we expect explicit wakeups
syscall(__NR_futex, reinterpret_cast<int*>(const_cast<Atomic32*>(w)),
FUTEX_WAIT | futex_private_flag, value,
reinterpret_cast<struct kernel_timespec*>(&tm), NULL, 0);
} else {
nanosleep(&tm, NULL);
}
errno = save_errno;
}
}
void SpinLockWake(volatile Atomic32 *w, bool all) {
if (have_futex) {
syscall(__NR_futex, reinterpret_cast<int*>(const_cast<Atomic32*>(w)),
FUTEX_WAKE | futex_private_flag, all ? INT_MAX : 1, NULL, NULL, 0);
}
}
} // namespace internal
} // namespace base

View file

@ -0,0 +1,63 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2009, 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.
*
* ---
* This file is a Posix-specific part of spinlock_internal.cc
*/
#include <config.h>
#include <errno.h>
#ifdef HAVE_SCHED_H
#include <sched.h> /* For sched_yield() */
#endif
#include <time.h> /* For nanosleep() */
namespace base {
namespace internal {
void SpinLockDelay(volatile Atomic32 *w, int32 value, int loop) {
int save_errno = errno;
if (loop == 0) {
} else if (loop == 1) {
sched_yield();
} else {
struct timespec tm;
tm.tv_sec = 0;
tm.tv_nsec = base::internal::SuggestedDelayNS(loop);
nanosleep(&tm, NULL);
}
errno = save_errno;
}
void SpinLockWake(volatile Atomic32 *w, bool all) {
}
} // namespace internal
} // namespace base

View file

@ -0,0 +1,54 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2009, 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.
*
* ---
* This file is a Win32-specific part of spinlock_internal.cc
*/
#include <windows.h>
namespace base {
namespace internal {
void SpinLockDelay(volatile Atomic32 *w, int32 value, int loop) {
if (loop == 0) {
} else if (loop == 1) {
Sleep(0);
} else {
Sleep(base::internal::SuggestedDelayNS(loop) / 1000000);
}
}
void SpinLockWake(volatile Atomic32 *w, bool all) {
}
} // namespace internal
} // namespace base

View file

@ -0,0 +1,98 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2006, 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: Maxim Lifantsev
*/
#ifndef BASE_STL_ALLOCATOR_H_
#define BASE_STL_ALLOCATOR_H_
#include <config.h>
#include <stddef.h> // for ptrdiff_t
#include <limits>
#include "base/logging.h"
// Generic allocator class for STL objects
// that uses a given type-less allocator Alloc, which must provide:
// static void* Alloc::Allocate(size_t size);
// static void Alloc::Free(void* ptr, size_t size);
//
// STL_Allocator<T, MyAlloc> provides the same thread-safety
// guarantees as MyAlloc.
//
// Usage example:
// set<T, less<T>, STL_Allocator<T, MyAlloc> > my_set;
// CAVEAT: Parts of the code below are probably specific
// to the STL version(s) we are using.
// The code is simply lifted from what std::allocator<> provides.
template <typename T, class Alloc>
class STL_Allocator {
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef T value_type;
template <class T1> struct rebind {
typedef STL_Allocator<T1, Alloc> other;
};
STL_Allocator() { }
STL_Allocator(const STL_Allocator&) { }
template <class T1> STL_Allocator(const STL_Allocator<T1, Alloc>&) { }
~STL_Allocator() { }
pointer address(reference x) const { return &x; }
const_pointer address(const_reference x) const { return &x; }
pointer allocate(size_type n, const void* = 0) {
RAW_DCHECK((n * sizeof(T)) / sizeof(T) == n, "n is too big to allocate");
return static_cast<T*>(Alloc::Allocate(n * sizeof(T)));
}
void deallocate(pointer p, size_type n) { Alloc::Free(p, n * sizeof(T)); }
size_type max_size() const { return size_t(-1) / sizeof(T); }
void construct(pointer p, const T& val) { ::new(p) T(val); }
void construct(pointer p) { ::new(p) T(); }
void destroy(pointer p) { p->~T(); }
// There's no state, so these allocators are always equal
bool operator==(const STL_Allocator&) const { return true; }
};
#endif // BASE_STL_ALLOCATOR_H_

View file

@ -0,0 +1,878 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2006, 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.
#include <config.h>
#if (defined(_WIN32) || defined(__MINGW32__)) && !defined(__CYGWIN__) && !defined(__CYGWIN32)
# define PLATFORM_WINDOWS 1
#endif
#include <ctype.h> // for isspace()
#include <stdlib.h> // for getenv()
#include <stdio.h> // for snprintf(), sscanf()
#include <string.h> // for memmove(), memchr(), etc.
#include <fcntl.h> // for open()
#include <errno.h> // for errno
#ifdef HAVE_UNISTD_H
#include <unistd.h> // for read()
#endif
#if defined __MACH__ // Mac OS X, almost certainly
#include <mach-o/dyld.h> // for iterating over dll's in ProcMapsIter
#include <mach-o/loader.h> // for iterating over dll's in ProcMapsIter
#include <sys/types.h>
#include <sys/sysctl.h> // how we figure out numcpu's on OS X
#elif defined __FreeBSD__
#include <sys/sysctl.h>
#elif defined __sun__ // Solaris
#include <procfs.h> // for, e.g., prmap_t
#elif defined(PLATFORM_WINDOWS)
#include <process.h> // for getpid() (actually, _getpid())
#include <shlwapi.h> // for SHGetValueA()
#include <tlhelp32.h> // for Module32First()
#endif
#include "base/sysinfo.h"
#include "base/commandlineflags.h"
#include "base/dynamic_annotations.h" // for RunningOnValgrind
#include "base/logging.h"
#ifdef PLATFORM_WINDOWS
#ifdef MODULEENTRY32
// In a change from the usual W-A pattern, there is no A variant of
// MODULEENTRY32. Tlhelp32.h #defines the W variant, but not the A.
// In unicode mode, tlhelp32.h #defines MODULEENTRY32 to be
// MODULEENTRY32W. These #undefs are the only way I see to get back
// access to the original, ascii struct (and related functions).
#undef MODULEENTRY32
#undef Module32First
#undef Module32Next
#undef PMODULEENTRY32
#undef LPMODULEENTRY32
#endif /* MODULEENTRY32 */
// MinGW doesn't seem to define this, perhaps some windowsen don't either.
#ifndef TH32CS_SNAPMODULE32
#define TH32CS_SNAPMODULE32 0
#endif /* TH32CS_SNAPMODULE32 */
#endif /* PLATFORM_WINDOWS */
// Re-run fn until it doesn't cause EINTR.
#define NO_INTR(fn) do {} while ((fn) < 0 && errno == EINTR)
// open/read/close can set errno, which may be illegal at this
// time, so prefer making the syscalls directly if we can.
#ifdef HAVE_SYS_SYSCALL_H
# include <sys/syscall.h>
#endif
#ifdef SYS_open // solaris 11, at least sometimes, only defines SYS_openat
# define safeopen(filename, mode) syscall(SYS_open, filename, mode)
#else
# define safeopen(filename, mode) open(filename, mode)
#endif
#ifdef SYS_read
# define saferead(fd, buffer, size) syscall(SYS_read, fd, buffer, size)
#else
# define saferead(fd, buffer, size) read(fd, buffer, size)
#endif
#ifdef SYS_close
# define safeclose(fd) syscall(SYS_close, fd)
#else
# define safeclose(fd) close(fd)
#endif
// ----------------------------------------------------------------------
// GetenvBeforeMain()
// GetUniquePathFromEnv()
// Some non-trivial getenv-related functions.
// ----------------------------------------------------------------------
// we reimplement memcmp and friends to avoid depending on any glibc
// calls too early in the process lifetime. This allows us to use
// GetenvBeforeMain from inside ifunc handler
static int slow_memcmp(const void *_a, const void *_b, size_t n) {
const uint8_t *a = reinterpret_cast<const uint8_t *>(_a);
const uint8_t *b = reinterpret_cast<const uint8_t *>(_b);
while (n-- != 0) {
uint8_t ac = *a++;
uint8_t bc = *b++;
if (ac != bc) {
if (ac < bc) {
return -1;
}
return 1;
}
}
return 0;
}
static const char *slow_memchr(const char *s, int c, size_t n) {
uint8_t ch = static_cast<uint8_t>(c);
while (n--) {
if (*s++ == ch) {
return s - 1;
}
}
return 0;
}
static size_t slow_strlen(const char *s) {
const char *s2 = slow_memchr(s, '\0', static_cast<size_t>(-1));
return s2 - s;
}
// It's not safe to call getenv() in the malloc hooks, because they
// might be called extremely early, before libc is done setting up
// correctly. In particular, the thread library may not be done
// setting up errno. So instead, we use the built-in __environ array
// if it exists, and otherwise read /proc/self/environ directly, using
// system calls to read the file, and thus avoid setting errno.
// /proc/self/environ has a limit of how much data it exports (around
// 8K), so it's not an ideal solution.
const char* GetenvBeforeMain(const char* name) {
const int namelen = slow_strlen(name);
#if defined(HAVE___ENVIRON) // if we have it, it's declared in unistd.h
if (__environ) { // can exist but be NULL, if statically linked
for (char** p = __environ; *p; p++) {
if (!slow_memcmp(*p, name, namelen) && (*p)[namelen] == '=')
return *p + namelen+1;
}
return NULL;
}
#endif
#if defined(PLATFORM_WINDOWS)
// TODO(mbelshe) - repeated calls to this function will overwrite the
// contents of the static buffer.
static char envvar_buf[1024]; // enough to hold any envvar we care about
if (!GetEnvironmentVariableA(name, envvar_buf, sizeof(envvar_buf)-1))
return NULL;
return envvar_buf;
#endif
// static is ok because this function should only be called before
// main(), when we're single-threaded.
static char envbuf[16<<10];
if (*envbuf == '\0') { // haven't read the environ yet
int fd = safeopen("/proc/self/environ", O_RDONLY);
// The -2 below guarantees the last two bytes of the buffer will be \0\0
if (fd == -1 || // unable to open the file, fall back onto libc
saferead(fd, envbuf, sizeof(envbuf) - 2) < 0) { // error reading file
RAW_VLOG(1, "Unable to open /proc/self/environ, falling back "
"on getenv(\"%s\"), which may not work", name);
if (fd != -1) safeclose(fd);
return getenv(name);
}
safeclose(fd);
}
const char* p = envbuf;
while (*p != '\0') { // will happen at the \0\0 that terminates the buffer
// proc file has the format NAME=value\0NAME=value\0NAME=value\0...
const char* endp = (char*)slow_memchr(p, '\0',
sizeof(envbuf) - (p - envbuf));
if (endp == NULL) // this entry isn't NUL terminated
return NULL;
else if (!slow_memcmp(p, name, namelen) && p[namelen] == '=') // it's a match
return p + namelen+1; // point after =
p = endp + 1;
}
return NULL; // env var never found
}
extern "C" {
const char* TCMallocGetenvSafe(const char* name) {
return GetenvBeforeMain(name);
}
}
// This takes as an argument an environment-variable name (like
// CPUPROFILE) whose value is supposed to be a file-path, and sets
// path to that path, and returns true. If the env var doesn't exist,
// or is the empty string, leave path unchanged and returns false.
// The reason this is non-trivial is that this function handles munged
// pathnames. Here's why:
//
// If we're a child process of the 'main' process, we can't just use
// getenv("CPUPROFILE") -- the parent process will be using that path.
// Instead we append our pid to the pathname. How do we tell if we're a
// child process? Ideally we'd set an environment variable that all
// our children would inherit. But -- and this is seemingly a bug in
// gcc -- if you do a setenv() in a shared libarary in a global
// constructor, the environment setting is lost by the time main() is
// called. The only safe thing we can do in such a situation is to
// modify the existing envvar. So we do a hack: in the parent, we set
// the high bit of the 1st char of CPUPROFILE. In the child, we
// notice the high bit is set and append the pid(). This works
// assuming cpuprofile filenames don't normally have the high bit set
// in their first character! If that assumption is violated, we'll
// still get a profile, but one with an unexpected name.
// TODO(csilvers): set an envvar instead when we can do it reliably.
bool GetUniquePathFromEnv(const char* env_name, char* path) {
char* envval = getenv(env_name);
if (envval == NULL || *envval == '\0')
return false;
if (envval[0] & 128) { // high bit is set
snprintf(path, PATH_MAX, "%c%s_%u", // add pid and clear high bit
envval[0] & 127, envval+1, (unsigned int)(getpid()));
} else {
snprintf(path, PATH_MAX, "%s", envval);
envval[0] |= 128; // set high bit for kids to see
}
return true;
}
int GetSystemCPUsCount()
{
#if defined(PLATFORM_WINDOWS)
// Get the number of processors.
SYSTEM_INFO info;
GetSystemInfo(&info);
return info.dwNumberOfProcessors;
#else
long rv = sysconf(_SC_NPROCESSORS_ONLN);
if (rv < 0) {
return 1;
}
return static_cast<int>(rv);
#endif
}
// ----------------------------------------------------------------------
#if defined __linux__ || defined __FreeBSD__ || defined __sun__ || defined __CYGWIN__ || defined __CYGWIN32__
static void ConstructFilename(const char* spec, pid_t pid,
char* buf, int buf_size) {
CHECK_LT(snprintf(buf, buf_size,
spec,
static_cast<int>(pid ? pid : getpid())), buf_size);
}
#endif
// A templatized helper function instantiated for Mach (OS X) only.
// It can handle finding info for both 32 bits and 64 bits.
// Returns true if it successfully handled the hdr, false else.
#ifdef __MACH__ // Mac OS X, almost certainly
template<uint32_t kMagic, uint32_t kLCSegment,
typename MachHeader, typename SegmentCommand>
static bool NextExtMachHelper(const mach_header* hdr,
int current_image, int current_load_cmd,
uint64 *start, uint64 *end, char **flags,
uint64 *offset, int64 *inode, char **filename,
uint64 *file_mapping, uint64 *file_pages,
uint64 *anon_mapping, uint64 *anon_pages,
dev_t *dev) {
static char kDefaultPerms[5] = "r-xp";
if (hdr->magic != kMagic)
return false;
const char* lc = (const char *)hdr + sizeof(MachHeader);
// TODO(csilvers): make this not-quadradic (increment and hold state)
for (int j = 0; j < current_load_cmd; j++) // advance to *our* load_cmd
lc += ((const load_command *)lc)->cmdsize;
if (((const load_command *)lc)->cmd == kLCSegment) {
const intptr_t dlloff = _dyld_get_image_vmaddr_slide(current_image);
const SegmentCommand* sc = (const SegmentCommand *)lc;
if (start) *start = sc->vmaddr + dlloff;
if (end) *end = sc->vmaddr + sc->vmsize + dlloff;
if (flags) *flags = kDefaultPerms; // can we do better?
if (offset) *offset = sc->fileoff;
if (inode) *inode = 0;
if (filename)
*filename = const_cast<char*>(_dyld_get_image_name(current_image));
if (file_mapping) *file_mapping = 0;
if (file_pages) *file_pages = 0; // could we use sc->filesize?
if (anon_mapping) *anon_mapping = 0;
if (anon_pages) *anon_pages = 0;
if (dev) *dev = 0;
return true;
}
return false;
}
#endif
// Finds |c| in |text|, and assign '\0' at the found position.
// The original character at the modified position should be |c|.
// A pointer to the modified position is stored in |endptr|.
// |endptr| should not be NULL.
static bool ExtractUntilChar(char *text, int c, char **endptr) {
CHECK_NE(text, NULL);
CHECK_NE(endptr, NULL);
char *found;
found = strchr(text, c);
if (found == NULL) {
*endptr = NULL;
return false;
}
*endptr = found;
*found = '\0';
return true;
}
// Increments |*text_pointer| while it points a whitespace character.
// It is to follow sscanf's whilespace handling.
static void SkipWhileWhitespace(char **text_pointer, int c) {
if (isspace(c)) {
while (isspace(**text_pointer) && isspace(*((*text_pointer) + 1))) {
++(*text_pointer);
}
}
}
template<class T>
static T StringToInteger(char *text, char **endptr, int base) {
assert(false);
return T();
}
template<>
int StringToInteger<int>(char *text, char **endptr, int base) {
return strtol(text, endptr, base);
}
template<>
int64 StringToInteger<int64>(char *text, char **endptr, int base) {
return strtoll(text, endptr, base);
}
template<>
uint64 StringToInteger<uint64>(char *text, char **endptr, int base) {
return strtoull(text, endptr, base);
}
template<typename T>
static T StringToIntegerUntilChar(
char *text, int base, int c, char **endptr_result) {
CHECK_NE(endptr_result, NULL);
*endptr_result = NULL;
char *endptr_extract;
if (!ExtractUntilChar(text, c, &endptr_extract))
return 0;
T result;
char *endptr_strto;
result = StringToInteger<T>(text, &endptr_strto, base);
*endptr_extract = c;
if (endptr_extract != endptr_strto)
return 0;
*endptr_result = endptr_extract;
SkipWhileWhitespace(endptr_result, c);
return result;
}
static char *CopyStringUntilChar(
char *text, unsigned out_len, int c, char *out) {
char *endptr;
if (!ExtractUntilChar(text, c, &endptr))
return NULL;
strncpy(out, text, out_len);
out[out_len-1] = '\0';
*endptr = c;
SkipWhileWhitespace(&endptr, c);
return endptr;
}
template<typename T>
static bool StringToIntegerUntilCharWithCheck(
T *outptr, char *text, int base, int c, char **endptr) {
*outptr = StringToIntegerUntilChar<T>(*endptr, base, c, endptr);
if (*endptr == NULL || **endptr == '\0') return false;
++(*endptr);
return true;
}
static bool ParseProcMapsLine(char *text, uint64 *start, uint64 *end,
char *flags, uint64 *offset,
int *major, int *minor, int64 *inode,
unsigned *filename_offset) {
#if defined(__linux__)
/*
* It's similar to:
* sscanf(text, "%"SCNx64"-%"SCNx64" %4s %"SCNx64" %x:%x %"SCNd64" %n",
* start, end, flags, offset, major, minor, inode, filename_offset)
*/
char *endptr = text;
if (endptr == NULL || *endptr == '\0') return false;
if (!StringToIntegerUntilCharWithCheck(start, endptr, 16, '-', &endptr))
return false;
if (!StringToIntegerUntilCharWithCheck(end, endptr, 16, ' ', &endptr))
return false;
endptr = CopyStringUntilChar(endptr, 5, ' ', flags);
if (endptr == NULL || *endptr == '\0') return false;
++endptr;
if (!StringToIntegerUntilCharWithCheck(offset, endptr, 16, ' ', &endptr))
return false;
if (!StringToIntegerUntilCharWithCheck(major, endptr, 16, ':', &endptr))
return false;
if (!StringToIntegerUntilCharWithCheck(minor, endptr, 16, ' ', &endptr))
return false;
if (!StringToIntegerUntilCharWithCheck(inode, endptr, 10, ' ', &endptr))
return false;
*filename_offset = (endptr - text);
return true;
#else
return false;
#endif
}
ProcMapsIterator::ProcMapsIterator(pid_t pid) {
Init(pid, NULL, false);
}
ProcMapsIterator::ProcMapsIterator(pid_t pid, Buffer *buffer) {
Init(pid, buffer, false);
}
ProcMapsIterator::ProcMapsIterator(pid_t pid, Buffer *buffer,
bool use_maps_backing) {
Init(pid, buffer, use_maps_backing);
}
void ProcMapsIterator::Init(pid_t pid, Buffer *buffer,
bool use_maps_backing) {
pid_ = pid;
using_maps_backing_ = use_maps_backing;
dynamic_buffer_ = NULL;
if (!buffer) {
// If the user didn't pass in any buffer storage, allocate it
// now. This is the normal case; the signal handler passes in a
// static buffer.
buffer = dynamic_buffer_ = new Buffer;
} else {
dynamic_buffer_ = NULL;
}
ibuf_ = buffer->buf_;
stext_ = etext_ = nextline_ = ibuf_;
ebuf_ = ibuf_ + Buffer::kBufSize - 1;
nextline_ = ibuf_;
#if defined(__linux__) || defined(__CYGWIN__) || defined(__CYGWIN32__)
if (use_maps_backing) { // don't bother with clever "self" stuff in this case
ConstructFilename("/proc/%d/maps_backing", pid, ibuf_, Buffer::kBufSize);
} else if (pid == 0) {
// We have to kludge a bit to deal with the args ConstructFilename
// expects. The 1 is never used -- it's only impt. that it's not 0.
ConstructFilename("/proc/self/maps", 1, ibuf_, Buffer::kBufSize);
} else {
ConstructFilename("/proc/%d/maps", pid, ibuf_, Buffer::kBufSize);
}
// No error logging since this can be called from the crash dump
// handler at awkward moments. Users should call Valid() before
// using.
NO_INTR(fd_ = open(ibuf_, O_RDONLY));
#elif defined(__FreeBSD__)
// We don't support maps_backing on freebsd
if (pid == 0) {
ConstructFilename("/proc/curproc/map", 1, ibuf_, Buffer::kBufSize);
} else {
ConstructFilename("/proc/%d/map", pid, ibuf_, Buffer::kBufSize);
}
NO_INTR(fd_ = open(ibuf_, O_RDONLY));
#elif defined(__sun__)
if (pid == 0) {
ConstructFilename("/proc/self/map", 1, ibuf_, Buffer::kBufSize);
} else {
ConstructFilename("/proc/%d/map", pid, ibuf_, Buffer::kBufSize);
}
NO_INTR(fd_ = open(ibuf_, O_RDONLY));
#elif defined(__MACH__)
current_image_ = _dyld_image_count(); // count down from the top
current_load_cmd_ = -1;
#elif defined(PLATFORM_WINDOWS)
snapshot_ = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE |
TH32CS_SNAPMODULE32,
GetCurrentProcessId());
memset(&module_, 0, sizeof(module_));
#else
fd_ = -1; // so Valid() is always false
#endif
}
ProcMapsIterator::~ProcMapsIterator() {
#if defined(PLATFORM_WINDOWS)
if (snapshot_ != INVALID_HANDLE_VALUE) CloseHandle(snapshot_);
#elif defined(__MACH__)
// no cleanup necessary!
#else
if (fd_ >= 0) NO_INTR(close(fd_));
#endif
delete dynamic_buffer_;
}
bool ProcMapsIterator::Valid() const {
#if defined(PLATFORM_WINDOWS)
return snapshot_ != INVALID_HANDLE_VALUE;
#elif defined(__MACH__)
return 1;
#else
return fd_ != -1;
#endif
}
bool ProcMapsIterator::Next(uint64 *start, uint64 *end, char **flags,
uint64 *offset, int64 *inode, char **filename) {
return NextExt(start, end, flags, offset, inode, filename, NULL, NULL,
NULL, NULL, NULL);
}
// This has too many arguments. It should really be building
// a map object and returning it. The problem is that this is called
// when the memory allocator state is undefined, hence the arguments.
bool ProcMapsIterator::NextExt(uint64 *start, uint64 *end, char **flags,
uint64 *offset, int64 *inode, char **filename,
uint64 *file_mapping, uint64 *file_pages,
uint64 *anon_mapping, uint64 *anon_pages,
dev_t *dev) {
#if defined(__linux__) || defined(__FreeBSD__) || defined(__CYGWIN__) || defined(__CYGWIN32__)
do {
// Advance to the start of the next line
stext_ = nextline_;
// See if we have a complete line in the buffer already
nextline_ = static_cast<char *>(memchr (stext_, '\n', etext_ - stext_));
if (!nextline_) {
// Shift/fill the buffer so we do have a line
int count = etext_ - stext_;
// Move the current text to the start of the buffer
memmove(ibuf_, stext_, count);
stext_ = ibuf_;
etext_ = ibuf_ + count;
int nread = 0; // fill up buffer with text
while (etext_ < ebuf_) {
NO_INTR(nread = read(fd_, etext_, ebuf_ - etext_));
if (nread > 0)
etext_ += nread;
else
break;
}
// Zero out remaining characters in buffer at EOF to avoid returning
// garbage from subsequent calls.
if (etext_ != ebuf_ && nread == 0) {
memset(etext_, 0, ebuf_ - etext_);
}
*etext_ = '\n'; // sentinel; safe because ibuf extends 1 char beyond ebuf
nextline_ = static_cast<char *>(memchr (stext_, '\n', etext_ + 1 - stext_));
}
*nextline_ = 0; // turn newline into nul
nextline_ += ((nextline_ < etext_)? 1 : 0); // skip nul if not end of text
// stext_ now points at a nul-terminated line
uint64 tmpstart, tmpend, tmpoffset;
int64 tmpinode;
int major, minor;
unsigned filename_offset = 0;
#if defined(__linux__)
// for now, assume all linuxes have the same format
if (!ParseProcMapsLine(
stext_,
start ? start : &tmpstart,
end ? end : &tmpend,
flags_,
offset ? offset : &tmpoffset,
&major, &minor,
inode ? inode : &tmpinode, &filename_offset)) continue;
#elif defined(__CYGWIN__) || defined(__CYGWIN32__)
// cygwin is like linux, except the third field is the "entry point"
// rather than the offset (see format_process_maps at
// http://cygwin.com/cgi-bin/cvsweb.cgi/src/winsup/cygwin/fhandler_process.cc?rev=1.89&content-type=text/x-cvsweb-markup&cvsroot=src
// Offset is always be 0 on cygwin: cygwin implements an mmap
// by loading the whole file and then calling NtMapViewOfSection.
// Cygwin also seems to set its flags kinda randomly; use windows default.
char tmpflags[5];
if (offset)
*offset = 0;
strcpy(flags_, "r-xp");
if (sscanf(stext_, "%llx-%llx %4s %llx %x:%x %lld %n",
start ? start : &tmpstart,
end ? end : &tmpend,
tmpflags,
&tmpoffset,
&major, &minor,
inode ? inode : &tmpinode, &filename_offset) != 7) continue;
#elif defined(__FreeBSD__)
// For the format, see http://www.freebsd.org/cgi/cvsweb.cgi/src/sys/fs/procfs/procfs_map.c?rev=1.31&content-type=text/x-cvsweb-markup
tmpstart = tmpend = tmpoffset = 0;
tmpinode = 0;
major = minor = 0; // can't get this info in freebsd
if (inode)
*inode = 0; // nor this
if (offset)
*offset = 0; // seems like this should be in there, but maybe not
// start end resident privateresident obj(?) prot refcnt shadowcnt
// flags copy_on_write needs_copy type filename:
// 0x8048000 0x804a000 2 0 0xc104ce70 r-x 1 0 0x0 COW NC vnode /bin/cat
if (sscanf(stext_, "0x%" SCNx64 " 0x%" SCNx64 " %*d %*d %*p %3s %*d %*d 0x%*x %*s %*s %*s %n",
start ? start : &tmpstart,
end ? end : &tmpend,
flags_,
&filename_offset) != 3) continue;
#endif
// Depending on the Linux kernel being used, there may or may not be a space
// after the inode if there is no filename. sscanf will in such situations
// nondeterministically either fill in filename_offset or not (the results
// differ on multiple calls in the same run even with identical arguments).
// We don't want to wander off somewhere beyond the end of the string.
size_t stext_length = strlen(stext_);
if (filename_offset == 0 || filename_offset > stext_length)
filename_offset = stext_length;
// We found an entry
if (flags) *flags = flags_;
if (filename) *filename = stext_ + filename_offset;
if (dev) *dev = minor | (major << 8);
if (using_maps_backing_) {
// Extract and parse physical page backing info.
char *backing_ptr = stext_ + filename_offset +
strlen(stext_+filename_offset);
// find the second '('
int paren_count = 0;
while (--backing_ptr > stext_) {
if (*backing_ptr == '(') {
++paren_count;
if (paren_count >= 2) {
uint64 tmp_file_mapping;
uint64 tmp_file_pages;
uint64 tmp_anon_mapping;
uint64 tmp_anon_pages;
sscanf(backing_ptr+1, "F %" SCNx64 " %" SCNd64 ") (A %" SCNx64 " %" SCNd64 ")",
file_mapping ? file_mapping : &tmp_file_mapping,
file_pages ? file_pages : &tmp_file_pages,
anon_mapping ? anon_mapping : &tmp_anon_mapping,
anon_pages ? anon_pages : &tmp_anon_pages);
// null terminate the file name (there is a space
// before the first (.
backing_ptr[-1] = 0;
break;
}
}
}
}
return true;
} while (etext_ > ibuf_);
#elif defined(__sun__)
// This is based on MA_READ == 4, MA_WRITE == 2, MA_EXEC == 1
static char kPerms[8][4] = { "---", "--x", "-w-", "-wx",
"r--", "r-x", "rw-", "rwx" };
COMPILE_ASSERT(MA_READ == 4, solaris_ma_read_must_equal_4);
COMPILE_ASSERT(MA_WRITE == 2, solaris_ma_write_must_equal_2);
COMPILE_ASSERT(MA_EXEC == 1, solaris_ma_exec_must_equal_1);
Buffer object_path;
int nread = 0; // fill up buffer with text
NO_INTR(nread = read(fd_, ibuf_, sizeof(prmap_t)));
if (nread == sizeof(prmap_t)) {
long inode_from_mapname = 0;
prmap_t* mapinfo = reinterpret_cast<prmap_t*>(ibuf_);
// Best-effort attempt to get the inode from the filename. I think the
// two middle ints are major and minor device numbers, but I'm not sure.
sscanf(mapinfo->pr_mapname, "ufs.%*d.%*d.%ld", &inode_from_mapname);
if (pid_ == 0) {
CHECK_LT(snprintf(object_path.buf_, Buffer::kBufSize,
"/proc/self/path/%s", mapinfo->pr_mapname),
Buffer::kBufSize);
} else {
CHECK_LT(snprintf(object_path.buf_, Buffer::kBufSize,
"/proc/%d/path/%s",
static_cast<int>(pid_), mapinfo->pr_mapname),
Buffer::kBufSize);
}
ssize_t len = readlink(object_path.buf_, current_filename_, PATH_MAX);
CHECK_LT(len, PATH_MAX);
if (len < 0)
len = 0;
current_filename_[len] = '\0';
if (start) *start = mapinfo->pr_vaddr;
if (end) *end = mapinfo->pr_vaddr + mapinfo->pr_size;
if (flags) *flags = kPerms[mapinfo->pr_mflags & 7];
if (offset) *offset = mapinfo->pr_offset;
if (inode) *inode = inode_from_mapname;
if (filename) *filename = current_filename_;
if (file_mapping) *file_mapping = 0;
if (file_pages) *file_pages = 0;
if (anon_mapping) *anon_mapping = 0;
if (anon_pages) *anon_pages = 0;
if (dev) *dev = 0;
return true;
}
#elif defined(__MACH__)
// We return a separate entry for each segment in the DLL. (TODO(csilvers):
// can we do better?) A DLL ("image") has load-commands, some of which
// talk about segment boundaries.
// cf image_for_address from http://svn.digium.com/view/asterisk/team/oej/minivoicemail/dlfcn.c?revision=53912
for (; current_image_ >= 0; current_image_--) {
const mach_header* hdr = _dyld_get_image_header(current_image_);
if (!hdr) continue;
if (current_load_cmd_ < 0) // set up for this image
current_load_cmd_ = hdr->ncmds; // again, go from the top down
// We start with the next load command (we've already looked at this one).
for (current_load_cmd_--; current_load_cmd_ >= 0; current_load_cmd_--) {
#ifdef MH_MAGIC_64
if (NextExtMachHelper<MH_MAGIC_64, LC_SEGMENT_64,
struct mach_header_64, struct segment_command_64>(
hdr, current_image_, current_load_cmd_,
start, end, flags, offset, inode, filename,
file_mapping, file_pages, anon_mapping,
anon_pages, dev)) {
return true;
}
#endif
if (NextExtMachHelper<MH_MAGIC, LC_SEGMENT,
struct mach_header, struct segment_command>(
hdr, current_image_, current_load_cmd_,
start, end, flags, offset, inode, filename,
file_mapping, file_pages, anon_mapping,
anon_pages, dev)) {
return true;
}
}
// If we get here, no more load_cmd's in this image talk about
// segments. Go on to the next image.
}
#elif defined(PLATFORM_WINDOWS)
static char kDefaultPerms[5] = "r-xp";
BOOL ok;
if (module_.dwSize == 0) { // only possible before first call
module_.dwSize = sizeof(module_);
ok = Module32First(snapshot_, &module_);
} else {
ok = Module32Next(snapshot_, &module_);
}
if (ok) {
uint64 base_addr = reinterpret_cast<DWORD_PTR>(module_.modBaseAddr);
if (start) *start = base_addr;
if (end) *end = base_addr + module_.modBaseSize;
if (flags) *flags = kDefaultPerms;
if (offset) *offset = 0;
if (inode) *inode = 0;
if (filename) *filename = module_.szExePath;
if (file_mapping) *file_mapping = 0;
if (file_pages) *file_pages = 0;
if (anon_mapping) *anon_mapping = 0;
if (anon_pages) *anon_pages = 0;
if (dev) *dev = 0;
return true;
}
#endif
// We didn't find anything
return false;
}
int ProcMapsIterator::FormatLine(char* buffer, int bufsize,
uint64 start, uint64 end, const char *flags,
uint64 offset, int64 inode,
const char *filename, dev_t dev) {
// We assume 'flags' looks like 'rwxp' or 'rwx'.
char r = (flags && flags[0] == 'r') ? 'r' : '-';
char w = (flags && flags[0] && flags[1] == 'w') ? 'w' : '-';
char x = (flags && flags[0] && flags[1] && flags[2] == 'x') ? 'x' : '-';
// p always seems set on linux, so we set the default to 'p', not '-'
char p = (flags && flags[0] && flags[1] && flags[2] && flags[3] != 'p')
? '-' : 'p';
const int rc = snprintf(buffer, bufsize,
"%08" PRIx64 "-%08" PRIx64 " %c%c%c%c %08" PRIx64 " %02x:%02x %-11" PRId64 " %s\n",
start, end, r,w,x,p, offset,
static_cast<int>(dev/256), static_cast<int>(dev%256),
inode, filename);
return (rc < 0 || rc >= bufsize) ? 0 : rc;
}
namespace tcmalloc {
// Helper to add the list of mapped shared libraries to a profile.
// Fill formatted "/proc/self/maps" contents into buffer 'buf' of size 'size'
// and return the actual size occupied in 'buf'. We fill wrote_all to true
// if we successfully wrote all proc lines to buf, false else.
// We do not provision for 0-terminating 'buf'.
int FillProcSelfMaps(char buf[], int size, bool* wrote_all) {
ProcMapsIterator::Buffer iterbuf;
ProcMapsIterator it(0, &iterbuf); // 0 means "current pid"
uint64 start, end, offset;
int64 inode;
char *flags, *filename;
int bytes_written = 0;
*wrote_all = true;
while (it.Next(&start, &end, &flags, &offset, &inode, &filename)) {
const int line_length = it.FormatLine(buf + bytes_written,
size - bytes_written,
start, end, flags, offset,
inode, filename, 0);
if (line_length == 0)
*wrote_all = false; // failed to write this line out
else
bytes_written += line_length;
}
return bytes_written;
}
// Dump the same data as FillProcSelfMaps reads to fd.
// It seems easier to repeat parts of FillProcSelfMaps here than to
// reuse it via a call.
void DumpProcSelfMaps(RawFD 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);
RawWrite(fd, linebuf.buf_, written);
}
}
} // namespace tcmalloc

View file

@ -0,0 +1,230 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2006, 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.
// All functions here are thread-hostile due to file caching unless
// commented otherwise.
#ifndef _SYSINFO_H_
#define _SYSINFO_H_
#include <config.h>
#include <time.h>
#if (defined(_WIN32) || defined(__MINGW32__)) && (!defined(__CYGWIN__) && !defined(__CYGWIN32__))
#include <windows.h> // for DWORD
#include <tlhelp32.h> // for CreateToolhelp32Snapshot
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h> // for pid_t
#endif
#include <stddef.h> // for size_t
#include <limits.h> // for PATH_MAX
#include "base/basictypes.h"
#include "base/logging.h" // for RawFD
// This getenv function is safe to call before the C runtime is initialized.
// On Windows, it utilizes GetEnvironmentVariable() and on unix it uses
// /proc/self/environ instead calling getenv(). It's intended to be used in
// routines that run before main(), when the state required for getenv() may
// not be set up yet. In particular, errno isn't set up until relatively late
// (after the pthreads library has a chance to make it threadsafe), and
// getenv() doesn't work until then.
// On some platforms, this call will utilize the same, static buffer for
// repeated GetenvBeforeMain() calls. Callers should not expect pointers from
// this routine to be long lived.
// Note that on unix, /proc only has the environment at the time the
// application was started, so this routine ignores setenv() calls/etc. Also
// note it only reads the first 16K of the environment.
extern const char* GetenvBeforeMain(const char* name);
// This takes as an argument an environment-variable name (like
// CPUPROFILE) whose value is supposed to be a file-path, and sets
// path to that path, and returns true. Non-trivial for surprising
// reasons, as documented in sysinfo.cc. path must have space PATH_MAX.
extern bool GetUniquePathFromEnv(const char* env_name, char* path);
extern int GetSystemCPUsCount();
// Return true if we're running POSIX (e.g., NPTL on Linux) threads,
// as opposed to a non-POSIX thread library. The thing that we care
// about is whether a thread's pid is the same as the thread that
// spawned it. If so, this function returns true.
// Thread-safe.
// Note: We consider false negatives to be OK.
bool HasPosixThreads();
#ifndef SWIG // SWIG doesn't like struct Buffer and variable arguments.
// A ProcMapsIterator abstracts access to /proc/maps for a given
// process. Needs to be stack-allocatable and avoid using stdio/malloc
// so it can be used in the google stack dumper, heap-profiler, etc.
//
// On Windows and Mac OS X, this iterator iterates *only* over DLLs
// mapped into this process space. For Linux, FreeBSD, and Solaris,
// it iterates over *all* mapped memory regions, including anonymous
// mmaps. For other O/Ss, it is unlikely to work at all, and Valid()
// will always return false. Also note: this routine only works on
// FreeBSD if procfs is mounted: make sure this is in your /etc/fstab:
// proc /proc procfs rw 0 0
class ProcMapsIterator {
public:
struct Buffer {
#ifdef __FreeBSD__
// FreeBSD requires us to read all of the maps file at once, so
// we have to make a buffer that's "always" big enough
static const size_t kBufSize = 102400;
#else // a one-line buffer is good enough
static const size_t kBufSize = PATH_MAX + 1024;
#endif
char buf_[kBufSize];
};
// Create a new iterator for the specified pid. pid can be 0 for "self".
explicit ProcMapsIterator(pid_t pid);
// Create an iterator with specified storage (for use in signal
// handler). "buffer" should point to a ProcMapsIterator::Buffer
// buffer can be NULL in which case a bufer will be allocated.
ProcMapsIterator(pid_t pid, Buffer *buffer);
// Iterate through maps_backing instead of maps if use_maps_backing
// is true. Otherwise the same as above. buffer can be NULL and
// it will allocate a buffer itself.
ProcMapsIterator(pid_t pid, Buffer *buffer,
bool use_maps_backing);
// Returns true if the iterator successfully initialized;
bool Valid() const;
// Returns a pointer to the most recently parsed line. Only valid
// after Next() returns true, and until the iterator is destroyed or
// Next() is called again. This may give strange results on non-Linux
// systems. Prefer FormatLine() if that may be a concern.
const char *CurrentLine() const { return stext_; }
// Writes the "canonical" form of the /proc/xxx/maps info for a single
// line to the passed-in buffer. Returns the number of bytes written,
// or 0 if it was not able to write the complete line. (To guarantee
// success, buffer should have size at least Buffer::kBufSize.)
// Takes as arguments values set via a call to Next(). The
// "canonical" form of the line (taken from linux's /proc/xxx/maps):
// <start_addr(hex)>-<end_addr(hex)> <perms(rwxp)> <offset(hex)> +
// <major_dev(hex)>:<minor_dev(hex)> <inode> <filename> Note: the
// eg
// 08048000-0804c000 r-xp 00000000 03:01 3793678 /bin/cat
// If you don't have the dev_t (dev), feel free to pass in 0.
// (Next() doesn't return a dev_t, though NextExt does.)
//
// Note: if filename and flags were obtained via a call to Next(),
// then the output of this function is only valid if Next() returned
// true, and only until the iterator is destroyed or Next() is
// called again. (Since filename, at least, points into CurrentLine.)
static int FormatLine(char* buffer, int bufsize,
uint64 start, uint64 end, const char *flags,
uint64 offset, int64 inode, const char *filename,
dev_t dev);
// Find the next entry in /proc/maps; return true if found or false
// if at the end of the file.
//
// Any of the result pointers can be NULL if you're not interested
// in those values.
//
// If "flags" and "filename" are passed, they end up pointing to
// storage within the ProcMapsIterator that is valid only until the
// iterator is destroyed or Next() is called again. The caller may
// modify the contents of these strings (up as far as the first NUL,
// and only until the subsequent call to Next()) if desired.
// The offsets are all uint64 in order to handle the case of a
// 32-bit process running on a 64-bit kernel
//
// IMPORTANT NOTE: see top-of-class notes for details about what
// mapped regions Next() iterates over, depending on O/S.
// TODO(csilvers): make flags and filename const.
bool Next(uint64 *start, uint64 *end, char **flags,
uint64 *offset, int64 *inode, char **filename);
bool NextExt(uint64 *start, uint64 *end, char **flags,
uint64 *offset, int64 *inode, char **filename,
uint64 *file_mapping, uint64 *file_pages,
uint64 *anon_mapping, uint64 *anon_pages,
dev_t *dev);
~ProcMapsIterator();
private:
void Init(pid_t pid, Buffer *buffer, bool use_maps_backing);
char *ibuf_; // input buffer
char *stext_; // start of text
char *etext_; // end of text
char *nextline_; // start of next line
char *ebuf_; // end of buffer (1 char for a nul)
#if (defined(_WIN32) || defined(__MINGW32__)) && (!defined(__CYGWIN__) && !defined(__CYGWIN32__))
HANDLE snapshot_; // filehandle on dll info
// In a change from the usual W-A pattern, there is no A variant of
// MODULEENTRY32. Tlhelp32.h #defines the W variant, but not the A.
// We want the original A variants, and this #undef is the only
// way I see to get them. Redefining it when we're done prevents us
// from affecting other .cc files.
# ifdef MODULEENTRY32 // Alias of W
# undef MODULEENTRY32
MODULEENTRY32 module_; // info about current dll (and dll iterator)
# define MODULEENTRY32 MODULEENTRY32W
# else // It's the ascii, the one we want.
MODULEENTRY32 module_; // info about current dll (and dll iterator)
# endif
#elif defined(__MACH__)
int current_image_; // dll's are called "images" in macos parlance
int current_load_cmd_; // the segment of this dll we're examining
#elif defined(__sun__) // Solaris
int fd_;
char current_filename_[PATH_MAX];
#else
int fd_; // filehandle on /proc/*/maps
#endif
pid_t pid_;
char flags_[10];
Buffer* dynamic_buffer_; // dynamically-allocated Buffer
bool using_maps_backing_; // true if we are looking at maps_backing instead of maps.
};
#endif /* #ifndef SWIG */
// Helper routines
namespace tcmalloc {
int FillProcSelfMaps(char buf[], int size, bool* wrote_all);
void DumpProcSelfMaps(RawFD fd);
}
#endif /* #ifndef _SYSINFO_H_ */

View file

@ -0,0 +1,133 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2008, 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: Le-Chun Wu
//
// This header file contains the macro definitions for thread safety
// annotations that allow the developers to document the locking policies
// of their multi-threaded code. The annotations can also help program
// analysis tools to identify potential thread safety issues.
//
// The annotations are implemented using clang's "attributes" extension.
// Using the macros defined here instead of the raw clang attributes allows
// for portability and future compatibility.
//
// This functionality is not yet fully implemented in perftools,
// but may be one day.
#ifndef BASE_THREAD_ANNOTATIONS_H_
#define BASE_THREAD_ANNOTATIONS_H_
#if defined(__clang__)
#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
#else
#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
#endif
// Document if a shared variable/field needs to be protected by a lock.
// GUARDED_BY allows the user to specify a particular lock that should be
// held when accessing the annotated variable, while GUARDED_VAR only
// indicates a shared variable should be guarded (by any lock). GUARDED_VAR
// is primarily used when the client cannot express the name of the lock.
#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
#define GUARDED_VAR THREAD_ANNOTATION_ATTRIBUTE__(guarded)
// Document if the memory location pointed to by a pointer should be guarded
// by a lock when dereferencing the pointer. Similar to GUARDED_VAR,
// PT_GUARDED_VAR is primarily used when the client cannot express the name
// of the lock. Note that a pointer variable to a shared memory location
// could itself be a shared variable. For example, if a shared global pointer
// q, which is guarded by mu1, points to a shared memory location that is
// guarded by mu2, q should be annotated as follows:
// int *q GUARDED_BY(mu1) PT_GUARDED_BY(mu2);
#define PT_GUARDED_BY(x) \
THREAD_ANNOTATION_ATTRIBUTE__(point_to_guarded_by(x))
#define PT_GUARDED_VAR \
THREAD_ANNOTATION_ATTRIBUTE__(point_to_guarded)
// Document the acquisition order between locks that can be held
// simultaneously by a thread. For any two locks that need to be annotated
// to establish an acquisition order, only one of them needs the annotation.
// (i.e. You don't have to annotate both locks with both ACQUIRED_AFTER
// and ACQUIRED_BEFORE.)
#define ACQUIRED_AFTER(x) \
THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(x))
#define ACQUIRED_BEFORE(x) \
THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(x))
// The following three annotations document the lock requirements for
// functions/methods.
// Document if a function expects certain locks to be held before it is called
#define EXCLUSIVE_LOCKS_REQUIRED(x) \
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(x))
#define SHARED_LOCKS_REQUIRED(x) \
THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(x))
// Document the locks acquired in the body of the function. These locks
// cannot be held when calling this function (as google3's Mutex locks are
// non-reentrant).
#define LOCKS_EXCLUDED(x) \
THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(x))
// Document the lock the annotated function returns without acquiring it.
#define LOCK_RETURNED(x) THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
// Document if a class/type is a lockable type (such as the Mutex class).
#define LOCKABLE THREAD_ANNOTATION_ATTRIBUTE__(lockable)
// Document if a class is a scoped lockable type (such as the MutexLock class).
#define SCOPED_LOCKABLE THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
// The following annotations specify lock and unlock primitives.
#define EXCLUSIVE_LOCK_FUNCTION(x) \
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(x))
#define SHARED_LOCK_FUNCTION(x) \
THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(x))
#define EXCLUSIVE_TRYLOCK_FUNCTION(x) \
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(x))
#define SHARED_TRYLOCK_FUNCTION(x) \
THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(x))
#define UNLOCK_FUNCTION(x) \
THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(x))
// An escape hatch for thread safety analysis to ignore the annotated function.
#define NO_THREAD_SAFETY_ANALYSIS \
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
#endif // BASE_THREAD_ANNOTATIONS_H_

View file

@ -0,0 +1,84 @@
/* -*- Mode: c; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/* Copyright (c) 2005-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: Markus Gutschke
*/
#include "config.h"
#include "base/thread_lister.h"
#include <stdio.h> /* needed for NULL on some powerpc platforms (?!) */
#include <sys/types.h>
#include <unistd.h> /* for getpid */
#ifdef HAVE_SYS_PRCTL
# include <sys/prctl.h>
#endif
#include "base/linuxthreads.h"
/* Include other thread listers here that define THREADS macro
* only when they can provide a good implementation.
*/
#ifndef THREADS
/* Default trivial thread lister for single-threaded applications,
* or if the multi-threading code has not been ported, yet.
*/
int TCMalloc_ListAllProcessThreads(void *parameter,
ListAllProcessThreadsCallBack callback, ...) {
int rc;
va_list ap;
pid_t pid;
#ifdef HAVE_SYS_PRCTL
int dumpable = prctl(PR_GET_DUMPABLE, 0);
if (!dumpable)
prctl(PR_SET_DUMPABLE, 1);
#endif
va_start(ap, callback);
pid = getpid();
rc = callback(parameter, 1, &pid, ap);
va_end(ap);
#ifdef HAVE_SYS_PRCTL
if (!dumpable)
prctl(PR_SET_DUMPABLE, 0);
#endif
return rc;
}
int TCMalloc_ResumeAllProcessThreads(int num_threads, pid_t *thread_pids) {
return 1;
}
#endif /* ifndef THREADS */

View file

@ -0,0 +1,83 @@
/* -*- Mode: c; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/* Copyright (c) 2005-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: Markus Gutschke
*/
#ifndef _THREAD_LISTER_H
#define _THREAD_LISTER_H
#include <stdarg.h>
#include <sys/types.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef int (*ListAllProcessThreadsCallBack)(void *parameter,
int num_threads,
pid_t *thread_pids,
va_list ap);
/* This function gets the list of all linux threads of the current process
* passes them to the 'callback' along with the 'parameter' pointer; at the
* call back call time all the threads are paused via
* PTRACE_ATTACH.
* The callback is executed from a separate thread which shares only the
* address space, the filesystem, and the filehandles with the caller. Most
* notably, it does not share the same pid and ppid; and if it terminates,
* the rest of the application is still there. 'callback' is supposed to do
* or arrange for TCMalloc_ResumeAllProcessThreads. This happens automatically, if
* the thread raises a synchronous signal (e.g. SIGSEGV); asynchronous
* signals are blocked. If the 'callback' decides to unblock them, it must
* ensure that they cannot terminate the application, or that
* TCMalloc_ResumeAllProcessThreads will get called.
* It is an error for the 'callback' to make any library calls that could
* acquire locks. Most notably, this means that most system calls have to
* avoid going through libc. Also, this means that it is not legal to call
* exit() or abort().
* We return -1 on error and the return value of 'callback' on success.
*/
int TCMalloc_ListAllProcessThreads(void *parameter,
ListAllProcessThreadsCallBack callback, ...);
/* This function resumes the list of all linux threads that
* TCMalloc_ListAllProcessThreads pauses before giving to its
* callback. The function returns non-zero if at least one thread was
* suspended and has now been resumed.
*/
int TCMalloc_ResumeAllProcessThreads(int num_threads, pid_t *thread_pids);
#ifdef __cplusplus
}
#endif
#endif /* _THREAD_LISTER_H */

View file

@ -0,0 +1,143 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2008, 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: Paul Pluzhnikov
//
// Allow dynamic symbol lookup in the kernel VDSO page.
//
// VDSOSupport -- a class representing kernel VDSO (if present).
//
#include "base/vdso_support.h"
#ifdef HAVE_VDSO_SUPPORT // defined in vdso_support.h
#include <fcntl.h>
#include <stddef.h> // for ptrdiff_t
#include "base/atomicops.h" // for MemoryBarrier
#include "base/logging.h"
#include "base/dynamic_annotations.h"
#include "base/basictypes.h" // for COMPILE_ASSERT
using base::subtle::MemoryBarrier;
#ifndef AT_SYSINFO_EHDR
#define AT_SYSINFO_EHDR 33
#endif
namespace base {
const void *VDSOSupport::vdso_base_ = ElfMemImage::kInvalidBase;
VDSOSupport::VDSOSupport()
// If vdso_base_ is still set to kInvalidBase, we got here
// before VDSOSupport::Init has been called. Call it now.
: image_(vdso_base_ == ElfMemImage::kInvalidBase ? Init() : vdso_base_) {
}
// NOTE: we can't use GoogleOnceInit() below, because we can be
// called by tcmalloc, and none of the *once* stuff may be functional yet.
//
// In addition, we hope that the VDSOSupportHelper constructor
// causes this code to run before there are any threads, and before
// InitGoogle() has executed any chroot or setuid calls.
//
// Finally, even if there is a race here, it is harmless, because
// the operation should be idempotent.
const void *VDSOSupport::Init() {
if (vdso_base_ == ElfMemImage::kInvalidBase) {
// Valgrind zaps AT_SYSINFO_EHDR and friends from the auxv[]
// on stack, and so glibc works as if VDSO was not present.
// But going directly to kernel via /proc/self/auxv below bypasses
// Valgrind zapping. So we check for Valgrind separately.
if (RunningOnValgrind()) {
vdso_base_ = NULL;
return NULL;
}
int fd = open("/proc/self/auxv", O_RDONLY);
if (fd == -1) {
// Kernel too old to have a VDSO.
vdso_base_ = NULL;
return NULL;
}
ElfW(auxv_t) aux;
while (read(fd, &aux, sizeof(aux)) == sizeof(aux)) {
if (aux.a_type == AT_SYSINFO_EHDR) {
COMPILE_ASSERT(sizeof(vdso_base_) == sizeof(aux.a_un.a_val),
unexpected_sizeof_pointer_NE_sizeof_a_val);
vdso_base_ = reinterpret_cast<void *>(aux.a_un.a_val);
break;
}
}
close(fd);
if (vdso_base_ == ElfMemImage::kInvalidBase) {
// Didn't find AT_SYSINFO_EHDR in auxv[].
vdso_base_ = NULL;
}
}
return vdso_base_;
}
const void *VDSOSupport::SetBase(const void *base) {
CHECK(base != ElfMemImage::kInvalidBase);
const void *old_base = vdso_base_;
vdso_base_ = base;
image_.Init(base);
return old_base;
}
bool VDSOSupport::LookupSymbol(const char *name,
const char *version,
int type,
SymbolInfo *info) const {
return image_.LookupSymbol(name, version, type, info);
}
bool VDSOSupport::LookupSymbolByAddress(const void *address,
SymbolInfo *info_out) const {
return image_.LookupSymbolByAddress(address, info_out);
}
// We need to make sure VDSOSupport::Init() is called before
// the main() runs, since it might do something like setuid or
// chroot. If VDSOSupport
// is used in any global constructor, this will happen, since
// VDSOSupport's constructor calls Init. But if not, we need to
// ensure it here, with a global constructor of our own. This
// is an allowed exception to the normal rule against non-trivial
// global constructors.
static class VDSOInitHelper {
public:
VDSOInitHelper() { VDSOSupport::Init(); }
} vdso_init_helper;
}
#endif // HAVE_VDSO_SUPPORT

View file

@ -0,0 +1,137 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2008, 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: Paul Pluzhnikov
//
// Allow dynamic symbol lookup in the kernel VDSO page.
//
// VDSO stands for "Virtual Dynamic Shared Object" -- a page of
// executable code, which looks like a shared library, but doesn't
// necessarily exist anywhere on disk, and which gets mmap()ed into
// every process by kernels which support VDSO, such as 2.6.x for 32-bit
// executables, and 2.6.24 and above for 64-bit executables.
//
// More details could be found here:
// http://www.trilithium.com/johan/2005/08/linux-gate/
//
// VDSOSupport -- a class representing kernel VDSO (if present).
//
// Example usage:
// VDSOSupport vdso;
// VDSOSupport::SymbolInfo info;
// typedef (*FN)(unsigned *, void *, void *);
// FN fn = NULL;
// if (vdso.LookupSymbol("__vdso_getcpu", "LINUX_2.6", STT_FUNC, &info)) {
// fn = reinterpret_cast<FN>(info.address);
// }
#ifndef BASE_VDSO_SUPPORT_H_
#define BASE_VDSO_SUPPORT_H_
#include <config.h>
#include "base/basictypes.h"
#include "base/elf_mem_image.h"
#ifdef HAVE_ELF_MEM_IMAGE
// Enable VDSO support only for the architectures/operating systems that
// support it.
#if defined(__linux__) && (defined(__i386__) || defined(__PPC__))
#define HAVE_VDSO_SUPPORT 1
#endif
#include <stdlib.h> // for NULL
namespace base {
// NOTE: this class may be used from within tcmalloc, and can not
// use any memory allocation routines.
class VDSOSupport {
public:
VDSOSupport();
typedef ElfMemImage::SymbolInfo SymbolInfo;
typedef ElfMemImage::SymbolIterator SymbolIterator;
// Answers whether we have a vdso at all.
bool IsPresent() const { return image_.IsPresent(); }
// Allow to iterate over all VDSO symbols.
SymbolIterator begin() const { return image_.begin(); }
SymbolIterator end() const { return image_.end(); }
// Look up versioned dynamic symbol in the kernel VDSO.
// Returns false if VDSO is not present, or doesn't contain given
// symbol/version/type combination.
// If info_out != NULL, additional details are filled in.
bool LookupSymbol(const char *name, const char *version,
int symbol_type, SymbolInfo *info_out) const;
// Find info about symbol (if any) which overlaps given address.
// Returns true if symbol was found; false if VDSO isn't present
// or doesn't have a symbol overlapping given address.
// If info_out != NULL, additional details are filled in.
bool LookupSymbolByAddress(const void *address, SymbolInfo *info_out) const;
// Used only for testing. Replace real VDSO base with a mock.
// Returns previous value of vdso_base_. After you are done testing,
// you are expected to call SetBase() with previous value, in order to
// reset state to the way it was.
const void *SetBase(const void *s);
// Computes vdso_base_ and returns it. Should be called as early as
// possible; before any thread creation, chroot or setuid.
static const void *Init();
private:
// image_ represents VDSO ELF image in memory.
// image_.ehdr_ == NULL implies there is no VDSO.
ElfMemImage image_;
// Cached value of auxv AT_SYSINFO_EHDR, computed once.
// This is a tri-state:
// kInvalidBase => value hasn't been determined yet.
// 0 => there is no VDSO.
// else => vma of VDSO Elf{32,64}_Ehdr.
//
// When testing with mock VDSO, low bit is set.
// The low bit is always available because vdso_base_ is
// page-aligned.
static const void *vdso_base_;
DISALLOW_COPY_AND_ASSIGN(VDSOSupport);
};
} // namespace base
#endif // HAVE_ELF_MEM_IMAGE
#endif // BASE_VDSO_SUPPORT_H_

View file

@ -0,0 +1,384 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2008, 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 <opensource@google.com>
#include "config.h"
#include <algorithm>
#include "central_freelist.h"
#include "internal_logging.h" // for ASSERT, MESSAGE
#include "linked_list.h" // for SLL_Next, SLL_Push, etc
#include "page_heap.h" // for PageHeap
#include "static_vars.h" // for Static
using std::min;
using std::max;
namespace tcmalloc {
void CentralFreeList::Init(size_t cl) {
size_class_ = cl;
tcmalloc::DLL_Init(&empty_);
tcmalloc::DLL_Init(&nonempty_);
num_spans_ = 0;
counter_ = 0;
max_cache_size_ = kMaxNumTransferEntries;
#ifdef TCMALLOC_SMALL_BUT_SLOW
// Disable the transfer cache for the small footprint case.
cache_size_ = 0;
#else
cache_size_ = 16;
#endif
if (cl > 0) {
// Limit the maximum size of the cache based on the size class. If this
// is not done, large size class objects will consume a lot of memory if
// they just sit in the transfer cache.
int32_t bytes = Static::sizemap()->ByteSizeForClass(cl);
int32_t objs_to_move = Static::sizemap()->num_objects_to_move(cl);
ASSERT(objs_to_move > 0 && bytes > 0);
// Limit each size class cache to at most 1MB of objects or one entry,
// whichever is greater. Total transfer cache memory used across all
// size classes then can't be greater than approximately
// 1MB * kMaxNumTransferEntries.
// min and max are in parens to avoid macro-expansion on windows.
max_cache_size_ = (min)(max_cache_size_,
(max)(1, (1024 * 1024) / (bytes * objs_to_move)));
cache_size_ = (min)(cache_size_, max_cache_size_);
}
used_slots_ = 0;
ASSERT(cache_size_ <= max_cache_size_);
}
void CentralFreeList::ReleaseListToSpans(void* start) {
while (start) {
void *next = SLL_Next(start);
ReleaseToSpans(start);
start = next;
}
}
// MapObjectToSpan should logically be part of ReleaseToSpans. But
// this triggers an optimization bug in gcc 4.5.0. Moving to a
// separate function, and making sure that function isn't inlined,
// seems to fix the problem. It also should be fixed for gcc 4.5.1.
static
#if __GNUC__ == 4 && __GNUC_MINOR__ == 5 && __GNUC_PATCHLEVEL__ == 0
__attribute__ ((noinline))
#endif
Span* MapObjectToSpan(void* object) {
const PageID p = reinterpret_cast<uintptr_t>(object) >> kPageShift;
Span* span = Static::pageheap()->GetDescriptor(p);
return span;
}
void CentralFreeList::ReleaseToSpans(void* object) {
Span* span = MapObjectToSpan(object);
ASSERT(span != NULL);
ASSERT(span->refcount > 0);
// If span is empty, move it to non-empty list
if (span->objects == NULL) {
tcmalloc::DLL_Remove(span);
tcmalloc::DLL_Prepend(&nonempty_, span);
}
// The following check is expensive, so it is disabled by default
if (false) {
// Check that object does not occur in list
int got = 0;
for (void* p = span->objects; p != NULL; p = *((void**) p)) {
ASSERT(p != object);
got++;
}
ASSERT(got + span->refcount ==
(span->length<<kPageShift) /
Static::sizemap()->ByteSizeForClass(span->sizeclass));
}
counter_++;
span->refcount--;
if (span->refcount == 0) {
counter_ -= ((span->length<<kPageShift) /
Static::sizemap()->ByteSizeForClass(span->sizeclass));
tcmalloc::DLL_Remove(span);
--num_spans_;
// Release central list lock while operating on pageheap
lock_.Unlock();
{
SpinLockHolder h(Static::pageheap_lock());
Static::pageheap()->Delete(span);
}
lock_.Lock();
} else {
*(reinterpret_cast<void**>(object)) = span->objects;
span->objects = object;
}
}
bool CentralFreeList::EvictRandomSizeClass(
int locked_size_class, bool force) {
static int race_counter = 0;
int t = race_counter++; // Updated without a lock, but who cares.
if (t >= Static::num_size_classes()) {
while (t >= Static::num_size_classes()) {
t -= Static::num_size_classes();
}
race_counter = t;
}
ASSERT(t >= 0);
ASSERT(t < Static::num_size_classes());
if (t == locked_size_class) return false;
return Static::central_cache()[t].ShrinkCache(locked_size_class, force);
}
bool CentralFreeList::MakeCacheSpace() {
// Is there room in the cache?
if (used_slots_ < cache_size_) return true;
// Check if we can expand this cache?
if (cache_size_ == max_cache_size_) return false;
// Ok, we'll try to grab an entry from some other size class.
if (EvictRandomSizeClass(size_class_, false) ||
EvictRandomSizeClass(size_class_, true)) {
// Succeeded in evicting, we're going to make our cache larger.
// However, we may have dropped and re-acquired the lock in
// EvictRandomSizeClass (via ShrinkCache and the LockInverter), so the
// cache_size may have changed. Therefore, check and verify that it is
// still OK to increase the cache_size.
if (cache_size_ < max_cache_size_) {
cache_size_++;
return true;
}
}
return false;
}
namespace {
class LockInverter {
private:
SpinLock *held_, *temp_;
public:
inline explicit LockInverter(SpinLock* held, SpinLock *temp)
: held_(held), temp_(temp) { held_->Unlock(); temp_->Lock(); }
inline ~LockInverter() { temp_->Unlock(); held_->Lock(); }
};
}
// This function is marked as NO_THREAD_SAFETY_ANALYSIS because it uses
// LockInverter to release one lock and acquire another in scoped-lock
// style, which our current annotation/analysis does not support.
bool CentralFreeList::ShrinkCache(int locked_size_class, bool force)
NO_THREAD_SAFETY_ANALYSIS {
// Start with a quick check without taking a lock.
if (cache_size_ == 0) return false;
// We don't evict from a full cache unless we are 'forcing'.
if (force == false && used_slots_ == cache_size_) return false;
// Grab lock, but first release the other lock held by this thread. We use
// the lock inverter to ensure that we never hold two size class locks
// concurrently. That can create a deadlock because there is no well
// defined nesting order.
LockInverter li(&Static::central_cache()[locked_size_class].lock_, &lock_);
ASSERT(used_slots_ <= cache_size_);
ASSERT(0 <= cache_size_);
if (cache_size_ == 0) return false;
if (used_slots_ == cache_size_) {
if (force == false) return false;
// ReleaseListToSpans releases the lock, so we have to make all the
// updates to the central list before calling it.
cache_size_--;
used_slots_--;
ReleaseListToSpans(tc_slots_[used_slots_].head);
return true;
}
cache_size_--;
return true;
}
void CentralFreeList::InsertRange(void *start, void *end, int N) {
SpinLockHolder h(&lock_);
if (N == Static::sizemap()->num_objects_to_move(size_class_) &&
MakeCacheSpace()) {
int slot = used_slots_++;
ASSERT(slot >=0);
ASSERT(slot < max_cache_size_);
TCEntry *entry = &tc_slots_[slot];
entry->head = start;
entry->tail = end;
return;
}
ReleaseListToSpans(start);
}
int CentralFreeList::RemoveRange(void **start, void **end, int N) {
ASSERT(N > 0);
lock_.Lock();
if (N == Static::sizemap()->num_objects_to_move(size_class_) &&
used_slots_ > 0) {
int slot = --used_slots_;
ASSERT(slot >= 0);
TCEntry *entry = &tc_slots_[slot];
*start = entry->head;
*end = entry->tail;
lock_.Unlock();
return N;
}
int result = 0;
*start = NULL;
*end = NULL;
// TODO: Prefetch multiple TCEntries?
result = FetchFromOneSpansSafe(N, start, end);
if (result != 0) {
while (result < N) {
int n;
void* head = NULL;
void* tail = NULL;
n = FetchFromOneSpans(N - result, &head, &tail);
if (!n) break;
result += n;
SLL_PushRange(start, head, tail);
}
}
lock_.Unlock();
return result;
}
int CentralFreeList::FetchFromOneSpansSafe(int N, void **start, void **end) {
int result = FetchFromOneSpans(N, start, end);
if (!result) {
Populate();
result = FetchFromOneSpans(N, start, end);
}
return result;
}
int CentralFreeList::FetchFromOneSpans(int N, void **start, void **end) {
if (tcmalloc::DLL_IsEmpty(&nonempty_)) return 0;
Span* span = nonempty_.next;
ASSERT(span->objects != NULL);
int result = 0;
void *prev, *curr;
curr = span->objects;
do {
prev = curr;
curr = *(reinterpret_cast<void**>(curr));
} while (++result < N && curr != NULL);
if (curr == NULL) {
// Move to empty list
tcmalloc::DLL_Remove(span);
tcmalloc::DLL_Prepend(&empty_, span);
}
*start = span->objects;
*end = prev;
span->objects = curr;
SLL_SetNext(*end, NULL);
span->refcount += result;
counter_ -= result;
return result;
}
// Fetch memory from the system and add to the central cache freelist.
void CentralFreeList::Populate() {
// Release central list lock while operating on pageheap
lock_.Unlock();
const size_t npages = Static::sizemap()->class_to_pages(size_class_);
Span* span;
{
SpinLockHolder h(Static::pageheap_lock());
span = Static::pageheap()->New(npages);
if (span) Static::pageheap()->RegisterSizeClass(span, size_class_);
}
if (span == NULL) {
Log(kLog, __FILE__, __LINE__,
"tcmalloc: allocation failed", npages << kPageShift);
lock_.Lock();
return;
}
ASSERT(span->length == npages);
// Cache sizeclass info eagerly. Locking is not necessary.
// (Instead of being eager, we could just replace any stale info
// about this span, but that seems to be no better in practice.)
for (int i = 0; i < npages; i++) {
Static::pageheap()->SetCachedSizeClass(span->start + i, size_class_);
}
// Split the block into pieces and add to the free-list
// TODO: coloring of objects to avoid cache conflicts?
void** tail = &span->objects;
char* ptr = reinterpret_cast<char*>(span->start << kPageShift);
char* limit = ptr + (npages << kPageShift);
const size_t size = Static::sizemap()->ByteSizeForClass(size_class_);
int num = 0;
while (ptr + size <= limit) {
*tail = ptr;
tail = reinterpret_cast<void**>(ptr);
ptr += size;
num++;
}
ASSERT(ptr <= limit);
*tail = NULL;
span->refcount = 0; // No sub-object in use yet
// Add span to list of non-empty spans
lock_.Lock();
tcmalloc::DLL_Prepend(&nonempty_, span);
++num_spans_;
counter_ += num;
}
int CentralFreeList::tc_length() {
SpinLockHolder h(&lock_);
return used_slots_ * Static::sizemap()->num_objects_to_move(size_class_);
}
size_t CentralFreeList::OverheadBytes() {
SpinLockHolder h(&lock_);
if (size_class_ == 0) { // 0 holds the 0-sized allocations
return 0;
}
const size_t pages_per_span = Static::sizemap()->class_to_pages(size_class_);
const size_t object_size = Static::sizemap()->class_to_size(size_class_);
ASSERT(object_size > 0);
const size_t overhead_per_span = (pages_per_span * kPageSize) % object_size;
return num_spans_ * overhead_per_span;
}
} // namespace tcmalloc

View file

@ -0,0 +1,211 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2008, 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 <opensource@google.com>
#ifndef TCMALLOC_CENTRAL_FREELIST_H_
#define TCMALLOC_CENTRAL_FREELIST_H_
#include "config.h"
#include <stddef.h> // for size_t
#ifdef HAVE_STDINT_H
#include <stdint.h> // for int32_t
#endif
#include "base/spinlock.h"
#include "base/thread_annotations.h"
#include "common.h"
#include "span.h"
namespace tcmalloc {
// Data kept per size-class in central cache.
class CentralFreeList {
public:
// A CentralFreeList may be used before its constructor runs.
// So we prevent lock_'s constructor from doing anything to the
// lock_ state.
CentralFreeList() : lock_(base::LINKER_INITIALIZED) { }
void Init(size_t cl);
// These methods all do internal locking.
// Insert the specified range into the central freelist. N is the number of
// elements in the range. RemoveRange() is the opposite operation.
void InsertRange(void *start, void *end, int N);
// Returns the actual number of fetched elements and sets *start and *end.
int RemoveRange(void **start, void **end, int N);
// Returns the number of free objects in cache.
int length() {
SpinLockHolder h(&lock_);
return counter_;
}
// Returns the number of free objects in the transfer cache.
int tc_length();
// Returns the memory overhead (internal fragmentation) attributable
// to the freelist. This is memory lost when the size of elements
// in a freelist doesn't exactly divide the page-size (an 8192-byte
// page full of 5-byte objects would have 2 bytes memory overhead).
size_t OverheadBytes();
// Lock/Unlock the internal SpinLock. Used on the pthread_atfork call
// to set the lock in a consistent state before the fork.
void Lock() EXCLUSIVE_LOCK_FUNCTION(lock_) {
lock_.Lock();
}
void Unlock() UNLOCK_FUNCTION(lock_) {
lock_.Unlock();
}
private:
// TransferCache is used to cache transfers of
// sizemap.num_objects_to_move(size_class) back and forth between
// thread caches and the central cache for a given size class.
struct TCEntry {
void *head; // Head of chain of objects.
void *tail; // Tail of chain of objects.
};
// A central cache freelist can have anywhere from 0 to kMaxNumTransferEntries
// slots to put link list chains into.
#ifdef TCMALLOC_SMALL_BUT_SLOW
// For the small memory model, the transfer cache is not used.
static const int kMaxNumTransferEntries = 0;
#else
// Starting point for the the maximum number of entries in the transfer cache.
// This actual maximum for a given size class may be lower than this
// maximum value.
static const int kMaxNumTransferEntries = 64;
#endif
// REQUIRES: lock_ is held
// Remove object from cache and return.
// Return NULL if no free entries in cache.
int FetchFromOneSpans(int N, void **start, void **end) EXCLUSIVE_LOCKS_REQUIRED(lock_);
// REQUIRES: lock_ is held
// Remove object from cache and return. Fetches
// from pageheap if cache is empty. Only returns
// NULL on allocation failure.
int FetchFromOneSpansSafe(int N, void **start, void **end) EXCLUSIVE_LOCKS_REQUIRED(lock_);
// REQUIRES: lock_ is held
// Release a linked list of objects to spans.
// May temporarily release lock_.
void ReleaseListToSpans(void *start) EXCLUSIVE_LOCKS_REQUIRED(lock_);
// REQUIRES: lock_ is held
// Release an object to spans.
// May temporarily release lock_.
void ReleaseToSpans(void* object) EXCLUSIVE_LOCKS_REQUIRED(lock_);
// REQUIRES: lock_ is held
// Populate cache by fetching from the page heap.
// May temporarily release lock_.
void Populate() EXCLUSIVE_LOCKS_REQUIRED(lock_);
// REQUIRES: lock is held.
// Tries to make room for a TCEntry. If the cache is full it will try to
// expand it at the cost of some other cache size. Return false if there is
// no space.
bool MakeCacheSpace() EXCLUSIVE_LOCKS_REQUIRED(lock_);
// REQUIRES: lock_ for locked_size_class is held.
// Picks a "random" size class to steal TCEntry slot from. In reality it
// just iterates over the sizeclasses but does so without taking a lock.
// Returns true on success.
// May temporarily lock a "random" size class.
static bool EvictRandomSizeClass(int locked_size_class, bool force);
// REQUIRES: lock_ is *not* held.
// Tries to shrink the Cache. If force is true it will relase objects to
// spans if it allows it to shrink the cache. Return false if it failed to
// shrink the cache. Decrements cache_size_ on succeess.
// May temporarily take lock_. If it takes lock_, the locked_size_class
// lock is released to keep the thread from holding two size class locks
// concurrently which could lead to a deadlock.
bool ShrinkCache(int locked_size_class, bool force) LOCKS_EXCLUDED(lock_);
// This lock protects all the data members. cached_entries and cache_size_
// may be looked at without holding the lock.
SpinLock lock_;
// We keep linked lists of empty and non-empty spans.
size_t size_class_; // My size class
Span empty_; // Dummy header for list of empty spans
Span nonempty_; // Dummy header for list of non-empty spans
size_t num_spans_; // Number of spans in empty_ plus nonempty_
size_t counter_; // Number of free objects in cache entry
// Here we reserve space for TCEntry cache slots. Space is preallocated
// for the largest possible number of entries than any one size class may
// accumulate. Not all size classes are allowed to accumulate
// kMaxNumTransferEntries, so there is some wasted space for those size
// classes.
TCEntry tc_slots_[kMaxNumTransferEntries];
// Number of currently used cached entries in tc_slots_. This variable is
// updated under a lock but can be read without one.
int32_t used_slots_;
// The current number of slots for this size class. This is an
// adaptive value that is increased if there is lots of traffic
// on a given size class.
int32_t cache_size_;
// Maximum size of the cache for a given size class.
int32_t max_cache_size_;
};
// Pads each CentralCache object to multiple of 64 bytes. Since some
// compilers (such as MSVC) don't like it when the padding is 0, I use
// template specialization to remove the padding entirely when
// sizeof(CentralFreeList) is a multiple of 64.
template<int kFreeListSizeMod64>
class CentralFreeListPaddedTo : public CentralFreeList {
private:
char pad_[64 - kFreeListSizeMod64];
};
template<>
class CentralFreeListPaddedTo<0> : public CentralFreeList {
};
class CentralFreeListPadded : public CentralFreeListPaddedTo<
sizeof(CentralFreeList) % 64> {
};
} // namespace tcmalloc
#endif // TCMALLOC_CENTRAL_FREELIST_H_

View file

@ -0,0 +1,291 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2008, 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 <opensource@google.com>
#include <stdlib.h> // for getenv and strtol
#include "config.h"
#include "common.h"
#include "system-alloc.h"
#include "base/spinlock.h"
#include "getenv_safe.h" // TCMallocGetenvSafe
namespace tcmalloc {
// Define the maximum number of object per classe type to transfer between
// thread and central caches.
static int32 FLAGS_tcmalloc_transfer_num_objects;
static const int32 kDefaultTransferNumObjecs = 32;
// The init function is provided to explicit initialize the variable value
// from the env. var to avoid C++ global construction that might defer its
// initialization after a malloc/new call.
static inline void InitTCMallocTransferNumObjects()
{
if (FLAGS_tcmalloc_transfer_num_objects == 0) {
const char *envval = TCMallocGetenvSafe("TCMALLOC_TRANSFER_NUM_OBJ");
FLAGS_tcmalloc_transfer_num_objects = !envval ? kDefaultTransferNumObjecs :
strtol(envval, NULL, 10);
}
}
// Note: the following only works for "n"s that fit in 32-bits, but
// that is fine since we only use it for small sizes.
static inline int LgFloor(size_t n) {
int log = 0;
for (int i = 4; i >= 0; --i) {
int shift = (1 << i);
size_t x = n >> shift;
if (x != 0) {
n = x;
log += shift;
}
}
ASSERT(n == 1);
return log;
}
int AlignmentForSize(size_t size) {
int alignment = kAlignment;
if (size > kMaxSize) {
// Cap alignment at kPageSize for large sizes.
alignment = kPageSize;
} else if (size >= 128) {
// Space wasted due to alignment is at most 1/8, i.e., 12.5%.
alignment = (1 << LgFloor(size)) / 8;
} else if (size >= kMinAlign) {
// We need an alignment of at least 16 bytes to satisfy
// requirements for some SSE types.
alignment = kMinAlign;
}
// Maximum alignment allowed is page size alignment.
if (alignment > kPageSize) {
alignment = kPageSize;
}
CHECK_CONDITION(size < kMinAlign || alignment >= kMinAlign);
CHECK_CONDITION((alignment & (alignment - 1)) == 0);
return alignment;
}
int SizeMap::NumMoveSize(size_t size) {
if (size == 0) return 0;
// Use approx 64k transfers between thread and central caches.
int num = static_cast<int>(64.0 * 1024.0 / size);
if (num < 2) num = 2;
// Avoid bringing too many objects into small object free lists.
// If this value is too large:
// - We waste memory with extra objects sitting in the thread caches.
// - The central freelist holds its lock for too long while
// building a linked list of objects, slowing down the allocations
// of other threads.
// If this value is too small:
// - We go to the central freelist too often and we have to acquire
// its lock each time.
// This value strikes a balance between the constraints above.
if (num > FLAGS_tcmalloc_transfer_num_objects)
num = FLAGS_tcmalloc_transfer_num_objects;
return num;
}
// Initialize the mapping arrays
void SizeMap::Init() {
InitTCMallocTransferNumObjects();
// Do some sanity checking on add_amount[]/shift_amount[]/class_array[]
if (ClassIndex(0) != 0) {
Log(kCrash, __FILE__, __LINE__,
"Invalid class index for size 0", ClassIndex(0));
}
if (ClassIndex(kMaxSize) >= sizeof(class_array_)) {
Log(kCrash, __FILE__, __LINE__,
"Invalid class index for kMaxSize", ClassIndex(kMaxSize));
}
// Compute the size classes we want to use
int sc = 1; // Next size class to assign
int alignment = kAlignment;
CHECK_CONDITION(kAlignment <= kMinAlign);
for (size_t size = kAlignment; size <= kMaxSize; size += alignment) {
alignment = AlignmentForSize(size);
CHECK_CONDITION((size % alignment) == 0);
int blocks_to_move = NumMoveSize(size) / 4;
size_t psize = 0;
do {
psize += kPageSize;
// Allocate enough pages so leftover is less than 1/8 of total.
// This bounds wasted space to at most 12.5%.
while ((psize % size) > (psize >> 3)) {
psize += kPageSize;
}
// Continue to add pages until there are at least as many objects in
// the span as are needed when moving objects from the central
// freelists and spans to the thread caches.
} while ((psize / size) < (blocks_to_move));
const size_t my_pages = psize >> kPageShift;
if (sc > 1 && my_pages == class_to_pages_[sc-1]) {
// See if we can merge this into the previous class without
// increasing the fragmentation of the previous class.
const size_t my_objects = (my_pages << kPageShift) / size;
const size_t prev_objects = (class_to_pages_[sc-1] << kPageShift)
/ class_to_size_[sc-1];
if (my_objects == prev_objects) {
// Adjust last class to include this size
class_to_size_[sc-1] = size;
continue;
}
}
// Add new class
class_to_pages_[sc] = my_pages;
class_to_size_[sc] = size;
sc++;
}
num_size_classes = sc;
if (sc > kClassSizesMax) {
Log(kCrash, __FILE__, __LINE__,
"too many size classes: (found vs. max)", sc, kClassSizesMax);
}
// Initialize the mapping arrays
int next_size = 0;
for (int c = 1; c < num_size_classes; c++) {
const int max_size_in_class = class_to_size_[c];
for (int s = next_size; s <= max_size_in_class; s += kAlignment) {
class_array_[ClassIndex(s)] = c;
}
next_size = max_size_in_class + kAlignment;
}
// Double-check sizes just to be safe
for (size_t size = 0; size <= kMaxSize;) {
const int sc = SizeClass(size);
if (sc <= 0 || sc >= num_size_classes) {
Log(kCrash, __FILE__, __LINE__,
"Bad size class (class, size)", sc, size);
}
if (sc > 1 && size <= class_to_size_[sc-1]) {
Log(kCrash, __FILE__, __LINE__,
"Allocating unnecessarily large class (class, size)", sc, size);
}
const size_t s = class_to_size_[sc];
if (size > s || s == 0) {
Log(kCrash, __FILE__, __LINE__,
"Bad (class, size, requested)", sc, s, size);
}
if (size <= kMaxSmallSize) {
size += 8;
} else {
size += 128;
}
}
// Our fast-path aligned allocation functions rely on 'naturally
// aligned' sizes to produce aligned addresses. Lets check if that
// holds for size classes that we produced.
//
// I.e. we're checking that
//
// align = (1 << shift), malloc(i * align) % align == 0,
//
// for all align values up to kPageSize.
for (size_t align = kMinAlign; align <= kPageSize; align <<= 1) {
for (size_t size = align; size < kPageSize; size += align) {
CHECK_CONDITION(class_to_size_[SizeClass(size)] % align == 0);
}
}
// Initialize the num_objects_to_move array.
for (size_t cl = 1; cl < num_size_classes; ++cl) {
num_objects_to_move_[cl] = NumMoveSize(ByteSizeForClass(cl));
}
}
// Metadata allocator -- keeps stats about how many bytes allocated.
static uint64_t metadata_system_bytes_ = 0;
static const size_t kMetadataAllocChunkSize = 8*1024*1024;
// As ThreadCache objects are allocated with MetaDataAlloc, and also
// CACHELINE_ALIGNED, we must use the same alignment as TCMalloc_SystemAlloc.
static const size_t kMetadataAllignment = sizeof(MemoryAligner);
static char *metadata_chunk_alloc_;
static size_t metadata_chunk_avail_;
static SpinLock metadata_alloc_lock(SpinLock::LINKER_INITIALIZED);
void* MetaDataAlloc(size_t bytes) {
if (bytes >= kMetadataAllocChunkSize) {
void *rv = TCMalloc_SystemAlloc(bytes,
NULL, kMetadataAllignment);
if (rv != NULL) {
metadata_system_bytes_ += bytes;
}
return rv;
}
SpinLockHolder h(&metadata_alloc_lock);
// the following works by essentially turning address to integer of
// log_2 kMetadataAllignment size and negating it. I.e. negated
// value + original value gets 0 and that's what we want modulo
// kMetadataAllignment. Note, we negate before masking higher bits
// off, otherwise we'd have to mask them off after negation anyways.
intptr_t alignment = -reinterpret_cast<intptr_t>(metadata_chunk_alloc_) & (kMetadataAllignment-1);
if (metadata_chunk_avail_ < bytes + alignment) {
size_t real_size;
void *ptr = TCMalloc_SystemAlloc(kMetadataAllocChunkSize,
&real_size, kMetadataAllignment);
if (ptr == NULL) {
return NULL;
}
metadata_chunk_alloc_ = static_cast<char *>(ptr);
metadata_chunk_avail_ = real_size;
alignment = 0;
}
void *rv = static_cast<void *>(metadata_chunk_alloc_ + alignment);
bytes += alignment;
metadata_chunk_alloc_ += bytes;
metadata_chunk_avail_ -= bytes;
metadata_system_bytes_ += bytes;
return rv;
}
uint64_t metadata_system_bytes() { return metadata_system_bytes_; }
} // namespace tcmalloc

View file

@ -0,0 +1,309 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2008, 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 <opensource@google.com>
//
// Common definitions for tcmalloc code.
#ifndef TCMALLOC_COMMON_H_
#define TCMALLOC_COMMON_H_
#include "config.h"
#include <stddef.h> // for size_t
#ifdef HAVE_STDINT_H
#include <stdint.h> // for uintptr_t, uint64_t
#endif
#include "internal_logging.h" // for ASSERT, etc
#include "base/basictypes.h" // for LIKELY, etc
// Type that can hold a page number
typedef uintptr_t PageID;
// Type that can hold the length of a run of pages
typedef uintptr_t Length;
//-------------------------------------------------------------------
// Configuration
//-------------------------------------------------------------------
#if defined(TCMALLOC_ALIGN_8BYTES)
// Unless we force to use 8 bytes alignment we use an alignment of
// at least 16 bytes to statisfy requirements for some SSE types.
// Keep in mind when using the 16 bytes alignment you can have a space
// waste due alignment of 25%. (eg malloc of 24 bytes will get 32 bytes)
static const size_t kMinAlign = 8;
#else
static const size_t kMinAlign = 16;
#endif
// Using large pages speeds up the execution at a cost of larger memory use.
// Deallocation may speed up by a factor as the page map gets 8x smaller, so
// lookups in the page map result in fewer L2 cache misses, which translates to
// speedup for application/platform combinations with high L2 cache pressure.
// As the number of size classes increases with large pages, we increase
// the thread cache allowance to avoid passing more free ranges to and from
// central lists. Also, larger pages are less likely to get freed.
// These two factors cause a bounded increase in memory use.
#if defined(TCMALLOC_PAGE_SIZE_SHIFT)
static const size_t kPageShift = TCMALLOC_PAGE_SIZE_SHIFT;
#else
static const size_t kPageShift = 13;
#endif
static const size_t kClassSizesMax = 128;
static const size_t kMaxThreadCacheSize = 4 << 20;
static const size_t kPageSize = 1 << kPageShift;
static const size_t kMaxSize = 256 * 1024;
static const size_t kAlignment = 8;
// For all span-lengths <= kMaxPages we keep an exact-size list in PageHeap.
static const size_t kMaxPages = 1 << (20 - kPageShift);
// Default bound on the total amount of thread caches.
#ifdef TCMALLOC_SMALL_BUT_SLOW
// Make the overall thread cache no bigger than that of a single thread
// for the small memory footprint case.
static const size_t kDefaultOverallThreadCacheSize = kMaxThreadCacheSize;
#else
static const size_t kDefaultOverallThreadCacheSize = 8u * kMaxThreadCacheSize;
#endif
// Lower bound on the per-thread cache sizes
static const size_t kMinThreadCacheSize = kMaxSize * 2;
// The number of bytes one ThreadCache will steal from another when
// the first ThreadCache is forced to Scavenge(), delaying the
// next call to Scavenge for this thread.
static const size_t kStealAmount = 1 << 16;
// The number of times that a deallocation can cause a freelist to
// go over its max_length() before shrinking max_length().
static const int kMaxOverages = 3;
// Maximum length we allow a per-thread free-list to have before we
// move objects from it into the corresponding central free-list. We
// want this big to avoid locking the central free-list too often. It
// should not hurt to make this list somewhat big because the
// scavenging code will shrink it down when its contents are not in use.
static const int kMaxDynamicFreeListLength = 8192;
static const Length kMaxValidPages = (~static_cast<Length>(0)) >> kPageShift;
#if __aarch64__ || __x86_64__ || _M_AMD64 || _M_ARM64
// All current x86_64 processors only look at the lower 48 bits in
// virtual to physical address translation. The top 16 are all same as
// bit 47. And bit 47 value 1 reserved for kernel-space addresses in
// practice. So it is actually 47 usable bits from malloc
// perspective. This lets us use faster two level page maps on this
// architecture.
//
// There is very similar story on 64-bit arms except it has full 48
// bits for user-space. Because of that, and because in principle OSes
// can start giving some of highest-bit-set addresses to user-space,
// we don't bother to limit x86 to 47 bits.
//
// As of now there are published plans to add more bits to x86-64
// virtual address space, but since 48 bits has been norm for long
// time and lots of software is relying on it, it will be opt-in from
// OS perspective. So we can keep doing "48 bits" at least for now.
static const int kAddressBits = (sizeof(void*) < 8 ? (8 * sizeof(void*)) : 48);
#else
// mipsen and ppcs have more general hardware so we have to support
// full 64-bits of addresses.
static const int kAddressBits = 8 * sizeof(void*);
#endif
namespace tcmalloc {
// Convert byte size into pages. This won't overflow, but may return
// an unreasonably large value if bytes is huge enough.
inline Length pages(size_t bytes) {
return (bytes >> kPageShift) +
((bytes & (kPageSize - 1)) > 0 ? 1 : 0);
}
// For larger allocation sizes, we use larger memory alignments to
// reduce the number of size classes.
int AlignmentForSize(size_t size);
// Size-class information + mapping
class SizeMap {
private:
//-------------------------------------------------------------------
// Mapping from size to size_class and vice versa
//-------------------------------------------------------------------
// Sizes <= 1024 have an alignment >= 8. So for such sizes we have an
// array indexed by ceil(size/8). Sizes > 1024 have an alignment >= 128.
// So for these larger sizes we have an array indexed by ceil(size/128).
//
// We flatten both logical arrays into one physical array and use
// arithmetic to compute an appropriate index. The constants used by
// ClassIndex() were selected to make the flattening work.
//
// Examples:
// Size Expression Index
// -------------------------------------------------------
// 0 (0 + 7) / 8 0
// 1 (1 + 7) / 8 1
// ...
// 1024 (1024 + 7) / 8 128
// 1025 (1025 + 127 + (120<<7)) / 128 129
// ...
// 32768 (32768 + 127 + (120<<7)) / 128 376
static const int kMaxSmallSize = 1024;
static const size_t kClassArraySize =
((kMaxSize + 127 + (120 << 7)) >> 7) + 1;
unsigned char class_array_[kClassArraySize];
static inline size_t SmallSizeClass(size_t s) {
return (static_cast<uint32_t>(s) + 7) >> 3;
}
static inline size_t LargeSizeClass(size_t s) {
return (static_cast<uint32_t>(s) + 127 + (120 << 7)) >> 7;
}
// If size is no more than kMaxSize, compute index of the
// class_array[] entry for it, putting the class index in output
// parameter idx and returning true. Otherwise return false.
static inline bool ATTRIBUTE_ALWAYS_INLINE ClassIndexMaybe(size_t s,
uint32* idx) {
if (PREDICT_TRUE(s <= kMaxSmallSize)) {
*idx = (static_cast<uint32>(s) + 7) >> 3;
return true;
} else if (s <= kMaxSize) {
*idx = (static_cast<uint32>(s) + 127 + (120 << 7)) >> 7;
return true;
}
return false;
}
// Compute index of the class_array[] entry for a given size
static inline size_t ClassIndex(size_t s) {
// Use unsigned arithmetic to avoid unnecessary sign extensions.
ASSERT(0 <= s);
ASSERT(s <= kMaxSize);
if (PREDICT_TRUE(s <= kMaxSmallSize)) {
return SmallSizeClass(s);
} else {
return LargeSizeClass(s);
}
}
// Number of objects to move between a per-thread list and a central
// list in one shot. We want this to be not too small so we can
// amortize the lock overhead for accessing the central list. Making
// it too big may temporarily cause unnecessary memory wastage in the
// per-thread free list until the scavenger cleans up the list.
int num_objects_to_move_[kClassSizesMax];
int NumMoveSize(size_t size);
// Mapping from size class to max size storable in that class
int32 class_to_size_[kClassSizesMax];
// Mapping from size class to number of pages to allocate at a time
size_t class_to_pages_[kClassSizesMax];
public:
size_t num_size_classes;
// Constructor should do nothing since we rely on explicit Init()
// call, which may or may not be called before the constructor runs.
SizeMap() { }
// Initialize the mapping arrays
void Init();
inline int SizeClass(size_t size) {
return class_array_[ClassIndex(size)];
}
// Check if size is small enough to be representable by a size
// class, and if it is, put matching size class into *cl. Returns
// true iff matching size class was found.
inline bool ATTRIBUTE_ALWAYS_INLINE GetSizeClass(size_t size, uint32* cl) {
uint32 idx;
if (!ClassIndexMaybe(size, &idx)) {
return false;
}
*cl = class_array_[idx];
return true;
}
// Get the byte-size for a specified class
inline int32 ATTRIBUTE_ALWAYS_INLINE ByteSizeForClass(uint32 cl) {
return class_to_size_[cl];
}
// Mapping from size class to max size storable in that class
inline int32 class_to_size(uint32 cl) {
return class_to_size_[cl];
}
// Mapping from size class to number of pages to allocate at a time
inline size_t class_to_pages(uint32 cl) {
return class_to_pages_[cl];
}
// Number of objects to move between a per-thread list and a central
// list in one shot. We want this to be not too small so we can
// amortize the lock overhead for accessing the central list. Making
// it too big may temporarily cause unnecessary memory wastage in the
// per-thread free list until the scavenger cleans up the list.
inline int num_objects_to_move(uint32 cl) {
return num_objects_to_move_[cl];
}
};
// Allocates "bytes" worth of memory and returns it. Increments
// metadata_system_bytes appropriately. May return NULL if allocation
// fails. Requires pageheap_lock is held.
void* MetaDataAlloc(size_t bytes);
// Returns the total number of bytes allocated from the system.
// Requires pageheap_lock is held.
uint64_t metadata_system_bytes();
// size/depth are made the same size as a pointer so that some generic
// code below can conveniently cast them back and forth to void*.
static const int kMaxStackDepth = 31;
struct StackTrace {
uintptr_t size; // Size of object
uintptr_t depth; // Number of PC values stored in array below
void* stack[kMaxStackDepth];
};
} // namespace tcmalloc
#endif // TCMALLOC_COMMON_H_

View file

@ -0,0 +1,279 @@
/* src/config.h.in. Generated from configure.ac by autoheader. */
#ifndef GPERFTOOLS_CONFIG_H_
#define GPERFTOOLS_CONFIG_H_
/* enable aggressive decommit by default */
#undef ENABLE_AGGRESSIVE_DECOMMIT_BY_DEFAULT
/* Build new/delete operators for overaligned types */
#undef ENABLE_ALIGNED_NEW_DELETE
/* Build runtime detection for sized delete */
#undef ENABLE_DYNAMIC_SIZED_DELETE
/* report large allocation */
#undef ENABLE_LARGE_ALLOC_REPORT
/* Build sized deletion operators */
#undef ENABLE_SIZED_DELETE
/* Define to 1 if you have the <asm/ptrace.h> header file. */
#undef HAVE_ASM_PTRACE_H
/* Define to 1 if you have the <conflict-signal.h> header file. */
#undef HAVE_CONFLICT_SIGNAL_H
/* define if the compiler supports basic C++11 syntax */
#undef HAVE_CXX11
/* Define to 1 if you have the <cygwin/signal.h> header file. */
#undef HAVE_CYGWIN_SIGNAL_H
/* Define to 1 if you have the declaration of `backtrace', and to 0 if you
don't. */
#undef HAVE_DECL_BACKTRACE
/* Define to 1 if you have the declaration of `cfree', and to 0 if you don't.
*/
#undef HAVE_DECL_CFREE
/* Define to 1 if you have the declaration of `memalign', and to 0 if you
don't. */
#undef HAVE_DECL_MEMALIGN
/* Define to 1 if you have the declaration of `nanosleep', and to 0 if you
don't. */
#undef HAVE_DECL_NANOSLEEP
/* Define to 1 if you have the declaration of `posix_memalign', and to 0 if
you don't. */
#undef HAVE_DECL_POSIX_MEMALIGN
/* Define to 1 if you have the declaration of `pvalloc', and to 0 if you
don't. */
#undef HAVE_DECL_PVALLOC
/* Define to 1 if you have the declaration of `sleep', and to 0 if you don't.
*/
#undef HAVE_DECL_SLEEP
/* Define to 1 if you have the declaration of `valloc', and to 0 if you don't.
*/
#undef HAVE_DECL_VALLOC
/* Define to 1 if you have the <dlfcn.h> header file. */
#undef HAVE_DLFCN_H
/* Define to 1 if the system has the type `Elf32_Versym'. */
#undef HAVE_ELF32_VERSYM
/* Define to 1 if you have the <execinfo.h> header file. */
#undef HAVE_EXECINFO_H
/* Define to 1 if you have the <fcntl.h> header file. */
#undef HAVE_FCNTL_H
/* Define to 1 if you have the <features.h> header file. */
#undef HAVE_FEATURES_H
/* Define to 1 if you have the `fork' function. */
#undef HAVE_FORK
/* Define to 1 if you have the `geteuid' function. */
#undef HAVE_GETEUID
/* Define to 1 if you have the <glob.h> header file. */
#undef HAVE_GLOB_H
/* Define to 1 if you have the <grp.h> header file. */
#undef HAVE_GRP_H
/* Define to 1 if you have the <inttypes.h> header file. */
#undef HAVE_INTTYPES_H
/* Define to 1 if you have the <libunwind.h> header file. */
#undef HAVE_LIBUNWIND_H
/* Define to 1 if you have the <linux/ptrace.h> header file. */
#undef HAVE_LINUX_PTRACE_H
/* Define if this is Linux that has SIGEV_THREAD_ID */
#undef HAVE_LINUX_SIGEV_THREAD_ID
/* Define to 1 if you have the <malloc.h> header file. */
#undef HAVE_MALLOC_H
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
/* Define to 1 if you have a working `mmap' system call. */
#undef HAVE_MMAP
/* Define to 1 if you have the <poll.h> header file. */
#undef HAVE_POLL_H
/* define if libc has program_invocation_name */
#undef HAVE_PROGRAM_INVOCATION_NAME
/* Define if you have POSIX threads libraries and header files. */
#undef HAVE_PTHREAD
/* defined to 1 if pthread symbols are exposed even without include pthread.h
*/
#undef HAVE_PTHREAD_DESPITE_ASKING_FOR
/* Define to 1 if you have the <pwd.h> header file. */
#undef HAVE_PWD_H
/* Define to 1 if you have the `sbrk' function. */
#undef HAVE_SBRK
/* Define to 1 if you have the <sched.h> header file. */
#undef HAVE_SCHED_H
/* Define to 1 if you have the <stdint.h> header file. */
#undef HAVE_STDINT_H
/* Define to 1 if you have the <stdlib.h> header file. */
#undef HAVE_STDLIB_H
/* Define to 1 if you have the <strings.h> header file. */
#undef HAVE_STRINGS_H
/* Define to 1 if you have the <string.h> header file. */
#undef HAVE_STRING_H
/* Define to 1 if the system has the type `struct mallinfo'. */
#undef HAVE_STRUCT_MALLINFO
/* Define to 1 if you have the <sys/cdefs.h> header file. */
#undef HAVE_SYS_CDEFS_H
/* Define to 1 if you have the <sys/prctl.h> header file. */
#undef HAVE_SYS_PRCTL_H
/* Define to 1 if you have the <sys/resource.h> header file. */
#undef HAVE_SYS_RESOURCE_H
/* Define to 1 if you have the <sys/socket.h> header file. */
#undef HAVE_SYS_SOCKET_H
/* Define to 1 if you have the <sys/stat.h> header file. */
#undef HAVE_SYS_STAT_H
/* Define to 1 if you have the <sys/syscall.h> header file. */
#undef HAVE_SYS_SYSCALL_H
/* Define to 1 if you have the <sys/types.h> header file. */
#undef HAVE_SYS_TYPES_H
/* Define to 1 if you have the <sys/ucontext.h> header file. */
#undef HAVE_SYS_UCONTEXT_H
/* Define to 1 if you have the <sys/wait.h> header file. */
#undef HAVE_SYS_WAIT_H
/* Define to 1 if compiler supports __thread */
#undef HAVE_TLS
/* Define to 1 if you have the <ucontext.h> header file. */
#undef HAVE_UCONTEXT_H
/* Define to 1 if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Whether <unwind.h> contains _Unwind_Backtrace */
#undef HAVE_UNWIND_BACKTRACE
/* Define to 1 if you have the <unwind.h> header file. */
#undef HAVE_UNWIND_H
/* define if your compiler has __attribute__ */
#undef HAVE___ATTRIBUTE__
/* define if your compiler supports alignment of functions */
#undef HAVE___ATTRIBUTE__ALIGNED_FN
/* Define to 1 if compiler supports __environ */
#undef HAVE___ENVIRON
/* Define to 1 if you have the `__sbrk' function. */
#undef HAVE___SBRK
/* prefix where we look for installed files */
#undef INSTALL_PREFIX
/* Define to 1 if int32_t is equivalent to intptr_t */
#undef INT32_EQUALS_INTPTR
/* Define to the sub-directory where libtool stores uninstalled libraries. */
#undef LT_OBJDIR
/* Name of package */
#undef PACKAGE
/* Define to the address where bug reports for this package should be sent. */
#undef PACKAGE_BUGREPORT
/* Define to the full name of this package. */
#undef PACKAGE_NAME
/* Define to the full name and version of this package. */
#undef PACKAGE_STRING
/* Define to the one symbol short name of this package. */
#undef PACKAGE_TARNAME
/* Define to the home page for this package. */
#undef PACKAGE_URL
/* Define to the version of this package. */
#undef PACKAGE_VERSION
/* How to access the PC from a struct ucontext */
#undef PC_FROM_UCONTEXT
/* Always the empty-string on non-windows systems. On windows, should be
"__declspec(dllexport)". This way, when we compile the dll, we export our
functions/classes. It's safe to define this here because config.h is only
used internally, to compile the DLL, and every DLL source file #includes
"config.h" before anything else. */
#undef PERFTOOLS_DLL_DECL
/* Mark the systems where we know it's bad if pthreads runs too
early before main (before threads are initialized, presumably). */
#ifdef __FreeBSD__
#define PTHREADS_CRASHES_IF_RUN_TOO_EARLY 1
#endif
/* Define to necessary symbol if this constant uses a non-standard name on
your system. */
#undef PTHREAD_CREATE_JOINABLE
/* Define to 1 if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Define 8 bytes of allocation alignment for tcmalloc */
#undef TCMALLOC_ALIGN_8BYTES
/* Define internal page size for tcmalloc as number of left bitshift */
#undef TCMALLOC_PAGE_SIZE_SHIFT
/* Version number of package */
#undef VERSION
/* C99 says: define this to get the PRI... macros from stdint.h */
#ifndef __STDC_FORMAT_MACROS
# define __STDC_FORMAT_MACROS 1
#endif
#ifdef __MINGW32__
#include "windows/mingw.h"
#endif
#endif /* #ifndef GPERFTOOLS_CONFIG_H_ */

View file

@ -0,0 +1,65 @@
// -*- 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.
// ---
// All Rights Reserved.
//
// Author: Craig Silverstein
//
// This file is needed for windows -- unittests are not part of the
// perftools dll, but still want to include config.h just like the
// dll does, so they can use internal tools and APIs for testing.
//
// The problem is that config.h declares PERFTOOLS_DLL_DECL to be
// for exporting symbols, but the unittest needs to *import* symbols
// (since it's not the dll).
//
// The solution is to have this file, which is just like config.h but
// sets PERFTOOLS_DLL_DECL to do a dllimport instead of a dllexport.
//
// The reason we need this extra PERFTOOLS_DLL_DECL_FOR_UNITTESTS
// variable is in case people want to set PERFTOOLS_DLL_DECL explicitly
// to something other than __declspec(dllexport). In that case, they
// may want to use something other than __declspec(dllimport) for the
// unittest case. For that, we allow folks to define both
// PERFTOOLS_DLL_DECL and PERFTOOLS_DLL_DECL_FOR_UNITTESTS explicitly.
//
// NOTE: This file is equivalent to config.h on non-windows systems,
// which never defined PERFTOOLS_DLL_DECL_FOR_UNITTESTS and always
// define PERFTOOLS_DLL_DECL to the empty string.
#include "config.h"
#undef PERFTOOLS_DLL_DECL
#ifdef PERFTOOLS_DLL_DECL_FOR_UNITTESTS
# define PERFTOOLS_DLL_DECL PERFTOOLS_DLL_DECL_FOR_UNITTESTS
#else
# define PERFTOOLS_DLL_DECL // if DLL_DECL_FOR_UNITTESTS isn't defined, use ""
#endif

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,169 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2014, gperftools Contributors
// 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.
//
#include "config.h"
#include "emergency_malloc.h"
#include <errno.h> // for ENOMEM, errno
#include <string.h> // for memset
#include "base/basictypes.h"
#include "base/logging.h"
#include "base/low_level_alloc.h"
#include "base/spinlock.h"
#include "internal_logging.h"
namespace tcmalloc {
__attribute__ ((visibility("internal"))) char *emergency_arena_start;
__attribute__ ((visibility("internal"))) uintptr_t emergency_arena_start_shifted;
static CACHELINE_ALIGNED SpinLock emergency_malloc_lock(base::LINKER_INITIALIZED);
static char *emergency_arena_end;
static LowLevelAlloc::Arena *emergency_arena;
class EmergencyArenaPagesAllocator : public LowLevelAlloc::PagesAllocator {
~EmergencyArenaPagesAllocator() {}
void *MapPages(int32 flags, size_t size) {
char *new_end = emergency_arena_end + size;
if (new_end > emergency_arena_start + kEmergencyArenaSize) {
RAW_LOG(FATAL, "Unable to allocate %zu bytes in emergency zone.", size);
}
char *rv = emergency_arena_end;
emergency_arena_end = new_end;
return static_cast<void *>(rv);
}
void UnMapPages(int32 flags, void *addr, size_t size) {
RAW_LOG(FATAL, "UnMapPages is not implemented for emergency arena");
}
};
static union {
char bytes[sizeof(EmergencyArenaPagesAllocator)];
void *ptr;
} pages_allocator_place;
static void InitEmergencyMalloc(void) {
const int32 flags = LowLevelAlloc::kAsyncSignalSafe;
void *arena = LowLevelAlloc::GetDefaultPagesAllocator()->MapPages(flags, kEmergencyArenaSize * 2);
uintptr_t arena_ptr = reinterpret_cast<uintptr_t>(arena);
uintptr_t ptr = (arena_ptr + kEmergencyArenaSize - 1) & ~(kEmergencyArenaSize-1);
emergency_arena_end = emergency_arena_start = reinterpret_cast<char *>(ptr);
EmergencyArenaPagesAllocator *allocator = new (pages_allocator_place.bytes) EmergencyArenaPagesAllocator();
emergency_arena = LowLevelAlloc::NewArenaWithCustomAlloc(0, LowLevelAlloc::DefaultArena(), allocator);
emergency_arena_start_shifted = reinterpret_cast<uintptr_t>(emergency_arena_start) >> kEmergencyArenaShift;
uintptr_t head_unmap_size = ptr - arena_ptr;
CHECK_CONDITION(head_unmap_size < kEmergencyArenaSize);
if (head_unmap_size != 0) {
LowLevelAlloc::GetDefaultPagesAllocator()->UnMapPages(flags, arena, ptr - arena_ptr);
}
uintptr_t tail_unmap_size = kEmergencyArenaSize - head_unmap_size;
void *tail_start = reinterpret_cast<void *>(arena_ptr + head_unmap_size + kEmergencyArenaSize);
LowLevelAlloc::GetDefaultPagesAllocator()->UnMapPages(flags, tail_start, tail_unmap_size);
}
PERFTOOLS_DLL_DECL void *EmergencyMalloc(size_t size) {
SpinLockHolder l(&emergency_malloc_lock);
if (emergency_arena_start == NULL) {
InitEmergencyMalloc();
CHECK_CONDITION(emergency_arena_start != NULL);
}
void *rv = LowLevelAlloc::AllocWithArena(size, emergency_arena);
if (rv == NULL) {
errno = ENOMEM;
}
return rv;
}
PERFTOOLS_DLL_DECL void EmergencyFree(void *p) {
SpinLockHolder l(&emergency_malloc_lock);
if (emergency_arena_start == NULL) {
InitEmergencyMalloc();
CHECK_CONDITION(emergency_arena_start != NULL);
free(p);
return;
}
CHECK_CONDITION(emergency_arena_start);
LowLevelAlloc::Free(p);
}
PERFTOOLS_DLL_DECL void *EmergencyRealloc(void *_old_ptr, size_t new_size) {
if (_old_ptr == NULL) {
return EmergencyMalloc(new_size);
}
if (new_size == 0) {
EmergencyFree(_old_ptr);
return NULL;
}
SpinLockHolder l(&emergency_malloc_lock);
CHECK_CONDITION(emergency_arena_start);
char *old_ptr = static_cast<char *>(_old_ptr);
CHECK_CONDITION(old_ptr <= emergency_arena_end);
CHECK_CONDITION(emergency_arena_start <= old_ptr);
// NOTE: we don't know previous size of old_ptr chunk. So instead
// of trying to figure out right size of copied memory, we just
// copy largest possible size. We don't care about being slow.
size_t old_ptr_size = emergency_arena_end - old_ptr;
size_t copy_size = (new_size < old_ptr_size) ? new_size : old_ptr_size;
void *new_ptr = LowLevelAlloc::AllocWithArena(new_size, emergency_arena);
if (new_ptr == NULL) {
errno = ENOMEM;
return NULL;
}
memcpy(new_ptr, old_ptr, copy_size);
LowLevelAlloc::Free(old_ptr);
return new_ptr;
}
PERFTOOLS_DLL_DECL void *EmergencyCalloc(size_t n, size_t elem_size) {
// Overflow check
const size_t size = n * elem_size;
if (elem_size != 0 && size / elem_size != n) return NULL;
void *rv = EmergencyMalloc(size);
if (rv != NULL) {
memset(rv, 0, size);
}
return rv;
}
};

View file

@ -0,0 +1,60 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2014, gperftools Contributors
// 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.
#ifndef EMERGENCY_MALLOC_H
#define EMERGENCY_MALLOC_H
#include "config.h"
#include <stddef.h>
#include "base/basictypes.h"
#include "common.h"
namespace tcmalloc {
static const uintptr_t kEmergencyArenaShift = 20+4; // 16 megs
static const uintptr_t kEmergencyArenaSize = 1 << kEmergencyArenaShift;
extern __attribute__ ((visibility("internal"))) char *emergency_arena_start;
extern __attribute__ ((visibility("internal"))) uintptr_t emergency_arena_start_shifted;;
PERFTOOLS_DLL_DECL void *EmergencyMalloc(size_t size);
PERFTOOLS_DLL_DECL void EmergencyFree(void *p);
PERFTOOLS_DLL_DECL void *EmergencyCalloc(size_t n, size_t elem_size);
PERFTOOLS_DLL_DECL void *EmergencyRealloc(void *old_ptr, size_t new_size);
static inline bool IsEmergencyPtr(const void *_ptr) {
uintptr_t ptr = reinterpret_cast<uintptr_t>(_ptr);
return PREDICT_FALSE((ptr >> kEmergencyArenaShift) == emergency_arena_start_shifted)
&& emergency_arena_start_shifted;
}
} // namespace tcmalloc
#endif

View file

@ -0,0 +1,48 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2014, gperftools Contributors
// 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.
#include "emergency_malloc.h"
#include "thread_cache.h"
namespace tcmalloc {
bool EnterStacktraceScope(void);
void LeaveStacktraceScope(void);
}
bool tcmalloc::EnterStacktraceScope(void) {
if (ThreadCache::IsUseEmergencyMalloc()) {
return false;
}
ThreadCache::SetUseEmergencyMalloc();
return true;
}
void tcmalloc::LeaveStacktraceScope(void) {
ThreadCache::ResetUseEmergencyMalloc();
}

View file

@ -0,0 +1,39 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2014, gperftools Contributors
// 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.
#include "base/basictypes.h"
namespace tcmalloc {
ATTRIBUTE_WEAK bool EnterStacktraceScope(void) {
return true;
}
ATTRIBUTE_WEAK void LeaveStacktraceScope(void) {
}
}

View file

@ -0,0 +1,63 @@
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
* Copyright (c) 2014, gperftools Contributors
* 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.
*/
#ifndef GETENV_SAFE_H
#define GETENV_SAFE_H
#ifdef __cplusplus
extern "C" {
#endif
/*
* This getenv function is safe to call before the C runtime is initialized.
* On Windows, it utilizes GetEnvironmentVariable() and on unix it uses
* /proc/self/environ instead calling getenv(). It's intended to be used in
* routines that run before main(), when the state required for getenv() may
* not be set up yet. In particular, errno isn't set up until relatively late
* (after the pthreads library has a chance to make it threadsafe), and
* getenv() doesn't work until then.
* On some platforms, this call will utilize the same, static buffer for
* repeated GetenvBeforeMain() calls. Callers should not expect pointers from
* this routine to be long lived.
* Note that on unix, /proc only has the environment at the time the
* application was started, so this routine ignores setenv() calls/etc. Also
* note it only reads the first 16K of the environment.
*
* NOTE: this is version of GetenvBeforeMain that's usable from
* C. Implementation is in sysinfo.cc
*/
const char* TCMallocGetenvSafe(const char* name);
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,195 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2005, 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: Craig Silverstein
//
// This is an internal header file used by profiler.cc. It defines
// the single (inline) function GetPC. GetPC is used in a signal
// handler to figure out the instruction that was being executed when
// the signal-handler was triggered.
//
// To get this, we use the ucontext_t argument to the signal-handler
// callback, which holds the full context of what was going on when
// the signal triggered. How to get from a ucontext_t to a Program
// Counter is OS-dependent.
#ifndef BASE_GETPC_H_
#define BASE_GETPC_H_
#include "config.h"
// On many linux systems, we may need _GNU_SOURCE to get access to
// the defined constants that define the register we want to see (eg
// REG_EIP). Note this #define must come first!
#define _GNU_SOURCE 1
// If #define _GNU_SOURCE causes problems, this might work instead.
// It will cause problems for FreeBSD though!, because it turns off
// the needed __BSD_VISIBLE.
//#define _XOPEN_SOURCE 500
#include <string.h> // for memcmp
#ifdef HAVE_ASM_PTRACE_H
#include <asm/ptrace.h>
#endif
#if defined(HAVE_SYS_UCONTEXT_H)
#include <sys/ucontext.h>
#elif defined(HAVE_UCONTEXT_H)
#include <ucontext.h> // for ucontext_t (and also mcontext_t)
#elif defined(HAVE_CYGWIN_SIGNAL_H)
#include <cygwin/signal.h>
typedef ucontext ucontext_t;
#endif
// Take the example where function Foo() calls function Bar(). For
// many architectures, Bar() is responsible for setting up and tearing
// down its own stack frame. In that case, it's possible for the
// interrupt to happen when execution is in Bar(), but the stack frame
// is not properly set up (either before it's done being set up, or
// after it's been torn down but before Bar() returns). In those
// cases, the stack trace cannot see the caller function anymore.
//
// GetPC can try to identify this situation, on architectures where it
// might occur, and unwind the current function call in that case to
// avoid false edges in the profile graph (that is, edges that appear
// to show a call skipping over a function). To do this, we hard-code
// in the asm instructions we might see when setting up or tearing
// down a stack frame.
//
// This is difficult to get right: the instructions depend on the
// processor, the compiler ABI, and even the optimization level. This
// is a best effort patch -- if we fail to detect such a situation, or
// mess up the PC, nothing happens; the returned PC is not used for
// any further processing.
struct CallUnrollInfo {
// Offset from (e)ip register where this instruction sequence
// should be matched. Interpreted as bytes. Offset 0 is the next
// instruction to execute. Be extra careful with negative offsets in
// architectures of variable instruction length (like x86) - it is
// not that easy as taking an offset to step one instruction back!
int pc_offset;
// The actual instruction bytes. Feel free to make it larger if you
// need a longer sequence.
unsigned char ins[16];
// How many bytes to match from ins array?
int ins_size;
// The offset from the stack pointer (e)sp where to look for the
// call return address. Interpreted as bytes.
int return_sp_offset;
};
// The dereferences needed to get the PC from a struct ucontext were
// determined at configure time, and stored in the macro
// PC_FROM_UCONTEXT in config.h. The only thing we need to do here,
// then, is to do the magic call-unrolling for systems that support it.
// -- Special case 1: linux x86, for which we have CallUnrollInfo
#if defined(__linux) && defined(__i386) && defined(__GNUC__)
static const CallUnrollInfo callunrollinfo[] = {
// Entry to a function: push %ebp; mov %esp,%ebp
// Top-of-stack contains the caller IP.
{ 0,
{0x55, 0x89, 0xe5}, 3,
0
},
// Entry to a function, second instruction: push %ebp; mov %esp,%ebp
// Top-of-stack contains the old frame, caller IP is +4.
{ -1,
{0x55, 0x89, 0xe5}, 3,
4
},
// Return from a function: RET.
// Top-of-stack contains the caller IP.
{ 0,
{0xc3}, 1,
0
}
};
inline void* GetPC(const ucontext_t& signal_ucontext) {
// See comment above struct CallUnrollInfo. Only try instruction
// flow matching if both eip and esp looks reasonable.
const int eip = signal_ucontext.uc_mcontext.gregs[REG_EIP];
const int esp = signal_ucontext.uc_mcontext.gregs[REG_ESP];
if ((eip & 0xffff0000) != 0 && (~eip & 0xffff0000) != 0 &&
(esp & 0xffff0000) != 0) {
char* eip_char = reinterpret_cast<char*>(eip);
for (int i = 0; i < sizeof(callunrollinfo)/sizeof(*callunrollinfo); ++i) {
if (!memcmp(eip_char + callunrollinfo[i].pc_offset,
callunrollinfo[i].ins, callunrollinfo[i].ins_size)) {
// We have a match.
void **retaddr = (void**)(esp + callunrollinfo[i].return_sp_offset);
return *retaddr;
}
}
}
return (void*)eip;
}
// Special case #2: Windows, which has to do something totally different.
#elif defined(_WIN32) || defined(__CYGWIN__) || defined(__CYGWIN32__) || defined(__MINGW32__)
// If this is ever implemented, probably the way to do it is to have
// profiler.cc use a high-precision timer via timeSetEvent:
// http://msdn2.microsoft.com/en-us/library/ms712713.aspx
// We'd use it in mode TIME_CALLBACK_FUNCTION/TIME_PERIODIC.
// The callback function would be something like prof_handler, but
// alas the arguments are different: no ucontext_t! I don't know
// how we'd get the PC (using StackWalk64?)
// http://msdn2.microsoft.com/en-us/library/ms680650.aspx
#include "base/logging.h" // for RAW_LOG
#ifndef HAVE_CYGWIN_SIGNAL_H
typedef int ucontext_t;
#endif
inline void* GetPC(const struct ucontext_t& signal_ucontext) {
RAW_LOG(ERROR, "GetPC is not yet implemented on Windows\n");
return NULL;
}
// Normal cases. If this doesn't compile, it's probably because
// PC_FROM_UCONTEXT is the empty string. You need to figure out
// the right value for your system, and add it to the list in
// configure.ac (or set it manually in your config.h).
#else
inline void* GetPC(const ucontext_t& signal_ucontext) {
#if defined(__s390__) && !defined(__s390x__)
// Mask out the AMODE31 bit from the PC recorded in the context.
return (void*)((unsigned long)signal_ucontext.PC_FROM_UCONTEXT & 0x7fffffffUL);
#else
return (void*)signal_ucontext.PC_FROM_UCONTEXT; // defined in config.h
#endif
}
#endif
#endif // BASE_GETPC_H_

View file

@ -0,0 +1,36 @@
// Copyright (c) 2005, 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.
/* The code has moved to gperftools/. Use that include-directory for
* new code.
*/
#if defined(__GNUC__) && !defined(GPERFTOOLS_SUPPRESS_LEGACY_WARNING)
#warning "google/heap-checker.h is deprecated. Use gperftools/heap-checker.h instead"
#endif
#include <gperftools/heap-checker.h>

View file

@ -0,0 +1,37 @@
/* Copyright (c) 2005, 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.
*/
/* The code has moved to gperftools/. Use that include-directory for
* new code.
*/
#if defined(__GNUC__) && !defined(GPERFTOOLS_SUPPRESS_LEGACY_WARNING)
#warning "google/heap-profiler.h is deprecated. Use gperftools/heap-profiler.h instead"
#endif
#include <gperftools/heap-profiler.h>

View file

@ -0,0 +1,36 @@
// Copyright (c) 2005, 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.
/* The code has moved to gperftools/. Use that include-directory for
* new code.
*/
#if defined(__GNUC__) && !defined(GPERFTOOLS_SUPPRESS_LEGACY_WARNING)
#warning "google/malloc_extension.h is deprecated. Use gperftools/malloc_extension.h instead"
#endif
#include <gperftools/malloc_extension.h>

View file

@ -0,0 +1,37 @@
/* Copyright (c) 2008, 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.
*/
/* The code has moved to gperftools/. Use that include-directory for
* new code.
*/
#if defined(__GNUC__) && !defined(GPERFTOOLS_SUPPRESS_LEGACY_WARNING)
#warning "google/malloc_extension_c.h is deprecated. Use gperftools/malloc_extension_c.h instead"
#endif
#include <gperftools/malloc_extension_c.h>

View file

@ -0,0 +1,36 @@
// Copyright (c) 2005, 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.
/* The code has moved to gperftools/. Use that include-directory for
* new code.
*/
#if defined(__GNUC__) && !defined(GPERFTOOLS_SUPPRESS_LEGACY_WARNING)
#warning "google/malloc_hook.h is deprecated. Use gperftools/malloc_hook.h instead"
#endif
#include <gperftools/malloc_hook.h>

View file

@ -0,0 +1,37 @@
/* Copyright (c) 2008, 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.
*/
/* The code has moved to gperftools/. Use that include-directory for
* new code.
*/
#if defined(__GNUC__) && !defined(GPERFTOOLS_SUPPRESS_LEGACY_WARNING)
#warning "google/malloc_hook_c.h is deprecated. Use gperftools/malloc_hook_c.h instead"
#endif
#include <gperftools/malloc_hook_c.h>

View file

@ -0,0 +1,37 @@
/* Copyright (c) 2005, 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.
*/
/* The code has moved to gperftools/. Use that include-directory for
* new code.
*/
#if defined(__GNUC__) && !defined(GPERFTOOLS_SUPPRESS_LEGACY_WARNING)
#warning "google/profiler.h is deprecated. Use gperftools/profiler.h instead"
#endif
#include <gperftools/profiler.h>

View file

@ -0,0 +1,36 @@
// Copyright (c) 2005, 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.
/* The code has moved to gperftools/. Use that include-directory for
* new code.
*/
#if defined(__GNUC__) && !defined(GPERFTOOLS_SUPPRESS_LEGACY_WARNING)
#warning "google/stacktrace.h is deprecated. Use gperftools/stacktrace.h instead"
#endif
#include <gperftools/stacktrace.h>

View file

@ -0,0 +1,37 @@
/* Copyright (c) 2003, 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.
*/
/* The code has moved to gperftools/. Use that include-directory for
* new code.
*/
#if defined(__GNUC__) && !defined(GPERFTOOLS_SUPPRESS_LEGACY_WARNING)
#warning "google/tcmalloc.h is deprecated. Use gperftools/tcmalloc.h instead"
#endif
#include <gperftools/tcmalloc.h>

View file

@ -0,0 +1,422 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2005, 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: Maxim Lifantsev (with design ideas by Sanjay Ghemawat)
//
//
// Module for detecing heap (memory) leaks.
//
// For full(er) information, see docs/heap_checker.html
//
// This module can be linked into programs with
// no slowdown caused by this unless you activate the leak-checker:
//
// 1. Set the environment variable HEAPCHEK to _type_ before
// running the program.
//
// _type_ is usually "normal" but can also be "minimal", "strict", or
// "draconian". (See the html file for other options, like 'local'.)
//
// After that, just run your binary. If the heap-checker detects
// a memory leak at program-exit, it will print instructions on how
// to track down the leak.
#ifndef BASE_HEAP_CHECKER_H_
#define BASE_HEAP_CHECKER_H_
#include <sys/types.h> // for size_t
// I can't #include config.h in this public API file, but I should
// really use configure (and make malloc_extension.h a .in file) to
// figure out if the system has stdint.h or not. But I'm lazy, so
// for now I'm assuming it's a problem only with MSVC.
#ifndef _MSC_VER
#include <stdint.h> // for uintptr_t
#endif
#include <stdarg.h> // for va_list
#include <vector>
// Annoying stuff for windows -- makes sure clients can import these functions
#ifndef PERFTOOLS_DLL_DECL
# ifdef _WIN32
# define PERFTOOLS_DLL_DECL __declspec(dllimport)
# else
# define PERFTOOLS_DLL_DECL
# endif
#endif
// The class is thread-safe with respect to all the provided static methods,
// as well as HeapLeakChecker objects: they can be accessed by multiple threads.
class PERFTOOLS_DLL_DECL HeapLeakChecker {
public:
// ----------------------------------------------------------------------- //
// Static functions for working with (whole-program) leak checking.
// If heap leak checking is currently active in some mode
// e.g. if leak checking was started (and is still active now)
// due to HEAPCHECK=... defined in the environment.
// The return value reflects iff HeapLeakChecker objects manually
// constructed right now will be doing leak checking or nothing.
// Note that we can go from active to inactive state during InitGoogle()
// if FLAGS_heap_check gets set to "" by some code before/during InitGoogle().
static bool IsActive();
// Return pointer to the whole-program checker if it has been created
// and NULL otherwise.
// Once GlobalChecker() returns non-NULL that object will not disappear and
// will be returned by all later GlobalChecker calls.
// This is mainly to access BytesLeaked() and ObjectsLeaked() (see below)
// for the whole-program checker after one calls NoGlobalLeaks()
// or similar and gets false.
static HeapLeakChecker* GlobalChecker();
// Do whole-program leak check now (if it was activated for this binary);
// return false only if it was activated and has failed.
// The mode of the check is controlled by the command-line flags.
// This method can be called repeatedly.
// Things like GlobalChecker()->SameHeap() can also be called explicitly
// to do the desired flavor of the check.
static bool NoGlobalLeaks();
// If whole-program checker if active,
// cancel its automatic execution after main() exits.
// This requires that some leak check (e.g. NoGlobalLeaks())
// has been called at least once on the whole-program checker.
static void CancelGlobalCheck();
// ----------------------------------------------------------------------- //
// Non-static functions for starting and doing leak checking.
// Start checking and name the leak check performed.
// The name is used in naming dumped profiles
// and needs to be unique only within your binary.
// It must also be a string that can be a part of a file name,
// in particular not contain path expressions.
explicit HeapLeakChecker(const char *name);
// Destructor (verifies that some *NoLeaks or *SameHeap method
// has been called at least once).
~HeapLeakChecker();
// These used to be different but are all the same now: they return
// true iff all memory allocated since this HeapLeakChecker object
// was constructor is still reachable from global state.
//
// Because we fork to convert addresses to symbol-names, and forking
// is not thread-safe, and we may be called in a threaded context,
// we do not try to symbolize addresses when called manually.
bool NoLeaks() { return DoNoLeaks(DO_NOT_SYMBOLIZE); }
// These forms are obsolete; use NoLeaks() instead.
// TODO(csilvers): mark as DEPRECATED.
bool QuickNoLeaks() { return NoLeaks(); }
bool BriefNoLeaks() { return NoLeaks(); }
bool SameHeap() { return NoLeaks(); }
bool QuickSameHeap() { return NoLeaks(); }
bool BriefSameHeap() { return NoLeaks(); }
// Detailed information about the number of leaked bytes and objects
// (both of these can be negative as well).
// These are available only after a *SameHeap or *NoLeaks
// method has been called.
// Note that it's possible for both of these to be zero
// while SameHeap() or NoLeaks() returned false in case
// of a heap state change that is significant
// but preserves the byte and object counts.
ssize_t BytesLeaked() const;
ssize_t ObjectsLeaked() const;
// ----------------------------------------------------------------------- //
// Static helpers to make us ignore certain leaks.
// Scoped helper class. Should be allocated on the stack inside a
// block of code. Any heap allocations done in the code block
// covered by the scoped object (including in nested function calls
// done by the code block) will not be reported as leaks. This is
// the recommended replacement for the GetDisableChecksStart() and
// DisableChecksToHereFrom() routines below.
//
// Example:
// void Foo() {
// HeapLeakChecker::Disabler disabler;
// ... code that allocates objects whose leaks should be ignored ...
// }
//
// REQUIRES: Destructor runs in same thread as constructor
class Disabler {
public:
Disabler();
~Disabler();
private:
Disabler(const Disabler&); // disallow copy
void operator=(const Disabler&); // and assign
};
// Ignore an object located at 'ptr' (can go at the start or into the object)
// as well as all heap objects (transitively) referenced from it for the
// purposes of heap leak checking. Returns 'ptr' so that one can write
// static T* obj = IgnoreObject(new T(...));
//
// If 'ptr' does not point to an active allocated object at the time of this
// call, it is ignored; but if it does, the object must not get deleted from
// the heap later on.
//
// See also HiddenPointer, below, if you need to prevent a pointer from
// being traversed by the heap checker but do not wish to transitively
// whitelist objects referenced through it.
template <typename T>
static T* IgnoreObject(T* ptr) {
DoIgnoreObject(static_cast<const void*>(const_cast<const T*>(ptr)));
return ptr;
}
// Undo what an earlier IgnoreObject() call promised and asked to do.
// At the time of this call 'ptr' must point at or inside of an active
// allocated object which was previously registered with IgnoreObject().
static void UnIgnoreObject(const void* ptr);
// ----------------------------------------------------------------------- //
// Internal types defined in .cc
class Allocator;
struct RangeValue;
private:
// ----------------------------------------------------------------------- //
// Various helpers
// Create the name of the heap profile file.
// Should be deleted via Allocator::Free().
char* MakeProfileNameLocked();
// Helper for constructors
void Create(const char *name, bool make_start_snapshot);
enum ShouldSymbolize { SYMBOLIZE, DO_NOT_SYMBOLIZE };
// Helper for *NoLeaks and *SameHeap
bool DoNoLeaks(ShouldSymbolize should_symbolize);
// Helper for NoGlobalLeaks, also called by the global destructor.
static bool NoGlobalLeaksMaybeSymbolize(ShouldSymbolize should_symbolize);
// These used to be public, but they are now deprecated.
// Will remove entirely when all internal uses are fixed.
// In the meantime, use friendship so the unittest can still test them.
static void* GetDisableChecksStart();
static void DisableChecksToHereFrom(const void* start_address);
static void DisableChecksIn(const char* pattern);
friend void RangeDisabledLeaks();
friend void NamedTwoDisabledLeaks();
friend void* RunNamedDisabledLeaks(void*);
friend void TestHeapLeakCheckerNamedDisabling();
// Actually implements IgnoreObject().
static void DoIgnoreObject(const void* ptr);
// Disable checks based on stack trace entry at a depth <=
// max_depth. Used to hide allocations done inside some special
// libraries.
static void DisableChecksFromToLocked(const void* start_address,
const void* end_address,
int max_depth);
// Helper for DoNoLeaks to ignore all objects reachable from all live data
static void IgnoreAllLiveObjectsLocked(const void* self_stack_top);
// Callback we pass to TCMalloc_ListAllProcessThreads (see thread_lister.h)
// that is invoked when all threads of our process are found and stopped.
// The call back does the things needed to ignore live data reachable from
// thread stacks and registers for all our threads
// as well as do other global-live-data ignoring
// (via IgnoreNonThreadLiveObjectsLocked)
// during the quiet state of all threads being stopped.
// For the argument meaning see the comment by TCMalloc_ListAllProcessThreads.
// Here we only use num_threads and thread_pids, that TCMalloc_ListAllProcessThreads
// fills for us with the number and pids of all the threads of our process
// it found and attached to.
static int IgnoreLiveThreadsLocked(void* parameter,
int num_threads,
pid_t* thread_pids,
va_list ap);
// Helper for IgnoreAllLiveObjectsLocked and IgnoreLiveThreadsLocked
// that we prefer to execute from IgnoreLiveThreadsLocked
// while all threads are stopped.
// This helper does live object discovery and ignoring
// for all objects that are reachable from everything
// not related to thread stacks and registers.
static void IgnoreNonThreadLiveObjectsLocked();
// Helper for IgnoreNonThreadLiveObjectsLocked and IgnoreLiveThreadsLocked
// to discover and ignore all heap objects
// reachable from currently considered live objects
// (live_objects static global variable in out .cc file).
// "name", "name2" are two strings that we print one after another
// in a debug message to describe what kind of live object sources
// are being used.
static void IgnoreLiveObjectsLocked(const char* name, const char* name2);
// Do the overall whole-program heap leak check if needed;
// returns true when did the leak check.
static bool DoMainHeapCheck();
// Type of task for UseProcMapsLocked
enum ProcMapsTask {
RECORD_GLOBAL_DATA,
DISABLE_LIBRARY_ALLOCS
};
// Success/Error Return codes for UseProcMapsLocked.
enum ProcMapsResult {
PROC_MAPS_USED,
CANT_OPEN_PROC_MAPS,
NO_SHARED_LIBS_IN_PROC_MAPS
};
// Read /proc/self/maps, parse it, and do the 'proc_maps_task' for each line.
static ProcMapsResult UseProcMapsLocked(ProcMapsTask proc_maps_task);
// A ProcMapsTask to disable allocations from 'library'
// that is mapped to [start_address..end_address)
// (only if library is a certain system library).
static void DisableLibraryAllocsLocked(const char* library,
uintptr_t start_address,
uintptr_t end_address);
// Return true iff "*ptr" points to a heap object
// ("*ptr" can point at the start or inside of a heap object
// so that this works e.g. for pointers to C++ arrays, C++ strings,
// multiple-inherited objects, or pointers to members).
// We also fill *object_size for this object then
// and we move "*ptr" to point to the very start of the heap object.
static inline bool HaveOnHeapLocked(const void** ptr, size_t* object_size);
// Helper to shutdown heap leak checker when it's not needed
// or can't function properly.
static void TurnItselfOffLocked();
// Internally-used c-tor to start whole-executable checking.
HeapLeakChecker();
// ----------------------------------------------------------------------- //
// Friends and externally accessed helpers.
// Helper for VerifyHeapProfileTableStackGet in the unittest
// to get the recorded allocation caller for ptr,
// which must be a heap object.
static const void* GetAllocCaller(void* ptr);
friend void VerifyHeapProfileTableStackGet();
// This gets to execute before constructors for all global objects
static void BeforeConstructorsLocked();
friend void HeapLeakChecker_BeforeConstructors();
// This gets to execute after destructors for all global objects
friend void HeapLeakChecker_AfterDestructors();
// Full starting of recommended whole-program checking.
friend void HeapLeakChecker_InternalInitStart();
// Runs REGISTER_HEAPCHECK_CLEANUP cleanups and potentially
// calls DoMainHeapCheck
friend void HeapLeakChecker_RunHeapCleanups();
// ----------------------------------------------------------------------- //
// Member data.
class SpinLock* lock_; // to make HeapLeakChecker objects thread-safe
const char* name_; // our remembered name (we own it)
// NULL means this leak checker is a noop
// Snapshot taken when the checker was created. May be NULL
// for the global heap checker object. We use void* instead of
// HeapProfileTable::Snapshot* to avoid including heap-profile-table.h.
void* start_snapshot_;
bool has_checked_; // if we have done the leak check, so these are ready:
ssize_t inuse_bytes_increase_; // bytes-in-use increase for this checker
ssize_t inuse_allocs_increase_; // allocations-in-use increase
// for this checker
bool keep_profiles_; // iff we should keep the heap profiles we've made
// ----------------------------------------------------------------------- //
// Disallow "evil" constructors.
HeapLeakChecker(const HeapLeakChecker&);
void operator=(const HeapLeakChecker&);
};
// Holds a pointer that will not be traversed by the heap checker.
// Contrast with HeapLeakChecker::IgnoreObject(o), in which o and
// all objects reachable from o are ignored by the heap checker.
template <class T>
class HiddenPointer {
public:
explicit HiddenPointer(T* t)
: masked_t_(reinterpret_cast<uintptr_t>(t) ^ kHideMask) {
}
// Returns unhidden pointer. Be careful where you save the result.
T* get() const { return reinterpret_cast<T*>(masked_t_ ^ kHideMask); }
private:
// Arbitrary value, but not such that xor'ing with it is likely
// to map one valid pointer to another valid pointer:
static const uintptr_t kHideMask =
static_cast<uintptr_t>(0xF03A5F7BF03A5F7Bll);
uintptr_t masked_t_;
};
// A class that exists solely to run its destructor. This class should not be
// used directly, but instead by the REGISTER_HEAPCHECK_CLEANUP macro below.
class PERFTOOLS_DLL_DECL HeapCleaner {
public:
typedef void (*void_function)(void);
HeapCleaner(void_function f);
static void RunHeapCleanups();
private:
static std::vector<void_function>* heap_cleanups_;
};
// A macro to declare module heap check cleanup tasks
// (they run only if we are doing heap leak checking.)
// 'body' should be the cleanup code to run. 'name' doesn't matter,
// but must be unique amongst all REGISTER_HEAPCHECK_CLEANUP calls.
#define REGISTER_HEAPCHECK_CLEANUP(name, body) \
namespace { \
void heapcheck_cleanup_##name() { body; } \
static HeapCleaner heapcheck_cleaner_##name(&heapcheck_cleanup_##name); \
}
#endif // BASE_HEAP_CHECKER_H_

View file

@ -0,0 +1,105 @@
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/* Copyright (c) 2005, 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
*
* Module for heap-profiling.
*
* For full(er) information, see docs/heapprofile.html
*
* This module can be linked into your program with
* no slowdown caused by this unless you activate the profiler
* using one of the following methods:
*
* 1. Before starting the program, set the environment variable
* "HEAPPROFILE" to be the name of the file to which the profile
* data should be written.
*
* 2. Programmatically, start and stop the profiler using the
* routines "HeapProfilerStart(filename)" and "HeapProfilerStop()".
*
*/
#ifndef BASE_HEAP_PROFILER_H_
#define BASE_HEAP_PROFILER_H_
#include <stddef.h>
/* Annoying stuff for windows; makes sure clients can import these functions */
#ifndef PERFTOOLS_DLL_DECL
# ifdef _WIN32
# define PERFTOOLS_DLL_DECL __declspec(dllimport)
# else
# define PERFTOOLS_DLL_DECL
# endif
#endif
/* All this code should be usable from within C apps. */
#ifdef __cplusplus
extern "C" {
#endif
/* Start profiling and arrange to write profile data to file names
* of the form: "prefix.0000", "prefix.0001", ...
*/
PERFTOOLS_DLL_DECL void HeapProfilerStart(const char* prefix);
/* Returns non-zero if we are currently profiling the heap. (Returns
* an int rather than a bool so it's usable from C.) This is true
* between calls to HeapProfilerStart() and HeapProfilerStop(), and
* also if the program has been run with HEAPPROFILER, or some other
* way to turn on whole-program profiling.
*/
int IsHeapProfilerRunning();
/* Stop heap profiling. Can be restarted again with HeapProfilerStart(),
* but the currently accumulated profiling information will be cleared.
*/
PERFTOOLS_DLL_DECL void HeapProfilerStop();
/* Dump a profile now - can be used for dumping at a hopefully
* quiescent state in your program, in order to more easily track down
* memory leaks. Will include the reason in the logged message
*/
PERFTOOLS_DLL_DECL void HeapProfilerDump(const char *reason);
/* Generate current heap profiling information.
* Returns an empty string when heap profiling is not active.
* The returned pointer is a '\0'-terminated string allocated using malloc()
* and should be free()-ed as soon as the caller does not need it anymore.
*/
PERFTOOLS_DLL_DECL char* GetHeapProfile();
#ifdef __cplusplus
} // extern "C"
#endif
#endif /* BASE_HEAP_PROFILER_H_ */

View file

@ -0,0 +1,446 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2005, 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 <opensource@google.com>
//
// Extra extensions exported by some malloc implementations. These
// extensions are accessed through a virtual base class so an
// application can link against a malloc that does not implement these
// extensions, and it will get default versions that do nothing.
//
// NOTE FOR C USERS: If you wish to use this functionality from within
// a C program, see malloc_extension_c.h.
#ifndef BASE_MALLOC_EXTENSION_H_
#define BASE_MALLOC_EXTENSION_H_
#include <stddef.h>
// I can't #include config.h in this public API file, but I should
// really use configure (and make malloc_extension.h a .in file) to
// figure out if the system has stdint.h or not. But I'm lazy, so
// for now I'm assuming it's a problem only with MSVC.
#ifndef _MSC_VER
#include <stdint.h>
#endif
#include <string>
#include <vector>
// Annoying stuff for windows -- makes sure clients can import these functions
#ifndef PERFTOOLS_DLL_DECL
# ifdef _WIN32
# define PERFTOOLS_DLL_DECL __declspec(dllimport)
# else
# define PERFTOOLS_DLL_DECL
# endif
#endif
static const int kMallocHistogramSize = 64;
// One day, we could support other types of writers (perhaps for C?)
typedef std::string MallocExtensionWriter;
namespace base {
struct MallocRange;
}
// Interface to a pluggable system allocator.
class PERFTOOLS_DLL_DECL SysAllocator {
public:
SysAllocator() {
}
virtual ~SysAllocator();
// Allocates "size"-byte of memory from system aligned with "alignment".
// Returns NULL if failed. Otherwise, the returned pointer p up to and
// including (p + actual_size -1) have been allocated.
virtual void* Alloc(size_t size, size_t *actual_size, size_t alignment) = 0;
};
// The default implementations of the following routines do nothing.
// All implementations should be thread-safe; the current one
// (TCMallocImplementation) is.
class PERFTOOLS_DLL_DECL MallocExtension {
public:
virtual ~MallocExtension();
// Call this very early in the program execution -- say, in a global
// constructor -- to set up parameters and state needed by all
// instrumented malloc implemenatations. One example: this routine
// sets environemnt variables to tell STL to use libc's malloc()
// instead of doing its own memory management. This is safe to call
// multiple times, as long as each time is before threads start up.
static void Initialize();
// See "verify_memory.h" to see what these routines do
virtual bool VerifyAllMemory();
virtual bool VerifyNewMemory(const void* p);
virtual bool VerifyArrayNewMemory(const void* p);
virtual bool VerifyMallocMemory(const void* p);
virtual bool MallocMemoryStats(int* blocks, size_t* total,
int histogram[kMallocHistogramSize]);
// Get a human readable description of the following malloc data structures.
// - Total inuse memory by application.
// - Free memory(thread, central and page heap),
// - Freelist of central cache, each class.
// - Page heap freelist.
// The state is stored as a null-terminated string
// in a prefix of "buffer[0,buffer_length-1]".
// REQUIRES: buffer_length > 0.
virtual void GetStats(char* buffer, int buffer_length);
// Outputs to "writer" a sample of live objects and the stack traces
// that allocated these objects. The format of the returned output
// is equivalent to the output of the heap profiler and can
// therefore be passed to "pprof". This function is equivalent to
// ReadStackTraces. The main difference is that this function returns
// serialized data appropriately formatted for use by the pprof tool.
//
// Since gperftools 2.8 heap samples are not de-duplicated by the
// library anymore.
//
// NOTE: by default, tcmalloc does not do any heap sampling, and this
// function will always return an empty sample. To get useful
// data from GetHeapSample, you must also set the environment
// variable TCMALLOC_SAMPLE_PARAMETER to a value such as 524288.
virtual void GetHeapSample(MallocExtensionWriter* writer);
// Outputs to "writer" the stack traces that caused growth in the
// address space size. The format of the returned output is
// equivalent to the output of the heap profiler and can therefore
// be passed to "pprof". This function is equivalent to
// ReadHeapGrowthStackTraces. The main difference is that this function
// returns serialized data appropriately formatted for use by the
// pprof tool. (This does not depend on, or require,
// TCMALLOC_SAMPLE_PARAMETER.)
virtual void GetHeapGrowthStacks(MallocExtensionWriter* writer);
// Invokes func(arg, range) for every controlled memory
// range. *range is filled in with information about the range.
//
// This is a best-effort interface useful only for performance
// analysis. The implementation may not call func at all.
typedef void (RangeFunction)(void*, const base::MallocRange*);
virtual void Ranges(void* arg, RangeFunction func);
// -------------------------------------------------------------------
// Control operations for getting and setting malloc implementation
// specific parameters. Some currently useful properties:
//
// generic
// -------
// "generic.current_allocated_bytes"
// Number of bytes currently allocated by application
// This property is not writable.
//
// "generic.heap_size"
// Number of bytes in the heap ==
// current_allocated_bytes +
// fragmentation +
// freed memory regions
// This property is not writable.
//
// "generic.total_physical_bytes"
// Estimate of total bytes of the physical memory usage by the
// allocator ==
// current_allocated_bytes +
// fragmentation +
// metadata
// This property is not writable.
//
// tcmalloc
// --------
// "tcmalloc.max_total_thread_cache_bytes"
// Upper limit on total number of bytes stored across all
// per-thread caches. Default: 16MB.
//
// "tcmalloc.current_total_thread_cache_bytes"
// Number of bytes used across all thread caches.
// This property is not writable.
//
// "tcmalloc.central_cache_free_bytes"
// Number of free bytes in the central cache that have been
// assigned to size classes. They always count towards virtual
// memory usage, and unless the underlying memory is swapped out
// by the OS, they also count towards physical memory usage.
// This property is not writable.
//
// "tcmalloc.transfer_cache_free_bytes"
// Number of free bytes that are waiting to be transfered between
// the central cache and a thread cache. They always count
// towards virtual memory usage, and unless the underlying memory
// is swapped out by the OS, they also count towards physical
// memory usage. This property is not writable.
//
// "tcmalloc.thread_cache_free_bytes"
// Number of free bytes in thread caches. They always count
// towards virtual memory usage, and unless the underlying memory
// is swapped out by the OS, they also count towards physical
// memory usage. This property is not writable.
//
// "tcmalloc.pageheap_free_bytes"
// Number of bytes in free, mapped pages in page heap. These
// bytes can be used to fulfill allocation requests. They
// always count towards virtual memory usage, and unless the
// underlying memory is swapped out by the OS, they also count
// towards physical memory usage. This property is not writable.
//
// "tcmalloc.pageheap_unmapped_bytes"
// Number of bytes in free, unmapped pages in page heap.
// These are bytes that have been released back to the OS,
// possibly by one of the MallocExtension "Release" calls.
// They can be used to fulfill allocation requests, but
// typically incur a page fault. They always count towards
// virtual memory usage, and depending on the OS, typically
// do not count towards physical memory usage. This property
// is not writable.
// -------------------------------------------------------------------
// Get the named "property"'s value. Returns true if the property
// is known. Returns false if the property is not a valid property
// name for the current malloc implementation.
// REQUIRES: property != NULL; value != NULL
virtual bool GetNumericProperty(const char* property, size_t* value);
// Set the named "property"'s value. Returns true if the property
// is known and writable. Returns false if the property is not a
// valid property name for the current malloc implementation, or
// is not writable.
// REQUIRES: property != NULL
virtual bool SetNumericProperty(const char* property, size_t value);
// Mark the current thread as "idle". This routine may optionally
// be called by threads as a hint to the malloc implementation that
// any thread-specific resources should be released. Note: this may
// be an expensive routine, so it should not be called too often.
//
// Also, if the code that calls this routine will go to sleep for
// a while, it should take care to not allocate anything between
// the call to this routine and the beginning of the sleep.
//
// Most malloc implementations ignore this routine.
virtual void MarkThreadIdle();
// Mark the current thread as "busy". This routine should be
// called after MarkThreadIdle() if the thread will now do more
// work. If this method is not called, performance may suffer.
//
// Most malloc implementations ignore this routine.
virtual void MarkThreadBusy();
// Gets the system allocator used by the malloc extension instance. Returns
// NULL for malloc implementations that do not support pluggable system
// allocators.
virtual SysAllocator* GetSystemAllocator();
// Sets the system allocator to the specified.
//
// Users could register their own system allocators for malloc implementation
// that supports pluggable system allocators, such as TCMalloc, by doing:
// alloc = new MyOwnSysAllocator();
// MallocExtension::instance()->SetSystemAllocator(alloc);
// It's up to users whether to fall back (recommended) to the default
// system allocator (use GetSystemAllocator() above) or not. The caller is
// responsible to any necessary locking.
// See tcmalloc/system-alloc.h for the interface and
// tcmalloc/memfs_malloc.cc for the examples.
//
// It's a no-op for malloc implementations that do not support pluggable
// system allocators.
virtual void SetSystemAllocator(SysAllocator *a);
// Try to release num_bytes of free memory back to the operating
// system for reuse. Use this extension with caution -- to get this
// memory back may require faulting pages back in by the OS, and
// that may be slow. (Currently only implemented in tcmalloc.)
virtual void ReleaseToSystem(size_t num_bytes);
// Same as ReleaseToSystem() but release as much memory as possible.
virtual void ReleaseFreeMemory();
// Sets the rate at which we release unused memory to the system.
// Zero means we never release memory back to the system. Increase
// this flag to return memory faster; decrease it to return memory
// slower. Reasonable rates are in the range [0,10]. (Currently
// only implemented in tcmalloc).
virtual void SetMemoryReleaseRate(double rate);
// Gets the release rate. Returns a value < 0 if unknown.
virtual double GetMemoryReleaseRate();
// Returns the estimated number of bytes that will be allocated for
// a request of "size" bytes. This is an estimate: an allocation of
// SIZE bytes may reserve more bytes, but will never reserve less.
// (Currently only implemented in tcmalloc, other implementations
// always return SIZE.)
// This is equivalent to malloc_good_size() in OS X.
virtual size_t GetEstimatedAllocatedSize(size_t size);
// Returns the actual number N of bytes reserved by tcmalloc for the
// pointer p. The client is allowed to use the range of bytes
// [p, p+N) in any way it wishes (i.e. N is the "usable size" of this
// allocation). This number may be equal to or greater than the number
// of bytes requested when p was allocated.
// p must have been allocated by this malloc implementation,
// must not be an interior pointer -- that is, must be exactly
// the pointer returned to by malloc() et al., not some offset
// from that -- and should not have been freed yet. p may be NULL.
// (Currently only implemented in tcmalloc; other implementations
// will return 0.)
// This is equivalent to malloc_size() in OS X, malloc_usable_size()
// in glibc, and _msize() for windows.
virtual size_t GetAllocatedSize(const void* p);
// Returns kOwned if this malloc implementation allocated the memory
// pointed to by p, or kNotOwned if some other malloc implementation
// allocated it or p is NULL. May also return kUnknownOwnership if
// the malloc implementation does not keep track of ownership.
// REQUIRES: p must be a value returned from a previous call to
// malloc(), calloc(), realloc(), memalign(), posix_memalign(),
// valloc(), pvalloc(), new, or new[], and must refer to memory that
// is currently allocated (so, for instance, you should not pass in
// a pointer after having called free() on it).
enum Ownership {
// NOTE: Enum values MUST be kept in sync with the version in
// malloc_extension_c.h
kUnknownOwnership = 0,
kOwned,
kNotOwned
};
virtual Ownership GetOwnership(const void* p);
// The current malloc implementation. Always non-NULL.
static MallocExtension* instance();
// Change the malloc implementation. Typically called by the
// malloc implementation during initialization.
static void Register(MallocExtension* implementation);
// Returns detailed information about malloc's freelists. For each list,
// return a FreeListInfo:
struct FreeListInfo {
size_t min_object_size;
size_t max_object_size;
size_t total_bytes_free;
const char* type;
};
// Each item in the vector refers to a different freelist. The lists
// are identified by the range of allocations that objects in the
// list can satisfy ([min_object_size, max_object_size]) and the
// type of freelist (see below). The current size of the list is
// returned in total_bytes_free (which count against a processes
// resident and virtual size).
//
// Currently supported types are:
//
// "tcmalloc.page{_unmapped}" - tcmalloc's page heap. An entry for each size
// class in the page heap is returned. Bytes in "page_unmapped"
// are no longer backed by physical memory and do not count against
// the resident size of a process.
//
// "tcmalloc.large{_unmapped}" - tcmalloc's list of objects larger
// than the largest page heap size class. Only one "large"
// entry is returned. There is no upper-bound on the size
// of objects in the large free list; this call returns
// kint64max for max_object_size. Bytes in
// "large_unmapped" are no longer backed by physical memory
// and do not count against the resident size of a process.
//
// "tcmalloc.central" - tcmalloc's central free-list. One entry per
// size-class is returned. Never unmapped.
//
// "debug.free_queue" - free objects queued by the debug allocator
// and not returned to tcmalloc.
//
// "tcmalloc.thread" - tcmalloc's per-thread caches. Never unmapped.
virtual void GetFreeListSizes(std::vector<FreeListInfo>* v);
// Get a list of stack traces of sampled allocation points. Returns
// a pointer to a "new[]-ed" result array, and stores the sample
// period in "sample_period".
//
// The state is stored as a sequence of adjacent entries
// in the returned array. Each entry has the following form:
// uintptr_t count; // Number of objects with following trace
// uintptr_t size; // Total size of objects with following trace
// uintptr_t depth; // Number of PC values in stack trace
// void* stack[depth]; // PC values that form the stack trace
//
// The list of entries is terminated by a "count" of 0.
//
// It is the responsibility of the caller to "delete[]" the returned array.
//
// May return NULL to indicate no results.
//
// This is an internal extension. Callers should use the more
// convenient "GetHeapSample(string*)" method defined above.
virtual void** ReadStackTraces(int* sample_period);
// Like ReadStackTraces(), but returns stack traces that caused growth
// in the address space size.
virtual void** ReadHeapGrowthStackTraces();
// Returns the size in bytes of the calling threads cache.
virtual size_t GetThreadCacheSize();
// Like MarkThreadIdle, but does not destroy the internal data
// structures of the thread cache. When the thread resumes, it wil
// have an empty cache but will not need to pay to reconstruct the
// cache data structures.
virtual void MarkThreadTemporarilyIdle();
};
namespace base {
// Information passed per range. More fields may be added later.
struct MallocRange {
enum Type {
INUSE, // Application is using this range
FREE, // Range is currently free
UNMAPPED, // Backing physical memory has been returned to the OS
UNKNOWN
// More enum values may be added in the future
};
uintptr_t address; // Address of range
size_t length; // Byte length of range
Type type; // Type of this range
double fraction; // Fraction of range that is being used (0 if !INUSE)
// Perhaps add the following:
// - stack trace if this range was sampled
// - heap growth stack trace if applicable to this range
// - age when allocated (for inuse) or freed (if not in use)
};
} // namespace base
#endif // BASE_MALLOC_EXTENSION_H_

View file

@ -0,0 +1,101 @@
/* Copyright (c) 2008, 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: Craig Silverstein
*
* C shims for the C++ malloc_extension.h. See malloc_extension.h for
* details. Note these C shims always work on
* MallocExtension::instance(); it is not possible to have more than
* one MallocExtension object in C applications.
*/
#ifndef _MALLOC_EXTENSION_C_H_
#define _MALLOC_EXTENSION_C_H_
#include <stddef.h>
#include <sys/types.h>
/* Annoying stuff for windows -- makes sure clients can import these fns */
#ifndef PERFTOOLS_DLL_DECL
# ifdef _WIN32
# define PERFTOOLS_DLL_DECL __declspec(dllimport)
# else
# define PERFTOOLS_DLL_DECL
# endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
#define kMallocExtensionHistogramSize 64
PERFTOOLS_DLL_DECL int MallocExtension_VerifyAllMemory(void);
PERFTOOLS_DLL_DECL int MallocExtension_VerifyNewMemory(const void* p);
PERFTOOLS_DLL_DECL int MallocExtension_VerifyArrayNewMemory(const void* p);
PERFTOOLS_DLL_DECL int MallocExtension_VerifyMallocMemory(const void* p);
PERFTOOLS_DLL_DECL int MallocExtension_MallocMemoryStats(int* blocks, size_t* total,
int histogram[kMallocExtensionHistogramSize]);
PERFTOOLS_DLL_DECL void MallocExtension_GetStats(char* buffer, int buffer_length);
/* TODO(csilvers): write a C version of these routines, that perhaps
* takes a function ptr and a void *.
*/
/* void MallocExtension_GetHeapSample(string* result); */
/* void MallocExtension_GetHeapGrowthStacks(string* result); */
PERFTOOLS_DLL_DECL int MallocExtension_GetNumericProperty(const char* property, size_t* value);
PERFTOOLS_DLL_DECL int MallocExtension_SetNumericProperty(const char* property, size_t value);
PERFTOOLS_DLL_DECL void MallocExtension_MarkThreadIdle(void);
PERFTOOLS_DLL_DECL void MallocExtension_MarkThreadBusy(void);
PERFTOOLS_DLL_DECL void MallocExtension_ReleaseToSystem(size_t num_bytes);
PERFTOOLS_DLL_DECL void MallocExtension_ReleaseFreeMemory(void);
PERFTOOLS_DLL_DECL size_t MallocExtension_GetEstimatedAllocatedSize(size_t size);
PERFTOOLS_DLL_DECL size_t MallocExtension_GetAllocatedSize(const void* p);
PERFTOOLS_DLL_DECL size_t MallocExtension_GetThreadCacheSize(void);
PERFTOOLS_DLL_DECL void MallocExtension_MarkThreadTemporarilyIdle(void);
/*
* NOTE: These enum values MUST be kept in sync with the version in
* malloc_extension.h
*/
typedef enum {
MallocExtension_kUnknownOwnership = 0,
MallocExtension_kOwned,
MallocExtension_kNotOwned
} MallocExtension_Ownership;
PERFTOOLS_DLL_DECL MallocExtension_Ownership MallocExtension_GetOwnership(const void* p);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* _MALLOC_EXTENSION_C_H_ */

View file

@ -0,0 +1,359 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2005, 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
//
// Some of our malloc implementations can invoke the following hooks whenever
// memory is allocated or deallocated. MallocHook is thread-safe, and things
// you do before calling AddFooHook(MyHook) are visible to any resulting calls
// to MyHook. Hooks must be thread-safe. If you write:
//
// CHECK(MallocHook::AddNewHook(&MyNewHook));
//
// MyNewHook will be invoked in subsequent calls in the current thread, but
// there are no guarantees on when it might be invoked in other threads.
//
// There are a limited number of slots available for each hook type. Add*Hook
// will return false if there are no slots available. Remove*Hook will return
// false if the given hook was not already installed.
//
// The order in which individual hooks are called in Invoke*Hook is undefined.
//
// It is safe for a hook to remove itself within Invoke*Hook and add other
// hooks. Any hooks added inside a hook invocation (for the same hook type)
// will not be invoked for the current invocation.
//
// One important user of these hooks is the heap profiler.
//
// CAVEAT: If you add new MallocHook::Invoke* calls then those calls must be
// directly in the code of the (de)allocation function that is provided to the
// user and that function must have an ATTRIBUTE_SECTION(malloc_hook) attribute.
//
// Note: the Invoke*Hook() functions are defined in malloc_hook-inl.h. If you
// need to invoke a hook (which you shouldn't unless you're part of tcmalloc),
// be sure to #include malloc_hook-inl.h in addition to malloc_hook.h.
//
// NOTE FOR C USERS: If you want to use malloc_hook functionality from
// a C program, #include malloc_hook_c.h instead of this file.
#ifndef _MALLOC_HOOK_H_
#define _MALLOC_HOOK_H_
#include <stddef.h>
#include <sys/types.h>
extern "C" {
#include "malloc_hook_c.h" // a C version of the malloc_hook interface
}
// Annoying stuff for windows -- makes sure clients can import these functions
#ifndef PERFTOOLS_DLL_DECL
# ifdef _WIN32
# define PERFTOOLS_DLL_DECL __declspec(dllimport)
# else
# define PERFTOOLS_DLL_DECL
# endif
#endif
// The C++ methods below call the C version (MallocHook_*), and thus
// convert between an int and a bool. Windows complains about this
// (a "performance warning") which we don't care about, so we suppress.
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable:4800)
#endif
// Note: malloc_hook_c.h defines MallocHook_*Hook and
// MallocHook_{Add,Remove}*Hook. The version of these inside the MallocHook
// class are defined in terms of the malloc_hook_c version. See malloc_hook_c.h
// for details of these types/functions.
class PERFTOOLS_DLL_DECL MallocHook {
public:
// The NewHook is invoked whenever an object is allocated.
// It may be passed NULL if the allocator returned NULL.
typedef MallocHook_NewHook NewHook;
inline static bool AddNewHook(NewHook hook) {
return MallocHook_AddNewHook(hook);
}
inline static bool RemoveNewHook(NewHook hook) {
return MallocHook_RemoveNewHook(hook);
}
inline static void InvokeNewHook(const void* p, size_t s);
// The DeleteHook is invoked whenever an object is deallocated.
// It may be passed NULL if the caller is trying to delete NULL.
typedef MallocHook_DeleteHook DeleteHook;
inline static bool AddDeleteHook(DeleteHook hook) {
return MallocHook_AddDeleteHook(hook);
}
inline static bool RemoveDeleteHook(DeleteHook hook) {
return MallocHook_RemoveDeleteHook(hook);
}
inline static void InvokeDeleteHook(const void* p);
// The PreMmapHook is invoked with mmap or mmap64 arguments just
// before the call is actually made. Such a hook may be useful
// in memory limited contexts, to catch allocations that will exceed
// a memory limit, and take outside actions to increase that limit.
typedef MallocHook_PreMmapHook PreMmapHook;
inline static bool AddPreMmapHook(PreMmapHook hook) {
return MallocHook_AddPreMmapHook(hook);
}
inline static bool RemovePreMmapHook(PreMmapHook hook) {
return MallocHook_RemovePreMmapHook(hook);
}
inline static void InvokePreMmapHook(const void* start,
size_t size,
int protection,
int flags,
int fd,
off_t offset);
// The MmapReplacement is invoked after the PreMmapHook but before
// the call is actually made. The MmapReplacement should return true
// if it handled the call, or false if it is still necessary to
// call mmap/mmap64.
// This should be used only by experts, and users must be be
// extremely careful to avoid recursive calls to mmap. The replacement
// should be async signal safe.
// Only one MmapReplacement is supported. After setting an MmapReplacement
// you must call RemoveMmapReplacement before calling SetMmapReplacement
// again.
typedef MallocHook_MmapReplacement MmapReplacement;
inline static bool SetMmapReplacement(MmapReplacement hook) {
return MallocHook_SetMmapReplacement(hook);
}
inline static bool RemoveMmapReplacement(MmapReplacement hook) {
return MallocHook_RemoveMmapReplacement(hook);
}
inline static bool InvokeMmapReplacement(const void* start,
size_t size,
int protection,
int flags,
int fd,
off_t offset,
void** result);
// The MmapHook is invoked whenever a region of memory is mapped.
// It may be passed MAP_FAILED if the mmap failed.
typedef MallocHook_MmapHook MmapHook;
inline static bool AddMmapHook(MmapHook hook) {
return MallocHook_AddMmapHook(hook);
}
inline static bool RemoveMmapHook(MmapHook hook) {
return MallocHook_RemoveMmapHook(hook);
}
inline static void InvokeMmapHook(const void* result,
const void* start,
size_t size,
int protection,
int flags,
int fd,
off_t offset);
// The MunmapReplacement is invoked with munmap arguments just before
// the call is actually made. The MunmapReplacement should return true
// if it handled the call, or false if it is still necessary to
// call munmap.
// This should be used only by experts. The replacement should be
// async signal safe.
// Only one MunmapReplacement is supported. After setting an
// MunmapReplacement you must call RemoveMunmapReplacement before
// calling SetMunmapReplacement again.
typedef MallocHook_MunmapReplacement MunmapReplacement;
inline static bool SetMunmapReplacement(MunmapReplacement hook) {
return MallocHook_SetMunmapReplacement(hook);
}
inline static bool RemoveMunmapReplacement(MunmapReplacement hook) {
return MallocHook_RemoveMunmapReplacement(hook);
}
inline static bool InvokeMunmapReplacement(const void* p,
size_t size,
int* result);
// The MunmapHook is invoked whenever a region of memory is unmapped.
typedef MallocHook_MunmapHook MunmapHook;
inline static bool AddMunmapHook(MunmapHook hook) {
return MallocHook_AddMunmapHook(hook);
}
inline static bool RemoveMunmapHook(MunmapHook hook) {
return MallocHook_RemoveMunmapHook(hook);
}
inline static void InvokeMunmapHook(const void* p, size_t size);
// The MremapHook is invoked whenever a region of memory is remapped.
typedef MallocHook_MremapHook MremapHook;
inline static bool AddMremapHook(MremapHook hook) {
return MallocHook_AddMremapHook(hook);
}
inline static bool RemoveMremapHook(MremapHook hook) {
return MallocHook_RemoveMremapHook(hook);
}
inline static void InvokeMremapHook(const void* result,
const void* old_addr,
size_t old_size,
size_t new_size,
int flags,
const void* new_addr);
// The PreSbrkHook is invoked just before sbrk is called -- except when
// the increment is 0. This is because sbrk(0) is often called
// to get the top of the memory stack, and is not actually a
// memory-allocation call. It may be useful in memory-limited contexts,
// to catch allocations that will exceed the limit and take outside
// actions to increase such a limit.
typedef MallocHook_PreSbrkHook PreSbrkHook;
inline static bool AddPreSbrkHook(PreSbrkHook hook) {
return MallocHook_AddPreSbrkHook(hook);
}
inline static bool RemovePreSbrkHook(PreSbrkHook hook) {
return MallocHook_RemovePreSbrkHook(hook);
}
inline static void InvokePreSbrkHook(ptrdiff_t increment);
// The SbrkHook is invoked whenever sbrk is called -- except when
// the increment is 0. This is because sbrk(0) is often called
// to get the top of the memory stack, and is not actually a
// memory-allocation call.
typedef MallocHook_SbrkHook SbrkHook;
inline static bool AddSbrkHook(SbrkHook hook) {
return MallocHook_AddSbrkHook(hook);
}
inline static bool RemoveSbrkHook(SbrkHook hook) {
return MallocHook_RemoveSbrkHook(hook);
}
inline static void InvokeSbrkHook(const void* result, ptrdiff_t increment);
// Get the current stack trace. Try to skip all routines up to and
// and including the caller of MallocHook::Invoke*.
// Use "skip_count" (similarly to GetStackTrace from stacktrace.h)
// as a hint about how many routines to skip if better information
// is not available.
inline static int GetCallerStackTrace(void** result, int max_depth,
int skip_count) {
return MallocHook_GetCallerStackTrace(result, max_depth, skip_count);
}
// Unhooked versions of mmap() and munmap(). These should be used
// only by experts, since they bypass heapchecking, etc.
// Note: These do not run hooks, but they still use the MmapReplacement
// and MunmapReplacement.
static void* UnhookedMMap(void *start, size_t length, int prot, int flags,
int fd, off_t offset);
static int UnhookedMUnmap(void *start, size_t length);
// The following are DEPRECATED.
inline static NewHook GetNewHook();
inline static NewHook SetNewHook(NewHook hook) {
return MallocHook_SetNewHook(hook);
}
inline static DeleteHook GetDeleteHook();
inline static DeleteHook SetDeleteHook(DeleteHook hook) {
return MallocHook_SetDeleteHook(hook);
}
inline static PreMmapHook GetPreMmapHook();
inline static PreMmapHook SetPreMmapHook(PreMmapHook hook) {
return MallocHook_SetPreMmapHook(hook);
}
inline static MmapHook GetMmapHook();
inline static MmapHook SetMmapHook(MmapHook hook) {
return MallocHook_SetMmapHook(hook);
}
inline static MunmapHook GetMunmapHook();
inline static MunmapHook SetMunmapHook(MunmapHook hook) {
return MallocHook_SetMunmapHook(hook);
}
inline static MremapHook GetMremapHook();
inline static MremapHook SetMremapHook(MremapHook hook) {
return MallocHook_SetMremapHook(hook);
}
inline static PreSbrkHook GetPreSbrkHook();
inline static PreSbrkHook SetPreSbrkHook(PreSbrkHook hook) {
return MallocHook_SetPreSbrkHook(hook);
}
inline static SbrkHook GetSbrkHook();
inline static SbrkHook SetSbrkHook(SbrkHook hook) {
return MallocHook_SetSbrkHook(hook);
}
// End of DEPRECATED methods.
private:
// Slow path versions of Invoke*Hook.
static void InvokeNewHookSlow(const void* p, size_t s);
static void InvokeDeleteHookSlow(const void* p);
static void InvokePreMmapHookSlow(const void* start,
size_t size,
int protection,
int flags,
int fd,
off_t offset);
static void InvokeMmapHookSlow(const void* result,
const void* start,
size_t size,
int protection,
int flags,
int fd,
off_t offset);
static bool InvokeMmapReplacementSlow(const void* start,
size_t size,
int protection,
int flags,
int fd,
off_t offset,
void** result);
static void InvokeMunmapHookSlow(const void* p, size_t size);
static bool InvokeMunmapReplacementSlow(const void* p,
size_t size,
int* result);
static void InvokeMremapHookSlow(const void* result,
const void* old_addr,
size_t old_size,
size_t new_size,
int flags,
const void* new_addr);
static void InvokePreSbrkHookSlow(ptrdiff_t increment);
static void InvokeSbrkHookSlow(const void* result, ptrdiff_t increment);
};
#ifdef _MSC_VER
#pragma warning(pop)
#endif
#endif /* _MALLOC_HOOK_H_ */

View file

@ -0,0 +1,173 @@
/* Copyright (c) 2008, 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: Craig Silverstein
*
* C shims for the C++ malloc_hook.h. See malloc_hook.h for details
* on how to use these.
*/
#ifndef _MALLOC_HOOK_C_H_
#define _MALLOC_HOOK_C_H_
#include <stddef.h>
#include <sys/types.h>
/* Annoying stuff for windows; makes sure clients can import these functions */
#ifndef PERFTOOLS_DLL_DECL
# ifdef _WIN32
# define PERFTOOLS_DLL_DECL __declspec(dllimport)
# else
# define PERFTOOLS_DLL_DECL
# endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
/* Get the current stack trace. Try to skip all routines up to and
* and including the caller of MallocHook::Invoke*.
* Use "skip_count" (similarly to GetStackTrace from stacktrace.h)
* as a hint about how many routines to skip if better information
* is not available.
*/
PERFTOOLS_DLL_DECL
int MallocHook_GetCallerStackTrace(void** result, int max_depth,
int skip_count);
/* The MallocHook_{Add,Remove}*Hook functions return 1 on success and 0 on
* failure.
*/
typedef void (*MallocHook_NewHook)(const void* ptr, size_t size);
PERFTOOLS_DLL_DECL
int MallocHook_AddNewHook(MallocHook_NewHook hook);
PERFTOOLS_DLL_DECL
int MallocHook_RemoveNewHook(MallocHook_NewHook hook);
typedef void (*MallocHook_DeleteHook)(const void* ptr);
PERFTOOLS_DLL_DECL
int MallocHook_AddDeleteHook(MallocHook_DeleteHook hook);
PERFTOOLS_DLL_DECL
int MallocHook_RemoveDeleteHook(MallocHook_DeleteHook hook);
typedef void (*MallocHook_PreMmapHook)(const void *start,
size_t size,
int protection,
int flags,
int fd,
off_t offset);
PERFTOOLS_DLL_DECL
int MallocHook_AddPreMmapHook(MallocHook_PreMmapHook hook);
PERFTOOLS_DLL_DECL
int MallocHook_RemovePreMmapHook(MallocHook_PreMmapHook hook);
typedef void (*MallocHook_MmapHook)(const void* result,
const void* start,
size_t size,
int protection,
int flags,
int fd,
off_t offset);
PERFTOOLS_DLL_DECL
int MallocHook_AddMmapHook(MallocHook_MmapHook hook);
PERFTOOLS_DLL_DECL
int MallocHook_RemoveMmapHook(MallocHook_MmapHook hook);
typedef int (*MallocHook_MmapReplacement)(const void* start,
size_t size,
int protection,
int flags,
int fd,
off_t offset,
void** result);
int MallocHook_SetMmapReplacement(MallocHook_MmapReplacement hook);
int MallocHook_RemoveMmapReplacement(MallocHook_MmapReplacement hook);
typedef void (*MallocHook_MunmapHook)(const void* ptr, size_t size);
PERFTOOLS_DLL_DECL
int MallocHook_AddMunmapHook(MallocHook_MunmapHook hook);
PERFTOOLS_DLL_DECL
int MallocHook_RemoveMunmapHook(MallocHook_MunmapHook hook);
typedef int (*MallocHook_MunmapReplacement)(const void* ptr,
size_t size,
int* result);
int MallocHook_SetMunmapReplacement(MallocHook_MunmapReplacement hook);
int MallocHook_RemoveMunmapReplacement(MallocHook_MunmapReplacement hook);
typedef void (*MallocHook_MremapHook)(const void* result,
const void* old_addr,
size_t old_size,
size_t new_size,
int flags,
const void* new_addr);
PERFTOOLS_DLL_DECL
int MallocHook_AddMremapHook(MallocHook_MremapHook hook);
PERFTOOLS_DLL_DECL
int MallocHook_RemoveMremapHook(MallocHook_MremapHook hook);
typedef void (*MallocHook_PreSbrkHook)(ptrdiff_t increment);
PERFTOOLS_DLL_DECL
int MallocHook_AddPreSbrkHook(MallocHook_PreSbrkHook hook);
PERFTOOLS_DLL_DECL
int MallocHook_RemovePreSbrkHook(MallocHook_PreSbrkHook hook);
typedef void (*MallocHook_SbrkHook)(const void* result, ptrdiff_t increment);
PERFTOOLS_DLL_DECL
int MallocHook_AddSbrkHook(MallocHook_SbrkHook hook);
PERFTOOLS_DLL_DECL
int MallocHook_RemoveSbrkHook(MallocHook_SbrkHook hook);
/* The following are DEPRECATED. */
PERFTOOLS_DLL_DECL
MallocHook_NewHook MallocHook_SetNewHook(MallocHook_NewHook hook);
PERFTOOLS_DLL_DECL
MallocHook_DeleteHook MallocHook_SetDeleteHook(MallocHook_DeleteHook hook);
PERFTOOLS_DLL_DECL
MallocHook_PreMmapHook MallocHook_SetPreMmapHook(MallocHook_PreMmapHook hook);
PERFTOOLS_DLL_DECL
MallocHook_MmapHook MallocHook_SetMmapHook(MallocHook_MmapHook hook);
PERFTOOLS_DLL_DECL
MallocHook_MunmapHook MallocHook_SetMunmapHook(MallocHook_MunmapHook hook);
PERFTOOLS_DLL_DECL
MallocHook_MremapHook MallocHook_SetMremapHook(MallocHook_MremapHook hook);
PERFTOOLS_DLL_DECL
MallocHook_PreSbrkHook MallocHook_SetPreSbrkHook(MallocHook_PreSbrkHook hook);
PERFTOOLS_DLL_DECL
MallocHook_SbrkHook MallocHook_SetSbrkHook(MallocHook_SbrkHook hook);
/* End of DEPRECATED functions. */
#ifdef __cplusplus
} // extern "C"
#endif
#endif /* _MALLOC_HOOK_C_H_ */

View file

@ -0,0 +1,37 @@
#ifndef _NALLOCX_H_
#define _NALLOCX_H_
#include <stddef.h>
#ifndef PERFTOOLS_DLL_DECL
# ifdef _WIN32
# define PERFTOOLS_DLL_DECL __declspec(dllimport)
# else
# define PERFTOOLS_DLL_DECL
# endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
#define MALLOCX_LG_ALIGN(la) ((int)(la))
/*
* The nallocx function allocates no memory, but it performs the same size
* computation as the malloc function, and returns the real size of the
* allocation that would result from the equivalent malloc function call.
* nallocx is a malloc extension originally implemented by jemalloc:
* http://www.unix.com/man-page/freebsd/3/nallocx/
*
* Note, we only support MALLOCX_LG_ALIGN flag and nothing else.
*/
PERFTOOLS_DLL_DECL size_t nallocx(size_t size, int flags);
/* same as above but never weak */
PERFTOOLS_DLL_DECL size_t tc_nallocx(size_t size, int flags);
#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* _NALLOCX_H_ */

View file

@ -0,0 +1,173 @@
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/* Copyright (c) 2005, 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
*
* Module for CPU profiling based on periodic pc-sampling.
*
* For full(er) information, see docs/cpuprofile.html
*
* This module is linked into your program with
* no slowdown caused by this unless you activate the profiler
* using one of the following methods:
*
* 1. Before starting the program, set the environment variable
* "CPUPROFILE" to be the name of the file to which the profile
* data should be written.
*
* 2. Programmatically, start and stop the profiler using the
* routines "ProfilerStart(filename)" and "ProfilerStop()".
*
*
* (Note: if using linux 2.4 or earlier, only the main thread may be
* profiled.)
*
* Use pprof to view the resulting profile output.
* % pprof <path_to_executable> <profile_file_name>
* % pprof --gv <path_to_executable> <profile_file_name>
*
* These functions are thread-safe.
*/
#ifndef BASE_PROFILER_H_
#define BASE_PROFILER_H_
#include <time.h> /* For time_t */
/* Annoying stuff for windows; makes sure clients can import these functions */
#ifndef PERFTOOLS_DLL_DECL
# ifdef _WIN32
# define PERFTOOLS_DLL_DECL __declspec(dllimport)
# else
# define PERFTOOLS_DLL_DECL
# endif
#endif
/* All this code should be usable from within C apps. */
#ifdef __cplusplus
extern "C" {
#endif
/* Profiler options, for use with ProfilerStartWithOptions. To use:
*
* struct ProfilerOptions options;
* memset(&options, 0, sizeof options);
*
* then fill in fields as needed.
*
* This structure is intended to be usable from C code, so no constructor
* is provided to initialize it. (Use memset as described above).
*/
struct ProfilerOptions {
/* Filter function and argument.
*
* If filter_in_thread is not NULL, when a profiling tick is delivered
* the profiler will call:
*
* (*filter_in_thread)(filter_in_thread_arg)
*
* If it returns nonzero, the sample will be included in the profile.
* Note that filter_in_thread runs in a signal handler, so must be
* async-signal-safe.
*
* A typical use would be to set up filter results for each thread
* in the system before starting the profiler, then to make
* filter_in_thread be a very simple function which retrieves those
* results in an async-signal-safe way. Retrieval could be done
* using thread-specific data, or using a shared data structure that
* supports async-signal-safe lookups.
*/
int (*filter_in_thread)(void *arg);
void *filter_in_thread_arg;
};
/* Start profiling and write profile info into fname, discarding any
* existing profiling data in that file.
*
* This is equivalent to calling ProfilerStartWithOptions(fname, NULL).
*/
PERFTOOLS_DLL_DECL int ProfilerStart(const char* fname);
/* Start profiling and write profile into fname, discarding any
* existing profiling data in that file.
*
* The profiler is configured using the options given by 'options'.
* Options which are not specified are given default values.
*
* 'options' may be NULL, in which case all are given default values.
*
* Returns nonzero if profiling was started successfully, or zero else.
*/
PERFTOOLS_DLL_DECL int ProfilerStartWithOptions(
const char *fname, const struct ProfilerOptions *options);
/* Stop profiling. Can be started again with ProfilerStart(), but
* the currently accumulated profiling data will be cleared.
*/
PERFTOOLS_DLL_DECL void ProfilerStop(void);
/* Flush any currently buffered profiling state to the profile file.
* Has no effect if the profiler has not been started.
*/
PERFTOOLS_DLL_DECL void ProfilerFlush(void);
/* DEPRECATED: these functions were used to enable/disable profiling
* in the current thread, but no longer do anything.
*/
PERFTOOLS_DLL_DECL void ProfilerEnable(void);
PERFTOOLS_DLL_DECL void ProfilerDisable(void);
/* Returns nonzero if profile is currently enabled, zero if it's not. */
PERFTOOLS_DLL_DECL int ProfilingIsEnabledForAllThreads(void);
/* Routine for registering new threads with the profiler.
*/
PERFTOOLS_DLL_DECL void ProfilerRegisterThread(void);
/* Stores state about profiler's current status into "*state". */
struct ProfilerState {
int enabled; /* Is profiling currently enabled? */
time_t start_time; /* If enabled, when was profiling started? */
char profile_name[1024]; /* Name of profile file being written, or '\0' */
int samples_gathered; /* Number of samples gathered so far (or 0) */
};
PERFTOOLS_DLL_DECL void ProfilerGetCurrentState(struct ProfilerState* state);
/* Returns the current stack trace, to be called from a SIGPROF handler. */
PERFTOOLS_DLL_DECL int ProfilerGetStackTrace(
void** result, int max_depth, int skip_count, const void *uc);
#ifdef __cplusplus
} // extern "C"
#endif
#endif /* BASE_PROFILER_H_ */

View file

@ -0,0 +1,117 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2005, 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
//
// Routines to extract the current stack trace. These functions are
// thread-safe.
#ifndef GOOGLE_STACKTRACE_H_
#define GOOGLE_STACKTRACE_H_
// Annoying stuff for windows -- makes sure clients can import these functions
#ifndef PERFTOOLS_DLL_DECL
# ifdef _WIN32
# define PERFTOOLS_DLL_DECL __declspec(dllimport)
# else
# define PERFTOOLS_DLL_DECL
# endif
#endif
// Skips the most recent "skip_count" stack frames (also skips the
// frame generated for the "GetStackFrames" routine itself), and then
// records the pc values for up to the next "max_depth" frames in
// "result", and the corresponding stack frame sizes in "sizes".
// Returns the number of values recorded in "result"/"sizes".
//
// Example:
// main() { foo(); }
// foo() { bar(); }
// bar() {
// void* result[10];
// int sizes[10];
// int depth = GetStackFrames(result, sizes, 10, 1);
// }
//
// The GetStackFrames call will skip the frame for "bar". It will
// return 2 and will produce pc values that map to the following
// procedures:
// result[0] foo
// result[1] main
// (Actually, there may be a few more entries after "main" to account for
// startup procedures.)
// And corresponding stack frame sizes will also be recorded:
// sizes[0] 16
// sizes[1] 16
// (Stack frame sizes of 16 above are just for illustration purposes.)
// Stack frame sizes of 0 or less indicate that those frame sizes couldn't
// be identified.
//
// This routine may return fewer stack frame entries than are
// available. Also note that "result" and "sizes" must both be non-NULL.
extern PERFTOOLS_DLL_DECL int GetStackFrames(void** result, int* sizes, int max_depth,
int skip_count);
// Same as above, but to be used from a signal handler. The "uc" parameter
// should be the pointer to ucontext_t which was passed as the 3rd parameter
// to sa_sigaction signal handler. It may help the unwinder to get a
// better stack trace under certain conditions. The "uc" may safely be NULL.
extern PERFTOOLS_DLL_DECL int GetStackFramesWithContext(void** result, int* sizes, int max_depth,
int skip_count, const void *uc);
// This is similar to the GetStackFrames routine, except that it returns
// the stack trace only, and not the stack frame sizes as well.
// Example:
// main() { foo(); }
// foo() { bar(); }
// bar() {
// void* result[10];
// int depth = GetStackTrace(result, 10, 1);
// }
//
// This produces:
// result[0] foo
// result[1] main
// .... ...
//
// "result" must not be NULL.
extern PERFTOOLS_DLL_DECL int GetStackTrace(void** result, int max_depth,
int skip_count);
// Same as above, but to be used from a signal handler. The "uc" parameter
// should be the pointer to ucontext_t which was passed as the 3rd parameter
// to sa_sigaction signal handler. It may help the unwinder to get a
// better stack trace under certain conditions. The "uc" may safely be NULL.
extern PERFTOOLS_DLL_DECL int GetStackTraceWithContext(void** result, int max_depth,
int skip_count, const void *uc);
#endif /* GOOGLE_STACKTRACE_H_ */

View file

@ -0,0 +1,163 @@
/* -*- Mode: C; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/* Copyright (c) 2003, 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 <opensource@google.com>
* .h file by Craig Silverstein <opensource@google.com>
*/
#ifndef TCMALLOC_TCMALLOC_H_
#define TCMALLOC_TCMALLOC_H_
#include <stddef.h> /* for size_t */
#ifdef __cplusplus
#include <new> /* for std::nothrow_t, std::align_val_t */
#endif
/* Define the version number so folks can check against it */
#define TC_VERSION_MAJOR @TC_VERSION_MAJOR@
#define TC_VERSION_MINOR @TC_VERSION_MINOR@
#define TC_VERSION_PATCH "@TC_VERSION_PATCH@"
#define TC_VERSION_STRING "gperftools @TC_VERSION_MAJOR@.@TC_VERSION_MINOR@@TC_VERSION_PATCH@"
/* For struct mallinfo, if it's defined. */
#if @ac_cv_have_struct_mallinfo@
# include <malloc.h>
#endif
#ifndef PERFTOOLS_NOTHROW
#if __cplusplus >= 201103L
#define PERFTOOLS_NOTHROW noexcept
#elif defined(__cplusplus)
#define PERFTOOLS_NOTHROW throw()
#else
# ifdef __GNUC__
# define PERFTOOLS_NOTHROW __attribute__((__nothrow__))
# else
# define PERFTOOLS_NOTHROW
# endif
#endif
#endif
#ifndef PERFTOOLS_DLL_DECL
# ifdef _WIN32
# define PERFTOOLS_DLL_DECL __declspec(dllimport)
# else
# define PERFTOOLS_DLL_DECL
# endif
#endif
#ifdef __cplusplus
extern "C" {
#endif
/*
* Returns a human-readable version string. If major, minor,
* and/or patch are not NULL, they are set to the major version,
* minor version, and patch-code (a string, usually "").
*/
PERFTOOLS_DLL_DECL const char* tc_version(int* major, int* minor,
const char** patch) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_malloc(size_t size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_malloc_skip_new_handler(size_t size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_free(void* ptr) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_free_sized(void *ptr, size_t size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_realloc(void* ptr, size_t size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_calloc(size_t nmemb, size_t size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_cfree(void* ptr) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_memalign(size_t __alignment,
size_t __size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL int tc_posix_memalign(void** ptr,
size_t align, size_t size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_valloc(size_t __size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_pvalloc(size_t __size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_malloc_stats(void) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL int tc_mallopt(int cmd, int value) PERFTOOLS_NOTHROW;
#if @ac_cv_have_struct_mallinfo@
PERFTOOLS_DLL_DECL struct mallinfo tc_mallinfo(void) PERFTOOLS_NOTHROW;
#endif
/*
* This is an alias for MallocExtension::instance()->GetAllocatedSize().
* It is equivalent to
* OS X: malloc_size()
* glibc: malloc_usable_size()
* Windows: _msize()
*/
PERFTOOLS_DLL_DECL size_t tc_malloc_size(void* ptr) PERFTOOLS_NOTHROW;
#ifdef __cplusplus
PERFTOOLS_DLL_DECL int tc_set_new_mode(int flag) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_new(size_t size);
PERFTOOLS_DLL_DECL void* tc_new_nothrow(size_t size,
const std::nothrow_t&) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_delete(void* p) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_delete_sized(void* p, size_t size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_delete_nothrow(void* p,
const std::nothrow_t&) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_newarray(size_t size);
PERFTOOLS_DLL_DECL void* tc_newarray_nothrow(size_t size,
const std::nothrow_t&) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_deletearray(void* p) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_deletearray_sized(void* p, size_t size) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_deletearray_nothrow(void* p,
const std::nothrow_t&) PERFTOOLS_NOTHROW;
#if @ac_cv_have_std_align_val_t@ && __cplusplus >= 201703L
PERFTOOLS_DLL_DECL void* tc_new_aligned(size_t size, std::align_val_t al);
PERFTOOLS_DLL_DECL void* tc_new_aligned_nothrow(size_t size, std::align_val_t al,
const std::nothrow_t&) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_delete_aligned(void* p, std::align_val_t al) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_delete_sized_aligned(void* p, size_t size, std::align_val_t al) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_delete_aligned_nothrow(void* p, std::align_val_t al,
const std::nothrow_t&) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void* tc_newarray_aligned(size_t size, std::align_val_t al);
PERFTOOLS_DLL_DECL void* tc_newarray_aligned_nothrow(size_t size, std::align_val_t al,
const std::nothrow_t&) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_deletearray_aligned(void* p, std::align_val_t al) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_deletearray_sized_aligned(void* p, size_t size, std::align_val_t al) PERFTOOLS_NOTHROW;
PERFTOOLS_DLL_DECL void tc_deletearray_aligned_nothrow(void* p, std::align_val_t al,
const std::nothrow_t&) PERFTOOLS_NOTHROW;
#endif
}
#endif
/* We're only un-defining for public */
#if !defined(GPERFTOOLS_CONFIG_H_)
#undef PERFTOOLS_NOTHROW
#endif /* GPERFTOOLS_CONFIG_H_ */
#endif /* #ifndef TCMALLOC_TCMALLOC_H_ */

View file

@ -0,0 +1,93 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2005, 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.
// ---
// All Rights Reserved.
//
// Author: Maxim Lifantsev
//
// A file to ensure that components of heap leak checker run before
// all global object constructors and after all global object
// destructors.
//
// This file must be the last library any binary links against.
// Otherwise, the heap checker may not be able to run early enough to
// catalog all the global objects in your program. If this happens,
// and later in the program you allocate memory and have one of these
// "uncataloged" global objects point to it, the heap checker will
// consider that allocation to be a leak, even though it's not (since
// the allocated object is reachable from global data and hence "live").
#include <stdlib.h> // for abort()
#include <gperftools/malloc_extension.h>
// A dummy variable to refer from heap-checker.cc. This is to make
// sure this file is not optimized out by the linker.
bool heap_leak_checker_bcad_variable;
extern void HeapLeakChecker_AfterDestructors(); // in heap-checker.cc
// A helper class to ensure that some components of heap leak checking
// can happen before construction and after destruction
// of all global/static objects.
class HeapLeakCheckerGlobalPrePost {
public:
HeapLeakCheckerGlobalPrePost() {
if (count_ == 0) {
// The 'new int' will ensure that we have run an initial malloc
// hook, which will set up the heap checker via
// MallocHook_InitAtFirstAllocation_HeapLeakChecker. See malloc_hook.cc.
// This is done in this roundabout fashion in order to avoid self-deadlock
// if we directly called HeapLeakChecker_BeforeConstructors here.
delete new int;
// This needs to be called before the first allocation of an STL
// object, but after libc is done setting up threads (because it
// calls setenv, which requires a thread-aware errno). By
// putting it here, we hope it's the first bit of code executed
// after the libc global-constructor code.
MallocExtension::Initialize();
}
++count_;
}
~HeapLeakCheckerGlobalPrePost() {
if (count_ <= 0) abort();
--count_;
if (count_ == 0) HeapLeakChecker_AfterDestructors();
}
private:
// Counter of constructions/destructions of objects of this class
// (just in case there are more than one of them).
static int count_;
};
int HeapLeakCheckerGlobalPrePost::count_ = 0;
// The early-construction/late-destruction global object.
static const HeapLeakCheckerGlobalPrePost heap_leak_checker_global_pre_post;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,78 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2013, 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.
// This file defines structs to accumulate memory allocation and deallocation
// counts. These structs are commonly used for malloc (in HeapProfileTable)
// and mmap (in MemoryRegionMap).
// A bucket is data structure for heap profiling to store a pair of a stack
// trace and counts of (de)allocation. Buckets are stored in a hash table
// which is declared as "HeapProfileBucket**".
//
// A hash value is computed from a stack trace. Collision in the hash table
// is resolved by separate chaining with linked lists. The links in the list
// are implemented with the member "HeapProfileBucket* next".
//
// A structure of a hash table HeapProfileBucket** bucket_table would be like:
// bucket_table[0] => NULL
// bucket_table[1] => HeapProfileBucket() => HeapProfileBucket() => NULL
// ...
// bucket_table[i] => HeapProfileBucket() => NULL
// ...
// bucket_table[n] => HeapProfileBucket() => NULL
#ifndef HEAP_PROFILE_STATS_H_
#define HEAP_PROFILE_STATS_H_
struct HeapProfileStats {
// Returns true if the two HeapProfileStats are semantically equal.
bool Equivalent(const HeapProfileStats& other) const {
return allocs - frees == other.allocs - other.frees &&
alloc_size - free_size == other.alloc_size - other.free_size;
}
int32 allocs; // Number of allocation calls.
int32 frees; // Number of free calls.
int64 alloc_size; // Total size of all allocated objects so far.
int64 free_size; // Total size of all freed objects so far.
};
// Allocation and deallocation statistics per each stack trace.
struct HeapProfileBucket : public HeapProfileStats {
// Longest stack trace we record.
static const int kMaxStackDepth = 32;
uintptr_t hash; // Hash value of the stack trace.
int depth; // Depth of stack trace.
const void** stack; // Stack trace.
HeapProfileBucket* next; // Next entry in hash-table.
};
#endif // HEAP_PROFILE_STATS_H_

View file

@ -0,0 +1,629 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2006, 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
// Maxim Lifantsev (refactoring)
//
#include <config.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h> // for write()
#endif
#include <fcntl.h> // for open()
#ifdef HAVE_GLOB_H
#include <glob.h>
#ifndef GLOB_NOMATCH // true on some old cygwins
# define GLOB_NOMATCH 0
#endif
#endif
#ifdef HAVE_INTTYPES_H
#include <inttypes.h> // for PRIxPTR
#endif
#ifdef HAVE_POLL_H
#include <poll.h>
#endif
#include <errno.h>
#include <stdarg.h>
#include <string>
#include <map>
#include <algorithm> // for sort(), equal(), and copy()
#include "heap-profile-table.h"
#include "base/logging.h"
#include "raw_printer.h"
#include "symbolize.h"
#include <gperftools/stacktrace.h>
#include <gperftools/malloc_hook.h>
#include "memory_region_map.h"
#include "base/commandlineflags.h"
#include "base/logging.h" // for the RawFD I/O commands
#include "base/sysinfo.h"
using std::sort;
using std::equal;
using std::copy;
using std::string;
using std::map;
using tcmalloc::FillProcSelfMaps; // from sysinfo.h
using tcmalloc::DumpProcSelfMaps; // from sysinfo.h
//----------------------------------------------------------------------
DEFINE_bool(cleanup_old_heap_profiles,
EnvToBool("HEAP_PROFILE_CLEANUP", true),
"At initialization time, delete old heap profiles.");
DEFINE_int32(heap_check_max_leaks,
EnvToInt("HEAP_CHECK_MAX_LEAKS", 20),
"The maximum number of leak reports to print.");
//----------------------------------------------------------------------
// header of the dumped heap profile
static const char kProfileHeader[] = "heap profile: ";
static const char kProcSelfMapsHeader[] = "\nMAPPED_LIBRARIES:\n";
//----------------------------------------------------------------------
const char HeapProfileTable::kFileExt[] = ".heap";
//----------------------------------------------------------------------
static const int kHashTableSize = 179999; // Size for bucket_table_.
/*static*/ const int HeapProfileTable::kMaxStackDepth;
//----------------------------------------------------------------------
// We strip out different number of stack frames in debug mode
// because less inlining happens in that case
#ifdef NDEBUG
static const int kStripFrames = 2;
#else
static const int kStripFrames = 3;
#endif
// For sorting Stats or Buckets by in-use space
static bool ByAllocatedSpace(HeapProfileTable::Stats* a,
HeapProfileTable::Stats* b) {
// Return true iff "a" has more allocated space than "b"
return (a->alloc_size - a->free_size) > (b->alloc_size - b->free_size);
}
//----------------------------------------------------------------------
HeapProfileTable::HeapProfileTable(Allocator alloc,
DeAllocator dealloc,
bool profile_mmap)
: alloc_(alloc),
dealloc_(dealloc),
profile_mmap_(profile_mmap),
bucket_table_(NULL),
num_buckets_(0),
address_map_(NULL) {
// Make a hash table for buckets.
const int table_bytes = kHashTableSize * sizeof(*bucket_table_);
bucket_table_ = static_cast<Bucket**>(alloc_(table_bytes));
memset(bucket_table_, 0, table_bytes);
// Make an allocation map.
address_map_ =
new(alloc_(sizeof(AllocationMap))) AllocationMap(alloc_, dealloc_);
// Initialize.
memset(&total_, 0, sizeof(total_));
num_buckets_ = 0;
}
HeapProfileTable::~HeapProfileTable() {
// Free the allocation map.
address_map_->~AllocationMap();
dealloc_(address_map_);
address_map_ = NULL;
// Free the hash table.
for (int i = 0; i < kHashTableSize; i++) {
for (Bucket* curr = bucket_table_[i]; curr != 0; /**/) {
Bucket* bucket = curr;
curr = curr->next;
dealloc_(bucket->stack);
dealloc_(bucket);
}
}
dealloc_(bucket_table_);
bucket_table_ = NULL;
}
HeapProfileTable::Bucket* HeapProfileTable::GetBucket(int depth,
const void* const key[]) {
// Make hash-value
uintptr_t h = 0;
for (int i = 0; i < depth; i++) {
h += reinterpret_cast<uintptr_t>(key[i]);
h += h << 10;
h ^= h >> 6;
}
h += h << 3;
h ^= h >> 11;
// Lookup stack trace in table
unsigned int buck = ((unsigned int) h) % kHashTableSize;
for (Bucket* b = bucket_table_[buck]; b != 0; b = b->next) {
if ((b->hash == h) &&
(b->depth == depth) &&
equal(key, key + depth, b->stack)) {
return b;
}
}
// Create new bucket
const size_t key_size = sizeof(key[0]) * depth;
const void** kcopy = reinterpret_cast<const void**>(alloc_(key_size));
copy(key, key + depth, kcopy);
Bucket* b = reinterpret_cast<Bucket*>(alloc_(sizeof(Bucket)));
memset(b, 0, sizeof(*b));
b->hash = h;
b->depth = depth;
b->stack = kcopy;
b->next = bucket_table_[buck];
bucket_table_[buck] = b;
num_buckets_++;
return b;
}
int HeapProfileTable::GetCallerStackTrace(
int skip_count, void* stack[kMaxStackDepth]) {
return MallocHook::GetCallerStackTrace(
stack, kMaxStackDepth, kStripFrames + skip_count + 1);
}
void HeapProfileTable::RecordAlloc(
const void* ptr, size_t bytes, int stack_depth,
const void* const call_stack[]) {
Bucket* b = GetBucket(stack_depth, call_stack);
b->allocs++;
b->alloc_size += bytes;
total_.allocs++;
total_.alloc_size += bytes;
AllocValue v;
v.set_bucket(b); // also did set_live(false); set_ignore(false)
v.bytes = bytes;
address_map_->Insert(ptr, v);
}
void HeapProfileTable::RecordFree(const void* ptr) {
AllocValue v;
if (address_map_->FindAndRemove(ptr, &v)) {
Bucket* b = v.bucket();
b->frees++;
b->free_size += v.bytes;
total_.frees++;
total_.free_size += v.bytes;
}
}
bool HeapProfileTable::FindAlloc(const void* ptr, size_t* object_size) const {
const AllocValue* alloc_value = address_map_->Find(ptr);
if (alloc_value != NULL) *object_size = alloc_value->bytes;
return alloc_value != NULL;
}
bool HeapProfileTable::FindAllocDetails(const void* ptr,
AllocInfo* info) const {
const AllocValue* alloc_value = address_map_->Find(ptr);
if (alloc_value != NULL) {
info->object_size = alloc_value->bytes;
info->call_stack = alloc_value->bucket()->stack;
info->stack_depth = alloc_value->bucket()->depth;
}
return alloc_value != NULL;
}
bool HeapProfileTable::FindInsideAlloc(const void* ptr,
size_t max_size,
const void** object_ptr,
size_t* object_size) const {
const AllocValue* alloc_value =
address_map_->FindInside(&AllocValueSize, max_size, ptr, object_ptr);
if (alloc_value != NULL) *object_size = alloc_value->bytes;
return alloc_value != NULL;
}
bool HeapProfileTable::MarkAsLive(const void* ptr) {
AllocValue* alloc = address_map_->FindMutable(ptr);
if (alloc && !alloc->live()) {
alloc->set_live(true);
return true;
}
return false;
}
void HeapProfileTable::MarkAsIgnored(const void* ptr) {
AllocValue* alloc = address_map_->FindMutable(ptr);
if (alloc) {
alloc->set_ignore(true);
}
}
// We'd be happier using snprintfer, but we don't to reduce dependencies.
int HeapProfileTable::UnparseBucket(const Bucket& b,
char* buf, int buflen, int bufsize,
const char* extra,
Stats* profile_stats) {
if (profile_stats != NULL) {
profile_stats->allocs += b.allocs;
profile_stats->alloc_size += b.alloc_size;
profile_stats->frees += b.frees;
profile_stats->free_size += b.free_size;
}
int printed =
snprintf(buf + buflen, bufsize - buflen, "%6d: %8" PRId64 " [%6d: %8" PRId64 "] @%s",
b.allocs - b.frees,
b.alloc_size - b.free_size,
b.allocs,
b.alloc_size,
extra);
// If it looks like the snprintf failed, ignore the fact we printed anything
if (printed < 0 || printed >= bufsize - buflen) return buflen;
buflen += printed;
for (int d = 0; d < b.depth; d++) {
printed = snprintf(buf + buflen, bufsize - buflen, " 0x%08" PRIxPTR,
reinterpret_cast<uintptr_t>(b.stack[d]));
if (printed < 0 || printed >= bufsize - buflen) return buflen;
buflen += printed;
}
printed = snprintf(buf + buflen, bufsize - buflen, "\n");
if (printed < 0 || printed >= bufsize - buflen) return buflen;
buflen += printed;
return buflen;
}
HeapProfileTable::Bucket**
HeapProfileTable::MakeSortedBucketList() const {
Bucket** list = static_cast<Bucket**>(alloc_(sizeof(Bucket) * num_buckets_));
int bucket_count = 0;
for (int i = 0; i < kHashTableSize; i++) {
for (Bucket* curr = bucket_table_[i]; curr != 0; curr = curr->next) {
list[bucket_count++] = curr;
}
}
RAW_DCHECK(bucket_count == num_buckets_, "");
sort(list, list + num_buckets_, ByAllocatedSpace);
return list;
}
void HeapProfileTable::IterateOrderedAllocContexts(
AllocContextIterator callback) const {
Bucket** list = MakeSortedBucketList();
AllocContextInfo info;
for (int i = 0; i < num_buckets_; ++i) {
*static_cast<Stats*>(&info) = *static_cast<Stats*>(list[i]);
info.stack_depth = list[i]->depth;
info.call_stack = list[i]->stack;
callback(info);
}
dealloc_(list);
}
int HeapProfileTable::FillOrderedProfile(char buf[], int size) const {
Bucket** list = MakeSortedBucketList();
// Our file format is "bucket, bucket, ..., bucket, proc_self_maps_info".
// In the cases buf is too small, we'd rather leave out the last
// buckets than leave out the /proc/self/maps info. To ensure that,
// we actually print the /proc/self/maps info first, then move it to
// the end of the buffer, then write the bucket info into whatever
// is remaining, and then move the maps info one last time to close
// any gaps. Whew!
int map_length = snprintf(buf, size, "%s", kProcSelfMapsHeader);
if (map_length < 0 || map_length >= size) {
dealloc_(list);
return 0;
}
bool dummy; // "wrote_all" -- did /proc/self/maps fit in its entirety?
map_length += FillProcSelfMaps(buf + map_length, size - map_length, &dummy);
RAW_DCHECK(map_length <= size, "");
char* const map_start = buf + size - map_length; // move to end
memmove(map_start, buf, map_length);
size -= map_length;
Stats stats;
memset(&stats, 0, sizeof(stats));
int bucket_length = snprintf(buf, size, "%s", kProfileHeader);
if (bucket_length < 0 || bucket_length >= size) {
dealloc_(list);
return 0;
}
bucket_length = UnparseBucket(total_, buf, bucket_length, size,
" heapprofile", &stats);
// Dump the mmap list first.
if (profile_mmap_) {
BufferArgs buffer(buf, bucket_length, size);
MemoryRegionMap::IterateBuckets<BufferArgs*>(DumpBucketIterator, &buffer);
bucket_length = buffer.buflen;
}
for (int i = 0; i < num_buckets_; i++) {
bucket_length = UnparseBucket(*list[i], buf, bucket_length, size, "",
&stats);
}
RAW_DCHECK(bucket_length < size, "");
dealloc_(list);
RAW_DCHECK(buf + bucket_length <= map_start, "");
memmove(buf + bucket_length, map_start, map_length); // close the gap
return bucket_length + map_length;
}
// static
void HeapProfileTable::DumpBucketIterator(const Bucket* bucket,
BufferArgs* args) {
args->buflen = UnparseBucket(*bucket, args->buf, args->buflen, args->bufsize,
"", NULL);
}
inline
void HeapProfileTable::DumpNonLiveIterator(const void* ptr, AllocValue* v,
const DumpArgs& args) {
if (v->live()) {
v->set_live(false);
return;
}
if (v->ignore()) {
return;
}
Bucket b;
memset(&b, 0, sizeof(b));
b.allocs = 1;
b.alloc_size = v->bytes;
b.depth = v->bucket()->depth;
b.stack = v->bucket()->stack;
char buf[1024];
int len = UnparseBucket(b, buf, 0, sizeof(buf), "", args.profile_stats);
RawWrite(args.fd, buf, len);
}
// Callback from NonLiveSnapshot; adds entry to arg->dest
// if not the entry is not live and is not present in arg->base.
void HeapProfileTable::AddIfNonLive(const void* ptr, AllocValue* v,
AddNonLiveArgs* arg) {
if (v->live()) {
v->set_live(false);
} else {
if (arg->base != NULL && arg->base->map_.Find(ptr) != NULL) {
// Present in arg->base, so do not save
} else {
arg->dest->Add(ptr, *v);
}
}
}
bool HeapProfileTable::WriteProfile(const char* file_name,
const Bucket& total,
AllocationMap* allocations) {
RAW_VLOG(1, "Dumping non-live heap profile to %s", file_name);
RawFD fd = RawOpenForWriting(file_name);
if (fd == kIllegalRawFD) {
RAW_LOG(ERROR, "Failed dumping filtered heap profile to %s", file_name);
return false;
}
RawWrite(fd, kProfileHeader, strlen(kProfileHeader));
char buf[512];
int len = UnparseBucket(total, buf, 0, sizeof(buf), " heapprofile", NULL);
RawWrite(fd, buf, len);
const DumpArgs args(fd, NULL);
allocations->Iterate<const DumpArgs&>(DumpNonLiveIterator, args);
RawWrite(fd, kProcSelfMapsHeader, strlen(kProcSelfMapsHeader));
DumpProcSelfMaps(fd);
RawClose(fd);
return true;
}
void HeapProfileTable::CleanupOldProfiles(const char* prefix) {
if (!FLAGS_cleanup_old_heap_profiles)
return;
string pattern = string(prefix) + ".*" + kFileExt;
#if defined(HAVE_GLOB_H)
glob_t g;
const int r = glob(pattern.c_str(), GLOB_ERR, NULL, &g);
if (r == 0 || r == GLOB_NOMATCH) {
const int prefix_length = strlen(prefix);
for (int i = 0; i < g.gl_pathc; i++) {
const char* fname = g.gl_pathv[i];
if ((strlen(fname) >= prefix_length) &&
(memcmp(fname, prefix, prefix_length) == 0)) {
RAW_VLOG(1, "Removing old heap profile %s", fname);
unlink(fname);
}
}
}
globfree(&g);
#else /* HAVE_GLOB_H */
RAW_LOG(WARNING, "Unable to remove old heap profiles (can't run glob())");
#endif
}
HeapProfileTable::Snapshot* HeapProfileTable::TakeSnapshot() {
Snapshot* s = new (alloc_(sizeof(Snapshot))) Snapshot(alloc_, dealloc_);
address_map_->Iterate(AddToSnapshot, s);
return s;
}
void HeapProfileTable::ReleaseSnapshot(Snapshot* s) {
s->~Snapshot();
dealloc_(s);
}
// Callback from TakeSnapshot; adds a single entry to snapshot
void HeapProfileTable::AddToSnapshot(const void* ptr, AllocValue* v,
Snapshot* snapshot) {
snapshot->Add(ptr, *v);
}
HeapProfileTable::Snapshot* HeapProfileTable::NonLiveSnapshot(
Snapshot* base) {
RAW_VLOG(2, "NonLiveSnapshot input: %d %d\n",
int(total_.allocs - total_.frees),
int(total_.alloc_size - total_.free_size));
Snapshot* s = new (alloc_(sizeof(Snapshot))) Snapshot(alloc_, dealloc_);
AddNonLiveArgs args;
args.dest = s;
args.base = base;
address_map_->Iterate<AddNonLiveArgs*>(AddIfNonLive, &args);
RAW_VLOG(2, "NonLiveSnapshot output: %d %d\n",
int(s->total_.allocs - s->total_.frees),
int(s->total_.alloc_size - s->total_.free_size));
return s;
}
// Information kept per unique bucket seen
struct HeapProfileTable::Snapshot::Entry {
int count;
int bytes;
Bucket* bucket;
Entry() : count(0), bytes(0) { }
// Order by decreasing bytes
bool operator<(const Entry& x) const {
return this->bytes > x.bytes;
}
};
// State used to generate leak report. We keep a mapping from Bucket pointer
// the collected stats for that bucket.
struct HeapProfileTable::Snapshot::ReportState {
map<Bucket*, Entry> buckets_;
};
// Callback from ReportLeaks; updates ReportState.
void HeapProfileTable::Snapshot::ReportCallback(const void* ptr,
AllocValue* v,
ReportState* state) {
Entry* e = &state->buckets_[v->bucket()]; // Creates empty Entry first time
e->bucket = v->bucket();
e->count++;
e->bytes += v->bytes;
}
void HeapProfileTable::Snapshot::ReportLeaks(const char* checker_name,
const char* filename,
bool should_symbolize) {
// This is only used by the heap leak checker, but is intimately
// tied to the allocation map that belongs in this module and is
// therefore placed here.
RAW_LOG(ERROR, "Leak check %s detected leaks of %zu bytes "
"in %zu objects",
checker_name,
size_t(total_.alloc_size),
size_t(total_.allocs));
// Group objects by Bucket
ReportState state;
map_.Iterate(&ReportCallback, &state);
// Sort buckets by decreasing leaked size
const int n = state.buckets_.size();
Entry* entries = new Entry[n];
int dst = 0;
for (map<Bucket*,Entry>::const_iterator iter = state.buckets_.begin();
iter != state.buckets_.end();
++iter) {
entries[dst++] = iter->second;
}
sort(entries, entries + n);
// Report a bounded number of leaks to keep the leak report from
// growing too long.
const int to_report =
(FLAGS_heap_check_max_leaks > 0 &&
n > FLAGS_heap_check_max_leaks) ? FLAGS_heap_check_max_leaks : n;
RAW_LOG(ERROR, "The %d largest leaks:", to_report);
// Print
SymbolTable symbolization_table;
for (int i = 0; i < to_report; i++) {
const Entry& e = entries[i];
for (int j = 0; j < e.bucket->depth; j++) {
symbolization_table.Add(e.bucket->stack[j]);
}
}
static const int kBufSize = 2<<10;
char buffer[kBufSize];
if (should_symbolize)
symbolization_table.Symbolize();
for (int i = 0; i < to_report; i++) {
const Entry& e = entries[i];
base::RawPrinter printer(buffer, kBufSize);
printer.Printf("Leak of %d bytes in %d objects allocated from:\n",
e.bytes, e.count);
for (int j = 0; j < e.bucket->depth; j++) {
const void* pc = e.bucket->stack[j];
printer.Printf("\t@ %" PRIxPTR " %s\n",
reinterpret_cast<uintptr_t>(pc), symbolization_table.GetSymbol(pc));
}
RAW_LOG(ERROR, "%s", buffer);
}
if (to_report < n) {
RAW_LOG(ERROR, "Skipping leaks numbered %d..%d",
to_report, n-1);
}
delete[] entries;
// TODO: Dump the sorted Entry list instead of dumping raw data?
// (should be much shorter)
if (!HeapProfileTable::WriteProfile(filename, total_, &map_)) {
RAW_LOG(ERROR, "Could not write pprof profile to %s", filename);
}
}
void HeapProfileTable::Snapshot::ReportObject(const void* ptr,
AllocValue* v,
char* unused) {
// Perhaps also log the allocation stack trace (unsymbolized)
// on this line in case somebody finds it useful.
RAW_LOG(ERROR, "leaked %zu byte object %p", v->bytes, ptr);
}
void HeapProfileTable::Snapshot::ReportIndividualObjects() {
char unused;
map_.Iterate(ReportObject, &unused);
}

View file

@ -0,0 +1,399 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2006, 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
// Maxim Lifantsev (refactoring)
//
#ifndef BASE_HEAP_PROFILE_TABLE_H_
#define BASE_HEAP_PROFILE_TABLE_H_
#include "addressmap-inl.h"
#include "base/basictypes.h"
#include "base/logging.h" // for RawFD
#include "heap-profile-stats.h"
// Table to maintain a heap profile data inside,
// i.e. the set of currently active heap memory allocations.
// thread-unsafe and non-reentrant code:
// each instance object must be used by one thread
// at a time w/o self-recursion.
//
// TODO(maxim): add a unittest for this class.
class HeapProfileTable {
public:
// Extension to be used for heap pforile files.
static const char kFileExt[];
// Longest stack trace we record.
static const int kMaxStackDepth = 32;
// data types ----------------------------
// Profile stats.
typedef HeapProfileStats Stats;
// Info we can return about an allocation.
struct AllocInfo {
size_t object_size; // size of the allocation
const void* const* call_stack; // call stack that made the allocation call
int stack_depth; // depth of call_stack
bool live;
bool ignored;
};
// Info we return about an allocation context.
// An allocation context is a unique caller stack trace
// of an allocation operation.
struct AllocContextInfo : public Stats {
int stack_depth; // Depth of stack trace
const void* const* call_stack; // Stack trace
};
// Memory (de)allocator interface we'll use.
typedef void* (*Allocator)(size_t size);
typedef void (*DeAllocator)(void* ptr);
// interface ---------------------------
HeapProfileTable(Allocator alloc, DeAllocator dealloc, bool profile_mmap);
~HeapProfileTable();
// Collect the stack trace for the function that asked to do the
// allocation for passing to RecordAlloc() below.
//
// The stack trace is stored in 'stack'. The stack depth is returned.
//
// 'skip_count' gives the number of stack frames between this call
// and the memory allocation function.
static int GetCallerStackTrace(int skip_count, void* stack[kMaxStackDepth]);
// Record an allocation at 'ptr' of 'bytes' bytes. 'stack_depth'
// and 'call_stack' identifying the function that requested the
// allocation. They can be generated using GetCallerStackTrace() above.
void RecordAlloc(const void* ptr, size_t bytes,
int stack_depth, const void* const call_stack[]);
// Record the deallocation of memory at 'ptr'.
void RecordFree(const void* ptr);
// Return true iff we have recorded an allocation at 'ptr'.
// If yes, fill *object_size with the allocation byte size.
bool FindAlloc(const void* ptr, size_t* object_size) const;
// Same as FindAlloc, but fills all of *info.
bool FindAllocDetails(const void* ptr, AllocInfo* info) const;
// Return true iff "ptr" points into a recorded allocation
// If yes, fill *object_ptr with the actual allocation address
// and *object_size with the allocation byte size.
// max_size specifies largest currently possible allocation size.
bool FindInsideAlloc(const void* ptr, size_t max_size,
const void** object_ptr, size_t* object_size) const;
// If "ptr" points to a recorded allocation and it's not marked as live
// mark it as live and return true. Else return false.
// All allocations start as non-live.
bool MarkAsLive(const void* ptr);
// If "ptr" points to a recorded allocation, mark it as "ignored".
// Ignored objects are treated like other objects, except that they
// are skipped in heap checking reports.
void MarkAsIgnored(const void* ptr);
// Return current total (de)allocation statistics. It doesn't contain
// mmap'ed regions.
const Stats& total() const { return total_; }
// Allocation data iteration callback: gets passed object pointer and
// fully-filled AllocInfo.
typedef void (*AllocIterator)(const void* ptr, const AllocInfo& info);
// Iterate over the allocation profile data calling "callback"
// for every allocation.
void IterateAllocs(AllocIterator callback) const {
address_map_->Iterate(MapArgsAllocIterator, callback);
}
// Allocation context profile data iteration callback
typedef void (*AllocContextIterator)(const AllocContextInfo& info);
// Iterate over the allocation context profile data calling "callback"
// for every allocation context. Allocation contexts are ordered by the
// size of allocated space.
void IterateOrderedAllocContexts(AllocContextIterator callback) const;
// Fill profile data into buffer 'buf' of size 'size'
// and return the actual size occupied by the dump in 'buf'.
// The profile buckets are dumped in the decreasing order
// of currently allocated bytes.
// We do not provision for 0-terminating 'buf'.
int FillOrderedProfile(char buf[], int size) const;
// Cleanup any old profile files matching prefix + ".*" + kFileExt.
static void CleanupOldProfiles(const char* prefix);
// Return a snapshot of the current contents of *this.
// Caller must call ReleaseSnapshot() on result when no longer needed.
// The result is only valid while this exists and until
// the snapshot is discarded by calling ReleaseSnapshot().
class Snapshot;
Snapshot* TakeSnapshot();
// Release a previously taken snapshot. snapshot must not
// be used after this call.
void ReleaseSnapshot(Snapshot* snapshot);
// Return a snapshot of every non-live, non-ignored object in *this.
// If "base" is non-NULL, skip any objects present in "base".
// As a side-effect, clears the "live" bit on every live object in *this.
// Caller must call ReleaseSnapshot() on result when no longer needed.
Snapshot* NonLiveSnapshot(Snapshot* base);
private:
// data types ----------------------------
// Hash table bucket to hold (de)allocation stats
// for a given allocation call stack trace.
typedef HeapProfileBucket Bucket;
// Info stored in the address map
struct AllocValue {
// Access to the stack-trace bucket
Bucket* bucket() const {
return reinterpret_cast<Bucket*>(bucket_rep & ~uintptr_t(kMask));
}
// This also does set_live(false).
void set_bucket(Bucket* b) { bucket_rep = reinterpret_cast<uintptr_t>(b); }
size_t bytes; // Number of bytes in this allocation
// Access to the allocation liveness flag (for leak checking)
bool live() const { return bucket_rep & kLive; }
void set_live(bool l) {
bucket_rep = (bucket_rep & ~uintptr_t(kLive)) | (l ? kLive : 0);
}
// Should this allocation be ignored if it looks like a leak?
bool ignore() const { return bucket_rep & kIgnore; }
void set_ignore(bool r) {
bucket_rep = (bucket_rep & ~uintptr_t(kIgnore)) | (r ? kIgnore : 0);
}
private:
// We store a few bits in the bottom bits of bucket_rep.
// (Alignment is at least four, so we have at least two bits.)
static const int kLive = 1;
static const int kIgnore = 2;
static const int kMask = kLive | kIgnore;
uintptr_t bucket_rep;
};
// helper for FindInsideAlloc
static size_t AllocValueSize(const AllocValue& v) { return v.bytes; }
typedef AddressMap<AllocValue> AllocationMap;
// Arguments that need to be passed DumpBucketIterator callback below.
struct BufferArgs {
BufferArgs(char* buf_arg, int buflen_arg, int bufsize_arg)
: buf(buf_arg),
buflen(buflen_arg),
bufsize(bufsize_arg) {
}
char* buf;
int buflen;
int bufsize;
DISALLOW_COPY_AND_ASSIGN(BufferArgs);
};
// Arguments that need to be passed DumpNonLiveIterator callback below.
struct DumpArgs {
DumpArgs(RawFD fd_arg, Stats* profile_stats_arg)
: fd(fd_arg),
profile_stats(profile_stats_arg) {
}
RawFD fd; // file to write to
Stats* profile_stats; // stats to update (may be NULL)
};
// helpers ----------------------------
// Unparse bucket b and print its portion of profile dump into buf.
// We return the amount of space in buf that we use. We start printing
// at buf + buflen, and promise not to go beyond buf + bufsize.
// We do not provision for 0-terminating 'buf'.
//
// If profile_stats is non-NULL, we update *profile_stats by
// counting bucket b.
//
// "extra" is appended to the unparsed bucket. Typically it is empty,
// but may be set to something like " heapprofile" for the total
// bucket to indicate the type of the profile.
static int UnparseBucket(const Bucket& b,
char* buf, int buflen, int bufsize,
const char* extra,
Stats* profile_stats);
// Get the bucket for the caller stack trace 'key' of depth 'depth'
// creating the bucket if needed.
Bucket* GetBucket(int depth, const void* const key[]);
// Helper for IterateAllocs to do callback signature conversion
// from AllocationMap::Iterate to AllocIterator.
static void MapArgsAllocIterator(const void* ptr, AllocValue* v,
AllocIterator callback) {
AllocInfo info;
info.object_size = v->bytes;
info.call_stack = v->bucket()->stack;
info.stack_depth = v->bucket()->depth;
info.live = v->live();
info.ignored = v->ignore();
callback(ptr, info);
}
// Helper to dump a bucket.
inline static void DumpBucketIterator(const Bucket* bucket,
BufferArgs* args);
// Helper for DumpNonLiveProfile to do object-granularity
// heap profile dumping. It gets passed to AllocationMap::Iterate.
inline static void DumpNonLiveIterator(const void* ptr, AllocValue* v,
const DumpArgs& args);
// Helper for IterateOrderedAllocContexts and FillOrderedProfile.
// Creates a sorted list of Buckets whose length is num_buckets_.
// The caller is responsible for deallocating the returned list.
Bucket** MakeSortedBucketList() const;
// Helper for TakeSnapshot. Saves object to snapshot.
static void AddToSnapshot(const void* ptr, AllocValue* v, Snapshot* s);
// Arguments passed to AddIfNonLive
struct AddNonLiveArgs {
Snapshot* dest;
Snapshot* base;
};
// Helper for NonLiveSnapshot. Adds the object to the destination
// snapshot if it is non-live.
static void AddIfNonLive(const void* ptr, AllocValue* v,
AddNonLiveArgs* arg);
// Write contents of "*allocations" as a heap profile to
// "file_name". "total" must contain the total of all entries in
// "*allocations".
static bool WriteProfile(const char* file_name,
const Bucket& total,
AllocationMap* allocations);
// data ----------------------------
// Memory (de)allocator that we use.
Allocator alloc_;
DeAllocator dealloc_;
// Overall profile stats; we use only the Stats part,
// but make it a Bucket to pass to UnparseBucket.
Bucket total_;
bool profile_mmap_;
// Bucket hash table for malloc.
// We hand-craft one instead of using one of the pre-written
// ones because we do not want to use malloc when operating on the table.
// It is only few lines of code, so no big deal.
Bucket** bucket_table_;
int num_buckets_;
// Map of all currently allocated objects and mapped regions we know about.
AllocationMap* address_map_;
DISALLOW_COPY_AND_ASSIGN(HeapProfileTable);
};
class HeapProfileTable::Snapshot {
public:
const Stats& total() const { return total_; }
// Report anything in this snapshot as a leak.
// May use new/delete for temporary storage.
// If should_symbolize is true, will fork (which is not threadsafe)
// to turn addresses into symbol names. Set to false for maximum safety.
// Also writes a heap profile to "filename" that contains
// all of the objects in this snapshot.
void ReportLeaks(const char* checker_name, const char* filename,
bool should_symbolize);
// Report the addresses of all leaked objects.
// May use new/delete for temporary storage.
void ReportIndividualObjects();
bool Empty() const {
return (total_.allocs == 0) && (total_.alloc_size == 0);
}
private:
friend class HeapProfileTable;
// Total count/size are stored in a Bucket so we can reuse UnparseBucket
Bucket total_;
// We share the Buckets managed by the parent table, but have our
// own object->bucket map.
AllocationMap map_;
Snapshot(Allocator alloc, DeAllocator dealloc) : map_(alloc, dealloc) {
memset(&total_, 0, sizeof(total_));
}
// Callback used to populate a Snapshot object with entries found
// in another allocation map.
inline void Add(const void* ptr, const AllocValue& v) {
map_.Insert(ptr, v);
total_.allocs++;
total_.alloc_size += v.bytes;
}
// Helpers for sorting and generating leak reports
struct Entry;
struct ReportState;
static void ReportCallback(const void* ptr, AllocValue* v, ReportState*);
static void ReportObject(const void* ptr, AllocValue* v, char*);
DISALLOW_COPY_AND_ASSIGN(Snapshot);
};
#endif // BASE_HEAP_PROFILE_TABLE_H_

View file

@ -0,0 +1,622 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2005, 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
//
// TODO: Log large allocations
#include <config.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h> // for open()
#endif
#ifdef HAVE_MMAP
#include <sys/mman.h>
#endif
#include <errno.h>
#include <assert.h>
#include <sys/types.h>
#include <signal.h>
#include <algorithm>
#include <string>
#include <gperftools/heap-profiler.h>
#include "base/logging.h"
#include "base/basictypes.h" // for PRId64, among other things
#include "base/googleinit.h"
#include "base/commandlineflags.h"
#include "malloc_hook-inl.h"
#include "tcmalloc_guard.h"
#include <gperftools/malloc_hook.h>
#include <gperftools/malloc_extension.h>
#include "base/spinlock.h"
#include "base/low_level_alloc.h"
#include "base/sysinfo.h" // for GetUniquePathFromEnv()
#include "heap-profile-table.h"
#include "memory_region_map.h"
#ifndef PATH_MAX
#ifdef MAXPATHLEN
#define PATH_MAX MAXPATHLEN
#else
#define PATH_MAX 4096 // seems conservative for max filename len!
#endif
#endif
using std::string;
using std::sort;
//----------------------------------------------------------------------
// Flags that control heap-profiling
//
// The thread-safety of the profiler depends on these being immutable
// after main starts, so don't change them.
//----------------------------------------------------------------------
DEFINE_int64(heap_profile_allocation_interval,
EnvToInt64("HEAP_PROFILE_ALLOCATION_INTERVAL", 1 << 30 /*1GB*/),
"If non-zero, dump heap profiling information once every "
"specified number of bytes allocated by the program since "
"the last dump.");
DEFINE_int64(heap_profile_deallocation_interval,
EnvToInt64("HEAP_PROFILE_DEALLOCATION_INTERVAL", 0),
"If non-zero, dump heap profiling information once every "
"specified number of bytes deallocated by the program "
"since the last dump.");
// We could also add flags that report whenever inuse_bytes changes by
// X or -X, but there hasn't been a need for that yet, so we haven't.
DEFINE_int64(heap_profile_inuse_interval,
EnvToInt64("HEAP_PROFILE_INUSE_INTERVAL", 100 << 20 /*100MB*/),
"If non-zero, dump heap profiling information whenever "
"the high-water memory usage mark increases by the specified "
"number of bytes.");
DEFINE_int64(heap_profile_time_interval,
EnvToInt64("HEAP_PROFILE_TIME_INTERVAL", 0),
"If non-zero, dump heap profiling information once every "
"specified number of seconds since the last dump.");
DEFINE_bool(mmap_log,
EnvToBool("HEAP_PROFILE_MMAP_LOG", false),
"Should mmap/munmap calls be logged?");
DEFINE_bool(mmap_profile,
EnvToBool("HEAP_PROFILE_MMAP", false),
"If heap-profiling is on, also profile mmap, mremap, and sbrk)");
DEFINE_bool(only_mmap_profile,
EnvToBool("HEAP_PROFILE_ONLY_MMAP", false),
"If heap-profiling is on, only profile mmap, mremap, and sbrk; "
"do not profile malloc/new/etc");
//----------------------------------------------------------------------
// Locking
//----------------------------------------------------------------------
// A pthread_mutex has way too much lock contention to be used here.
//
// I would like to use Mutex, but it can call malloc(),
// which can cause us to fall into an infinite recursion.
//
// So we use a simple spinlock.
static SpinLock heap_lock(SpinLock::LINKER_INITIALIZED);
//----------------------------------------------------------------------
// Simple allocator for heap profiler's internal memory
//----------------------------------------------------------------------
static LowLevelAlloc::Arena *heap_profiler_memory;
static void* ProfilerMalloc(size_t bytes) {
return LowLevelAlloc::AllocWithArena(bytes, heap_profiler_memory);
}
static void ProfilerFree(void* p) {
LowLevelAlloc::Free(p);
}
// We use buffers of this size in DoGetHeapProfile.
static const int kProfileBufferSize = 1 << 20;
// This is a last-ditch buffer we use in DumpProfileLocked in case we
// can't allocate more memory from ProfilerMalloc. We expect this
// will be used by HeapProfileEndWriter when the application has to
// exit due to out-of-memory. This buffer is allocated in
// HeapProfilerStart. Access to this must be protected by heap_lock.
static char* global_profiler_buffer = NULL;
//----------------------------------------------------------------------
// Profiling control/state data
//----------------------------------------------------------------------
// Access to all of these is protected by heap_lock.
static bool is_on = false; // If are on as a subsytem.
static bool dumping = false; // Dumping status to prevent recursion
static char* filename_prefix = NULL; // Prefix used for profile file names
// (NULL if no need for dumping yet)
static int dump_count = 0; // How many dumps so far
static int64 last_dump_alloc = 0; // alloc_size when did we last dump
static int64 last_dump_free = 0; // free_size when did we last dump
static int64 high_water_mark = 0; // In-use-bytes at last high-water dump
static int64 last_dump_time = 0; // The time of the last dump
static HeapProfileTable* heap_profile = NULL; // the heap profile table
//----------------------------------------------------------------------
// Profile generation
//----------------------------------------------------------------------
// Input must be a buffer of size at least 1MB.
static char* DoGetHeapProfileLocked(char* buf, int buflen) {
// We used to be smarter about estimating the required memory and
// then capping it to 1MB and generating the profile into that.
if (buf == NULL || buflen < 1)
return NULL;
RAW_DCHECK(heap_lock.IsHeld(), "");
int bytes_written = 0;
if (is_on) {
HeapProfileTable::Stats const stats = heap_profile->total();
(void)stats; // avoid an unused-variable warning in non-debug mode.
bytes_written = heap_profile->FillOrderedProfile(buf, buflen - 1);
// FillOrderedProfile should not reduce the set of active mmap-ed regions,
// hence MemoryRegionMap will let us remove everything we've added above:
RAW_DCHECK(stats.Equivalent(heap_profile->total()), "");
// if this fails, we somehow removed by FillOrderedProfile
// more than we have added.
}
buf[bytes_written] = '\0';
RAW_DCHECK(bytes_written == strlen(buf), "");
return buf;
}
extern "C" char* GetHeapProfile() {
// Use normal malloc: we return the profile to the user to free it:
char* buffer = reinterpret_cast<char*>(malloc(kProfileBufferSize));
SpinLockHolder l(&heap_lock);
return DoGetHeapProfileLocked(buffer, kProfileBufferSize);
}
// defined below
static void NewHook(const void* ptr, size_t size);
static void DeleteHook(const void* ptr);
// Helper for HeapProfilerDump.
static void DumpProfileLocked(const char* reason) {
RAW_DCHECK(heap_lock.IsHeld(), "");
RAW_DCHECK(is_on, "");
RAW_DCHECK(!dumping, "");
if (filename_prefix == NULL) return; // we do not yet need dumping
dumping = true;
// Make file name
char file_name[1000];
dump_count++;
snprintf(file_name, sizeof(file_name), "%s.%04d%s",
filename_prefix, dump_count, HeapProfileTable::kFileExt);
// Dump the profile
RAW_VLOG(0, "Dumping heap profile to %s (%s)", file_name, reason);
// We must use file routines that don't access memory, since we hold
// a memory lock now.
RawFD fd = RawOpenForWriting(file_name);
if (fd == kIllegalRawFD) {
RAW_LOG(ERROR, "Failed dumping heap profile to %s", file_name);
dumping = false;
return;
}
// This case may be impossible, but it's best to be safe.
// It's safe to use the global buffer: we're protected by heap_lock.
if (global_profiler_buffer == NULL) {
global_profiler_buffer =
reinterpret_cast<char*>(ProfilerMalloc(kProfileBufferSize));
}
char* profile = DoGetHeapProfileLocked(global_profiler_buffer,
kProfileBufferSize);
RawWrite(fd, profile, strlen(profile));
RawClose(fd);
dumping = false;
}
//----------------------------------------------------------------------
// Profile collection
//----------------------------------------------------------------------
// Dump a profile after either an allocation or deallocation, if
// the memory use has changed enough since the last dump.
static void MaybeDumpProfileLocked() {
if (!dumping) {
const HeapProfileTable::Stats& total = heap_profile->total();
const int64 inuse_bytes = total.alloc_size - total.free_size;
bool need_to_dump = false;
char buf[128];
if (FLAGS_heap_profile_allocation_interval > 0 &&
total.alloc_size >=
last_dump_alloc + FLAGS_heap_profile_allocation_interval) {
snprintf(buf, sizeof(buf), ("%" PRId64 " MB allocated cumulatively, "
"%" PRId64 " MB currently in use"),
total.alloc_size >> 20, inuse_bytes >> 20);
need_to_dump = true;
} else if (FLAGS_heap_profile_deallocation_interval > 0 &&
total.free_size >=
last_dump_free + FLAGS_heap_profile_deallocation_interval) {
snprintf(buf, sizeof(buf), ("%" PRId64 " MB freed cumulatively, "
"%" PRId64 " MB currently in use"),
total.free_size >> 20, inuse_bytes >> 20);
need_to_dump = true;
} else if (FLAGS_heap_profile_inuse_interval > 0 &&
inuse_bytes >
high_water_mark + FLAGS_heap_profile_inuse_interval) {
snprintf(buf, sizeof(buf), "%" PRId64 " MB currently in use",
inuse_bytes >> 20);
need_to_dump = true;
} else if (FLAGS_heap_profile_time_interval > 0 ) {
int64 current_time = time(NULL);
if (current_time - last_dump_time >=
FLAGS_heap_profile_time_interval) {
snprintf(buf, sizeof(buf), "%" PRId64 " sec since the last dump",
current_time - last_dump_time);
need_to_dump = true;
last_dump_time = current_time;
}
}
if (need_to_dump) {
DumpProfileLocked(buf);
last_dump_alloc = total.alloc_size;
last_dump_free = total.free_size;
if (inuse_bytes > high_water_mark)
high_water_mark = inuse_bytes;
}
}
}
// Record an allocation in the profile.
static void RecordAlloc(const void* ptr, size_t bytes, int skip_count) {
// Take the stack trace outside the critical section.
void* stack[HeapProfileTable::kMaxStackDepth];
int depth = HeapProfileTable::GetCallerStackTrace(skip_count + 1, stack);
SpinLockHolder l(&heap_lock);
if (is_on) {
heap_profile->RecordAlloc(ptr, bytes, depth, stack);
MaybeDumpProfileLocked();
}
}
// Record a deallocation in the profile.
static void RecordFree(const void* ptr) {
SpinLockHolder l(&heap_lock);
if (is_on) {
heap_profile->RecordFree(ptr);
MaybeDumpProfileLocked();
}
}
//----------------------------------------------------------------------
// Allocation/deallocation hooks for MallocHook
//----------------------------------------------------------------------
// static
void NewHook(const void* ptr, size_t size) {
if (ptr != NULL) RecordAlloc(ptr, size, 0);
}
// static
void DeleteHook(const void* ptr) {
if (ptr != NULL) RecordFree(ptr);
}
// TODO(jandrews): Re-enable stack tracing
#ifdef TODO_REENABLE_STACK_TRACING
static void RawInfoStackDumper(const char* message, void*) {
RAW_LOG(INFO, "%.*s", static_cast<int>(strlen(message) - 1), message);
// -1 is to chop the \n which will be added by RAW_LOG
}
#endif
static void MmapHook(const void* result, const void* start, size_t size,
int prot, int flags, int fd, off_t offset) {
if (FLAGS_mmap_log) { // log it
// We use PRIxPTR not just '%p' to avoid deadlocks
// in pretty-printing of NULL as "nil".
// TODO(maxim): instead should use a safe snprintf reimplementation
RAW_LOG(INFO,
"mmap(start=0x%" PRIxPTR ", len=%zu, prot=0x%x, flags=0x%x, "
"fd=%d, offset=0x%x) = 0x%" PRIxPTR "",
(uintptr_t) start, size, prot, flags, fd, (unsigned int) offset,
(uintptr_t) result);
#ifdef TODO_REENABLE_STACK_TRACING
DumpStackTrace(1, RawInfoStackDumper, NULL);
#endif
}
}
static void MremapHook(const void* result, const void* old_addr,
size_t old_size, size_t new_size,
int flags, const void* new_addr) {
if (FLAGS_mmap_log) { // log it
// We use PRIxPTR not just '%p' to avoid deadlocks
// in pretty-printing of NULL as "nil".
// TODO(maxim): instead should use a safe snprintf reimplementation
RAW_LOG(INFO,
"mremap(old_addr=0x%" PRIxPTR ", old_size=%zu, "
"new_size=%zu, flags=0x%x, new_addr=0x%" PRIxPTR ") = "
"0x%" PRIxPTR "",
(uintptr_t) old_addr, old_size, new_size, flags,
(uintptr_t) new_addr, (uintptr_t) result);
#ifdef TODO_REENABLE_STACK_TRACING
DumpStackTrace(1, RawInfoStackDumper, NULL);
#endif
}
}
static void MunmapHook(const void* ptr, size_t size) {
if (FLAGS_mmap_log) { // log it
// We use PRIxPTR not just '%p' to avoid deadlocks
// in pretty-printing of NULL as "nil".
// TODO(maxim): instead should use a safe snprintf reimplementation
RAW_LOG(INFO, "munmap(start=0x%" PRIxPTR ", len=%zu)",
(uintptr_t) ptr, size);
#ifdef TODO_REENABLE_STACK_TRACING
DumpStackTrace(1, RawInfoStackDumper, NULL);
#endif
}
}
static void SbrkHook(const void* result, ptrdiff_t increment) {
if (FLAGS_mmap_log) { // log it
RAW_LOG(INFO, "sbrk(inc=%zd) = 0x%" PRIxPTR "",
increment, (uintptr_t) result);
#ifdef TODO_REENABLE_STACK_TRACING
DumpStackTrace(1, RawInfoStackDumper, NULL);
#endif
}
}
//----------------------------------------------------------------------
// Starting/stopping/dumping
//----------------------------------------------------------------------
extern "C" void HeapProfilerStart(const char* prefix) {
SpinLockHolder l(&heap_lock);
if (is_on) return;
is_on = true;
RAW_VLOG(0, "Starting tracking the heap");
// This should be done before the hooks are set up, since it should
// call new, and we want that to be accounted for correctly.
MallocExtension::Initialize();
if (FLAGS_only_mmap_profile) {
FLAGS_mmap_profile = true;
}
if (FLAGS_mmap_profile) {
// Ask MemoryRegionMap to record all mmap, mremap, and sbrk
// call stack traces of at least size kMaxStackDepth:
MemoryRegionMap::Init(HeapProfileTable::kMaxStackDepth,
/* use_buckets */ true);
}
if (FLAGS_mmap_log) {
// Install our hooks to do the logging:
RAW_CHECK(MallocHook::AddMmapHook(&MmapHook), "");
RAW_CHECK(MallocHook::AddMremapHook(&MremapHook), "");
RAW_CHECK(MallocHook::AddMunmapHook(&MunmapHook), "");
RAW_CHECK(MallocHook::AddSbrkHook(&SbrkHook), "");
}
heap_profiler_memory =
LowLevelAlloc::NewArena(0, LowLevelAlloc::DefaultArena());
// Reserve space now for the heap profiler, so we can still write a
// heap profile even if the application runs out of memory.
global_profiler_buffer =
reinterpret_cast<char*>(ProfilerMalloc(kProfileBufferSize));
heap_profile = new(ProfilerMalloc(sizeof(HeapProfileTable)))
HeapProfileTable(ProfilerMalloc, ProfilerFree, FLAGS_mmap_profile);
last_dump_alloc = 0;
last_dump_free = 0;
high_water_mark = 0;
last_dump_time = 0;
// We do not reset dump_count so if the user does a sequence of
// HeapProfilerStart/HeapProfileStop, we will get a continuous
// sequence of profiles.
if (FLAGS_only_mmap_profile == false) {
// Now set the hooks that capture new/delete and malloc/free.
RAW_CHECK(MallocHook::AddNewHook(&NewHook), "");
RAW_CHECK(MallocHook::AddDeleteHook(&DeleteHook), "");
}
// Copy filename prefix
RAW_DCHECK(filename_prefix == NULL, "");
const int prefix_length = strlen(prefix);
filename_prefix = reinterpret_cast<char*>(ProfilerMalloc(prefix_length + 1));
memcpy(filename_prefix, prefix, prefix_length);
filename_prefix[prefix_length] = '\0';
}
extern "C" int IsHeapProfilerRunning() {
SpinLockHolder l(&heap_lock);
return is_on ? 1 : 0; // return an int, because C code doesn't have bool
}
extern "C" void HeapProfilerStop() {
SpinLockHolder l(&heap_lock);
if (!is_on) return;
if (FLAGS_only_mmap_profile == false) {
// Unset our new/delete hooks, checking they were set:
RAW_CHECK(MallocHook::RemoveNewHook(&NewHook), "");
RAW_CHECK(MallocHook::RemoveDeleteHook(&DeleteHook), "");
}
if (FLAGS_mmap_log) {
// Restore mmap/sbrk hooks, checking that our hooks were set:
RAW_CHECK(MallocHook::RemoveMmapHook(&MmapHook), "");
RAW_CHECK(MallocHook::RemoveMremapHook(&MremapHook), "");
RAW_CHECK(MallocHook::RemoveSbrkHook(&SbrkHook), "");
RAW_CHECK(MallocHook::RemoveMunmapHook(&MunmapHook), "");
}
// free profile
heap_profile->~HeapProfileTable();
ProfilerFree(heap_profile);
heap_profile = NULL;
// free output-buffer memory
ProfilerFree(global_profiler_buffer);
// free prefix
ProfilerFree(filename_prefix);
filename_prefix = NULL;
if (!LowLevelAlloc::DeleteArena(heap_profiler_memory)) {
RAW_LOG(FATAL, "Memory leak in HeapProfiler:");
}
if (FLAGS_mmap_profile) {
MemoryRegionMap::Shutdown();
}
is_on = false;
}
extern "C" void HeapProfilerDump(const char *reason) {
SpinLockHolder l(&heap_lock);
if (is_on && !dumping) {
DumpProfileLocked(reason);
}
}
// Signal handler that is registered when a user selectable signal
// number is defined in the environment variable HEAPPROFILESIGNAL.
static void HeapProfilerDumpSignal(int signal_number) {
(void)signal_number;
if (!heap_lock.TryLock()) {
return;
}
if (is_on && !dumping) {
DumpProfileLocked("signal");
}
heap_lock.Unlock();
}
//----------------------------------------------------------------------
// Initialization/finalization code
//----------------------------------------------------------------------
// Initialization code
static void HeapProfilerInit() {
// Everything after this point is for setting up the profiler based on envvar
char fname[PATH_MAX];
if (!GetUniquePathFromEnv("HEAPPROFILE", fname)) {
return;
}
// We do a uid check so we don't write out files in a setuid executable.
#ifdef HAVE_GETEUID
if (getuid() != geteuid()) {
RAW_LOG(WARNING, ("HeapProfiler: ignoring HEAPPROFILE because "
"program seems to be setuid\n"));
return;
}
#endif
char *signal_number_str = getenv("HEAPPROFILESIGNAL");
if (signal_number_str != NULL) {
long int signal_number = strtol(signal_number_str, NULL, 10);
intptr_t old_signal_handler = reinterpret_cast<intptr_t>(signal(signal_number, HeapProfilerDumpSignal));
if (old_signal_handler == reinterpret_cast<intptr_t>(SIG_ERR)) {
RAW_LOG(FATAL, "Failed to set signal. Perhaps signal number %s is invalid\n", signal_number_str);
} else if (old_signal_handler == 0) {
RAW_LOG(INFO,"Using signal %d as heap profiling switch", signal_number);
} else {
RAW_LOG(FATAL, "Signal %d already in use\n", signal_number);
}
}
HeapProfileTable::CleanupOldProfiles(fname);
HeapProfilerStart(fname);
}
// class used for finalization -- dumps the heap-profile at program exit
struct HeapProfileEndWriter {
~HeapProfileEndWriter() {
char buf[128];
if (heap_profile) {
const HeapProfileTable::Stats& total = heap_profile->total();
const int64 inuse_bytes = total.alloc_size - total.free_size;
if ((inuse_bytes >> 20) > 0) {
snprintf(buf, sizeof(buf), ("Exiting, %" PRId64 " MB in use"),
inuse_bytes >> 20);
} else if ((inuse_bytes >> 10) > 0) {
snprintf(buf, sizeof(buf), ("Exiting, %" PRId64 " kB in use"),
inuse_bytes >> 10);
} else {
snprintf(buf, sizeof(buf), ("Exiting, %" PRId64 " bytes in use"),
inuse_bytes);
}
} else {
snprintf(buf, sizeof(buf), ("Exiting"));
}
HeapProfilerDump(buf);
}
};
// We want to make sure tcmalloc is up and running before starting the profiler
static const TCMallocGuard tcmalloc_initializer;
REGISTER_MODULE_INITIALIZER(heapprofiler, HeapProfilerInit());
static HeapProfileEndWriter heap_profile_end_writer;

View file

@ -0,0 +1,192 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2005, 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.
// ---
// Sanjay Ghemawat <opensource@google.com>
#include <config.h>
#include "internal_logging.h"
#include <stdarg.h> // for va_end, va_start
#include <stdio.h> // for vsnprintf, va_list, etc
#include <stdlib.h> // for abort
#include <string.h> // for strlen, memcpy
#ifdef HAVE_UNISTD_H
#include <unistd.h> // for write()
#endif
#include <gperftools/malloc_extension.h>
#include "base/logging.h" // for perftools_vsnprintf
#include "base/spinlock.h" // for SpinLockHolder, SpinLock
// Variables for storing crash output. Allocated statically since we
// may not be able to heap-allocate while crashing.
static SpinLock crash_lock(base::LINKER_INITIALIZED);
static bool crashed = false;
static const int kStatsBufferSize = 16 << 10;
static char stats_buffer[kStatsBufferSize] = { 0 };
namespace tcmalloc {
static void WriteMessage(const char* msg, int length) {
write(STDERR_FILENO, msg, length);
}
void (*log_message_writer)(const char* msg, int length) = WriteMessage;
class Logger {
public:
bool Add(const LogItem& item);
bool AddStr(const char* str, int n);
bool AddNum(uint64_t num, int base); // base must be 10 or 16.
static const int kBufSize = 200;
char* p_;
char* end_;
char buf_[kBufSize];
};
void Log(LogMode mode, const char* filename, int line,
LogItem a, LogItem b, LogItem c, LogItem d) {
Logger state;
state.p_ = state.buf_;
state.end_ = state.buf_ + sizeof(state.buf_);
state.AddStr(filename, strlen(filename))
&& state.AddStr(":", 1)
&& state.AddNum(line, 10)
&& state.AddStr("]", 1)
&& state.Add(a)
&& state.Add(b)
&& state.Add(c)
&& state.Add(d);
// Teminate with newline
if (state.p_ >= state.end_) {
state.p_ = state.end_ - 1;
}
*state.p_ = '\n';
state.p_++;
int msglen = state.p_ - state.buf_;
if (mode == kLog) {
(*log_message_writer)(state.buf_, msglen);
return;
}
bool first_crash = false;
{
SpinLockHolder l(&crash_lock);
if (!crashed) {
crashed = true;
first_crash = true;
}
}
(*log_message_writer)(state.buf_, msglen);
if (first_crash && mode == kCrashWithStats) {
MallocExtension::instance()->GetStats(stats_buffer, kStatsBufferSize);
(*log_message_writer)(stats_buffer, strlen(stats_buffer));
}
abort();
}
bool Logger::Add(const LogItem& item) {
// Separate items with spaces
if (p_ < end_) {
*p_ = ' ';
p_++;
}
switch (item.tag_) {
case LogItem::kStr:
return AddStr(item.u_.str, strlen(item.u_.str));
case LogItem::kUnsigned:
return AddNum(item.u_.unum, 10);
case LogItem::kSigned:
if (item.u_.snum < 0) {
// The cast to uint64_t is intentionally before the negation
// so that we do not attempt to negate -2^63.
return AddStr("-", 1)
&& AddNum(- static_cast<uint64_t>(item.u_.snum), 10);
} else {
return AddNum(static_cast<uint64_t>(item.u_.snum), 10);
}
case LogItem::kPtr:
return AddStr("0x", 2)
&& AddNum(reinterpret_cast<uintptr_t>(item.u_.ptr), 16);
default:
return false;
}
}
bool Logger::AddStr(const char* str, int n) {
if (end_ - p_ < n) {
return false;
} else {
memcpy(p_, str, n);
p_ += n;
return true;
}
}
bool Logger::AddNum(uint64_t num, int base) {
static const char kDigits[] = "0123456789abcdef";
char space[22]; // more than enough for 2^64 in smallest supported base (10)
char* end = space + sizeof(space);
char* pos = end;
do {
pos--;
*pos = kDigits[num % base];
num /= base;
} while (num > 0 && pos > space);
return AddStr(pos, end - pos);
}
} // end tcmalloc namespace
void TCMalloc_Printer::printf(const char* format, ...) {
if (left_ > 0) {
va_list ap;
va_start(ap, format);
const int r = perftools_vsnprintf(buf_, left_, format, ap);
va_end(ap);
if (r < 0) {
// Perhaps an old glibc that returns -1 on truncation?
left_ = 0;
} else if (r > left_) {
// Truncation
left_ = 0;
} else {
left_ -= r;
buf_ += r;
}
}
}

View file

@ -0,0 +1,144 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2005, 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 <opensource@google.com>
//
// Internal logging and related utility routines.
#ifndef TCMALLOC_INTERNAL_LOGGING_H_
#define TCMALLOC_INTERNAL_LOGGING_H_
#include <config.h>
#include <stddef.h> // for size_t
#if defined HAVE_STDINT_H
#include <stdint.h>
#elif defined HAVE_INTTYPES_H
#include <inttypes.h>
#else
#include <sys/types.h>
#endif
//-------------------------------------------------------------------
// Utility routines
//-------------------------------------------------------------------
// Safe logging helper: we write directly to the stderr file
// descriptor and avoid FILE buffering because that may invoke
// malloc().
//
// Example:
// Log(kLog, __FILE__, __LINE__, "error", bytes);
namespace tcmalloc {
enum LogMode {
kLog, // Just print the message
kCrash, // Print the message and crash
kCrashWithStats // Print the message, some stats, and crash
};
class Logger;
// A LogItem holds any of the argument types that can be passed to Log()
class LogItem {
public:
LogItem() : tag_(kEnd) { }
LogItem(const char* v) : tag_(kStr) { u_.str = v; }
LogItem(int v) : tag_(kSigned) { u_.snum = v; }
LogItem(long v) : tag_(kSigned) { u_.snum = v; }
LogItem(long long v) : tag_(kSigned) { u_.snum = v; }
LogItem(unsigned int v) : tag_(kUnsigned) { u_.unum = v; }
LogItem(unsigned long v) : tag_(kUnsigned) { u_.unum = v; }
LogItem(unsigned long long v) : tag_(kUnsigned) { u_.unum = v; }
LogItem(const void* v) : tag_(kPtr) { u_.ptr = v; }
private:
friend class Logger;
enum Tag {
kStr,
kSigned,
kUnsigned,
kPtr,
kEnd
};
Tag tag_;
union {
const char* str;
const void* ptr;
int64_t snum;
uint64_t unum;
} u_;
};
extern PERFTOOLS_DLL_DECL void Log(LogMode mode, const char* filename, int line,
LogItem a, LogItem b = LogItem(),
LogItem c = LogItem(), LogItem d = LogItem());
// Tests can override this function to collect logging messages.
extern PERFTOOLS_DLL_DECL void (*log_message_writer)(const char* msg, int length);
} // end tcmalloc namespace
// Like assert(), but executed even in NDEBUG mode
#undef CHECK_CONDITION
#define CHECK_CONDITION(cond) \
do { \
if (!(cond)) { \
::tcmalloc::Log(::tcmalloc::kCrash, __FILE__, __LINE__, #cond); \
} \
} while (0)
// Our own version of assert() so we can avoid hanging by trying to do
// all kinds of goofy printing while holding the malloc lock.
#ifndef NDEBUG
#define ASSERT(cond) CHECK_CONDITION(cond)
#else
#define ASSERT(cond) ((void) 0)
#endif
// Print into buffer
class TCMalloc_Printer {
private:
char* buf_; // Where should we write next
int left_; // Space left in buffer (including space for \0)
public:
// REQUIRES: "length > 0"
TCMalloc_Printer(char* buf, int length) : buf_(buf), left_(length) {
buf[0] = '\0';
}
void printf(const char* format, ...)
#ifdef HAVE___ATTRIBUTE__
__attribute__ ((__format__ (__printf__, 2, 3)))
#endif
;
};
#endif // TCMALLOC_INTERNAL_LOGGING_H_

View file

@ -0,0 +1,99 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2011, 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: Craig Silverstein <opensource@google.com>
//
// This .h file imports the code that causes tcmalloc to override libc
// versions of malloc/free/new/delete/etc. That is, it provides the
// logic that makes it so calls to malloc(10) go through tcmalloc,
// rather than the default (libc) malloc.
//
// This file also provides a method: ReplaceSystemAlloc(), that every
// libc_override_*.h file it #includes is required to provide. This
// is called when first setting up tcmalloc -- that is, when a global
// constructor in tcmalloc.cc is executed -- to do any initialization
// work that may be required for this OS. (Note we cannot entirely
// control when tcmalloc is initialized, and the system may do some
// mallocs and frees before this routine is called.) It may be a
// noop.
//
// Every libc has its own way of doing this, and sometimes the compiler
// matters too, so we have a different file for each libc, and often
// for different compilers and OS's.
#ifndef TCMALLOC_LIBC_OVERRIDE_INL_H_
#define TCMALLOC_LIBC_OVERRIDE_INL_H_
#include <config.h>
#ifdef HAVE_FEATURES_H
#include <features.h> // for __GLIBC__
#endif
#include <gperftools/tcmalloc.h>
#if __cplusplus >= 201103L || (defined(_MSC_VER) && _MSC_VER >= 1900)
#define CPP_NOTHROW noexcept
#define CPP_BADALLOC
#else
#define CPP_NOTHROW throw()
#define CPP_BADALLOC throw(std::bad_alloc)
#endif
static void ReplaceSystemAlloc(); // defined in the .h files below
// For windows, there are two ways to get tcmalloc. If we're
// patching, then src/windows/patch_function.cc will do the necessary
// overriding here. Otherwise, we doing the 'redefine' trick, where
// we remove malloc/new/etc from mscvcrt.dll, and just need to define
// them now.
#if defined(_WIN32) && defined(WIN32_DO_PATCHING)
void PatchWindowsFunctions(); // in src/windows/patch_function.cc
static void ReplaceSystemAlloc() { PatchWindowsFunctions(); }
#elif defined(_WIN32) && !defined(WIN32_DO_PATCHING)
#include "libc_override_redefine.h"
#elif defined(__APPLE__)
#include "libc_override_osx.h"
#elif defined(__GLIBC__)
#include "libc_override_glibc.h"
// Not all gcc systems necessarily support weak symbols, but all the
// ones I know of do, so for now just assume they all do.
#elif defined(__GNUC__)
#include "libc_override_gcc_and_weak.h"
#else
#error Need to add support for your libc/OS here
#endif
#endif // TCMALLOC_LIBC_OVERRIDE_INL_H_

View file

@ -0,0 +1,244 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2011, 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: Craig Silverstein <opensource@google.com>
//
// Used to override malloc routines on systems that define the
// memory allocation routines to be weak symbols in their libc
// (almost all unix-based systems are like this), on gcc, which
// suppports the 'alias' attribute.
#ifndef TCMALLOC_LIBC_OVERRIDE_GCC_AND_WEAK_INL_H_
#define TCMALLOC_LIBC_OVERRIDE_GCC_AND_WEAK_INL_H_
#ifdef HAVE_SYS_CDEFS_H
#include <sys/cdefs.h> // for __THROW
#endif
#include <gperftools/tcmalloc.h>
#include "getenv_safe.h" // TCMallocGetenvSafe
#include "base/commandlineflags.h"
#ifndef __THROW // I guess we're not on a glibc-like system
# define __THROW // __THROW is just an optimization, so ok to make it ""
#endif
#ifndef __GNUC__
# error libc_override_gcc_and_weak.h is for gcc distributions only.
#endif
#define ALIAS(tc_fn) __attribute__ ((alias (#tc_fn), used))
void* operator new(size_t size) CPP_BADALLOC ALIAS(tc_new);
void operator delete(void* p) CPP_NOTHROW ALIAS(tc_delete);
void* operator new[](size_t size) CPP_BADALLOC ALIAS(tc_newarray);
void operator delete[](void* p) CPP_NOTHROW ALIAS(tc_deletearray);
void* operator new(size_t size, const std::nothrow_t& nt) CPP_NOTHROW
ALIAS(tc_new_nothrow);
void* operator new[](size_t size, const std::nothrow_t& nt) CPP_NOTHROW
ALIAS(tc_newarray_nothrow);
void operator delete(void* p, const std::nothrow_t& nt) CPP_NOTHROW
ALIAS(tc_delete_nothrow);
void operator delete[](void* p, const std::nothrow_t& nt) CPP_NOTHROW
ALIAS(tc_deletearray_nothrow);
#if defined(ENABLE_SIZED_DELETE)
void operator delete(void *p, size_t size) CPP_NOTHROW
ALIAS(tc_delete_sized);
void operator delete[](void *p, size_t size) CPP_NOTHROW
ALIAS(tc_deletearray_sized);
#elif defined(ENABLE_DYNAMIC_SIZED_DELETE) && \
(__GNUC__ * 100 + __GNUC_MINOR__) >= 405
static void delegate_sized_delete(void *p, size_t s) {
(operator delete)(p);
}
static void delegate_sized_deletearray(void *p, size_t s) {
(operator delete[])(p);
}
extern "C" __attribute__((weak))
int tcmalloc_sized_delete_enabled(void);
static bool sized_delete_enabled(void) {
if (tcmalloc_sized_delete_enabled != 0) {
return !!tcmalloc_sized_delete_enabled();
}
const char *flag = TCMallocGetenvSafe("TCMALLOC_ENABLE_SIZED_DELETE");
return tcmalloc::commandlineflags::StringToBool(flag, false);
}
extern "C" {
static void *resolve_delete_sized(void) {
if (sized_delete_enabled()) {
return reinterpret_cast<void *>(tc_delete_sized);
}
return reinterpret_cast<void *>(delegate_sized_delete);
}
static void *resolve_deletearray_sized(void) {
if (sized_delete_enabled()) {
return reinterpret_cast<void *>(tc_deletearray_sized);
}
return reinterpret_cast<void *>(delegate_sized_deletearray);
}
}
void operator delete(void *p, size_t size) CPP_NOTHROW
__attribute__((ifunc("resolve_delete_sized")));
void operator delete[](void *p, size_t size) CPP_NOTHROW
__attribute__((ifunc("resolve_deletearray_sized")));
#else /* !ENABLE_SIZED_DELETE && !ENABLE_DYN_SIZED_DELETE */
void operator delete(void *p, size_t size) CPP_NOTHROW
ALIAS(tc_delete_sized);
void operator delete[](void *p, size_t size) CPP_NOTHROW
ALIAS(tc_deletearray_sized);
#endif /* !ENABLE_SIZED_DELETE && !ENABLE_DYN_SIZED_DELETE */
#if defined(ENABLE_ALIGNED_NEW_DELETE)
void* operator new(size_t size, std::align_val_t al)
ALIAS(tc_new_aligned);
void operator delete(void* p, std::align_val_t al) CPP_NOTHROW
ALIAS(tc_delete_aligned);
void* operator new[](size_t size, std::align_val_t al)
ALIAS(tc_newarray_aligned);
void operator delete[](void* p, std::align_val_t al) CPP_NOTHROW
ALIAS(tc_deletearray_aligned);
void* operator new(size_t size, std::align_val_t al, const std::nothrow_t& nt) CPP_NOTHROW
ALIAS(tc_new_aligned_nothrow);
void* operator new[](size_t size, std::align_val_t al, const std::nothrow_t& nt) CPP_NOTHROW
ALIAS(tc_newarray_aligned_nothrow);
void operator delete(void* p, std::align_val_t al, const std::nothrow_t& nt) CPP_NOTHROW
ALIAS(tc_delete_aligned_nothrow);
void operator delete[](void* p, std::align_val_t al, const std::nothrow_t& nt) CPP_NOTHROW
ALIAS(tc_deletearray_aligned_nothrow);
#if defined(ENABLE_SIZED_DELETE)
void operator delete(void *p, size_t size, std::align_val_t al) CPP_NOTHROW
ALIAS(tc_delete_sized_aligned);
void operator delete[](void *p, size_t size, std::align_val_t al) CPP_NOTHROW
ALIAS(tc_deletearray_sized_aligned);
#else /* defined(ENABLE_SIZED_DELETE) */
#if defined(ENABLE_DYNAMIC_SIZED_DELETE) && \
(__GNUC__ * 100 + __GNUC_MINOR__) >= 405
static void delegate_sized_aligned_delete(void *p, size_t s, std::align_val_t al) {
(operator delete)(p, al);
}
static void delegate_sized_aligned_deletearray(void *p, size_t s, std::align_val_t al) {
(operator delete[])(p, al);
}
extern "C" {
static void *resolve_delete_sized_aligned(void) {
if (sized_delete_enabled()) {
return reinterpret_cast<void *>(tc_delete_sized_aligned);
}
return reinterpret_cast<void *>(delegate_sized_aligned_delete);
}
static void *resolve_deletearray_sized_aligned(void) {
if (sized_delete_enabled()) {
return reinterpret_cast<void *>(tc_deletearray_sized_aligned);
}
return reinterpret_cast<void *>(delegate_sized_aligned_deletearray);
}
}
void operator delete(void *p, size_t size, std::align_val_t al) CPP_NOTHROW
__attribute__((ifunc("resolve_delete_sized_aligned")));
void operator delete[](void *p, size_t size, std::align_val_t al) CPP_NOTHROW
__attribute__((ifunc("resolve_deletearray_sized_aligned")));
#else /* defined(ENABLE_DYN_SIZED_DELETE) */
void operator delete(void *p, size_t size, std::align_val_t al) CPP_NOTHROW
ALIAS(tc_delete_sized_aligned);
void operator delete[](void *p, size_t size, std::align_val_t al) CPP_NOTHROW
ALIAS(tc_deletearray_sized_aligned);
#endif /* defined(ENABLE_DYN_SIZED_DELETE) */
#endif /* defined(ENABLE_SIZED_DELETE) */
#endif /* defined(ENABLE_ALIGNED_NEW_DELETE) */
extern "C" {
void* malloc(size_t size) __THROW ALIAS(tc_malloc);
void free(void* ptr) __THROW ALIAS(tc_free);
void* realloc(void* ptr, size_t size) __THROW ALIAS(tc_realloc);
void* calloc(size_t n, size_t size) __THROW ALIAS(tc_calloc);
void cfree(void* ptr) __THROW ALIAS(tc_cfree);
void* memalign(size_t align, size_t s) __THROW ALIAS(tc_memalign);
void* aligned_alloc(size_t align, size_t s) __THROW ALIAS(tc_memalign);
void* valloc(size_t size) __THROW ALIAS(tc_valloc);
void* pvalloc(size_t size) __THROW ALIAS(tc_pvalloc);
int posix_memalign(void** r, size_t a, size_t s) __THROW
ALIAS(tc_posix_memalign);
#ifndef __UCLIBC__
void malloc_stats(void) __THROW ALIAS(tc_malloc_stats);
#endif
int mallopt(int cmd, int value) __THROW ALIAS(tc_mallopt);
#ifdef HAVE_STRUCT_MALLINFO
struct mallinfo mallinfo(void) __THROW ALIAS(tc_mallinfo);
#endif
size_t malloc_size(void* p) __THROW ALIAS(tc_malloc_size);
#if defined(__ANDROID__)
size_t malloc_usable_size(const void* p) __THROW
ALIAS(tc_malloc_size);
#else
size_t malloc_usable_size(void* p) __THROW ALIAS(tc_malloc_size);
#endif
} // extern "C"
#undef ALIAS
// No need to do anything at tcmalloc-registration time: we do it all
// via overriding weak symbols (at link time).
static void ReplaceSystemAlloc() { }
#endif // TCMALLOC_LIBC_OVERRIDE_GCC_AND_WEAK_INL_H_

View file

@ -0,0 +1,92 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2011, 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: Craig Silverstein <opensource@google.com>
//
// Used to override malloc routines on systems that are using glibc.
#ifndef TCMALLOC_LIBC_OVERRIDE_GLIBC_INL_H_
#define TCMALLOC_LIBC_OVERRIDE_GLIBC_INL_H_
#include <config.h>
#include <features.h> // for __GLIBC__
#include <gperftools/tcmalloc.h>
#ifndef __GLIBC__
# error libc_override_glibc.h is for glibc distributions only.
#endif
// In glibc, the memory-allocation methods are weak symbols, so we can
// just override them with our own. If we're using gcc, we can use
// __attribute__((alias)) to do the overriding easily (exception:
// Mach-O, which doesn't support aliases). Otherwise we have to use a
// function call.
#if !defined(__GNUC__) || defined(__MACH__)
// This also defines ReplaceSystemAlloc().
# include "libc_override_redefine.h" // defines functions malloc()/etc
#else // #if !defined(__GNUC__) || defined(__MACH__)
// If we get here, we're a gcc system, so do all the overriding we do
// with gcc. This does the overriding of all the 'normal' memory
// allocation. This also defines ReplaceSystemAlloc().
# include "libc_override_gcc_and_weak.h"
// We also have to do some glibc-specific overriding. Some library
// routines on RedHat 9 allocate memory using malloc() and free it
// using __libc_free() (or vice-versa). Since we provide our own
// implementations of malloc/free, we need to make sure that the
// __libc_XXX variants (defined as part of glibc) also point to the
// same implementations. Since it only matters for redhat, we
// do it inside the gcc #ifdef, since redhat uses gcc.
// TODO(csilvers): only do this if we detect we're an old enough glibc?
#define ALIAS(tc_fn) __attribute__ ((alias (#tc_fn)))
extern "C" {
void* __libc_malloc(size_t size) ALIAS(tc_malloc);
void __libc_free(void* ptr) ALIAS(tc_free);
void* __libc_realloc(void* ptr, size_t size) ALIAS(tc_realloc);
void* __libc_calloc(size_t n, size_t size) ALIAS(tc_calloc);
void __libc_cfree(void* ptr) ALIAS(tc_cfree);
void* __libc_memalign(size_t align, size_t s) ALIAS(tc_memalign);
void* __libc_valloc(size_t size) ALIAS(tc_valloc);
void* __libc_pvalloc(size_t size) ALIAS(tc_pvalloc);
int __posix_memalign(void** r, size_t a, size_t s) ALIAS(tc_posix_memalign);
} // extern "C"
#undef ALIAS
#endif // #if defined(__GNUC__) && !defined(__MACH__)
// No need to write ReplaceSystemAlloc(); one of the #includes above
// did it for us.
#endif // TCMALLOC_LIBC_OVERRIDE_GLIBC_INL_H_

View file

@ -0,0 +1,308 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2011, 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: Craig Silverstein <opensource@google.com>
//
// Used to override malloc routines on OS X systems. We use the
// malloc-zone functionality built into OS X to register our malloc
// routine.
//
// 1) We used to use the normal 'override weak libc malloc/etc'
// technique for OS X. This is not optimal because mach does not
// support the 'alias' attribute, so we had to have forwarding
// functions. It also does not work very well with OS X shared
// libraries (dylibs) -- in general, the shared libs don't use
// tcmalloc unless run with the DYLD_FORCE_FLAT_NAMESPACE envvar.
//
// 2) Another approach would be to use an interposition array:
// static const interpose_t interposers[] __attribute__((section("__DATA, __interpose"))) = {
// { (void *)tc_malloc, (void *)malloc },
// { (void *)tc_free, (void *)free },
// };
// This requires the user to set the DYLD_INSERT_LIBRARIES envvar, so
// is not much better.
//
// 3) Registering a new malloc zone avoids all these issues:
// http://www.opensource.apple.com/source/Libc/Libc-583/include/malloc/malloc.h
// http://www.opensource.apple.com/source/Libc/Libc-583/gen/malloc.c
// If we make tcmalloc the default malloc zone (undocumented but
// possible) then all new allocs use it, even those in shared
// libraries. Allocs done before tcmalloc was installed, or in libs
// that aren't using tcmalloc for some reason, will correctly go
// through the malloc-zone interface when free-ing, and will pick up
// the libc free rather than tcmalloc free. So it should "never"
// cause a crash (famous last words).
//
// 4) The routines one must define for one's own malloc have changed
// between OS X versions. This requires some hoops on our part, but
// is only really annoying when it comes to posix_memalign. The right
// behavior there depends on what OS version tcmalloc was compiled on,
// but also what OS version the program is running on. For now, we
// punt and don't implement our own posix_memalign. Apps that really
// care can use tc_posix_memalign directly.
#ifndef TCMALLOC_LIBC_OVERRIDE_OSX_INL_H_
#define TCMALLOC_LIBC_OVERRIDE_OSX_INL_H_
#include <config.h>
#ifdef HAVE_FEATURES_H
#include <features.h>
#endif
#include <gperftools/tcmalloc.h>
#if !defined(__APPLE__)
# error libc_override_glibc-osx.h is for OS X distributions only.
#endif
#include <AvailabilityMacros.h>
#include <malloc/malloc.h>
namespace tcmalloc {
void CentralCacheLockAll();
void CentralCacheUnlockAll();
}
// from AvailabilityMacros.h
#if defined(MAC_OS_X_VERSION_10_6) && \
MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
extern "C" {
// This function is only available on 10.6 (and later) but the
// LibSystem headers do not use AvailabilityMacros.h to handle weak
// importing automatically. This prototype is a copy of the one in
// <malloc/malloc.h> with the WEAK_IMPORT_ATTRBIUTE added.
extern malloc_zone_t *malloc_default_purgeable_zone(void)
WEAK_IMPORT_ATTRIBUTE;
}
#endif
// We need to provide wrappers around all the libc functions.
namespace {
size_t mz_size(malloc_zone_t* zone, const void* ptr) {
if (MallocExtension::instance()->GetOwnership(ptr) != MallocExtension::kOwned)
return 0; // malloc_zone semantics: return 0 if we don't own the memory
// TODO(csilvers): change this method to take a const void*, one day.
return MallocExtension::instance()->GetAllocatedSize(const_cast<void*>(ptr));
}
void* mz_malloc(malloc_zone_t* zone, size_t size) {
return tc_malloc(size);
}
void* mz_calloc(malloc_zone_t* zone, size_t num_items, size_t size) {
return tc_calloc(num_items, size);
}
void* mz_valloc(malloc_zone_t* zone, size_t size) {
return tc_valloc(size);
}
void mz_free(malloc_zone_t* zone, void* ptr) {
return tc_free(ptr);
}
void* mz_realloc(malloc_zone_t* zone, void* ptr, size_t size) {
return tc_realloc(ptr, size);
}
void* mz_memalign(malloc_zone_t* zone, size_t align, size_t size) {
return tc_memalign(align, size);
}
void mz_destroy(malloc_zone_t* zone) {
// A no-op -- we will not be destroyed!
}
// malloc_introspection callbacks. I'm not clear on what all of these do.
kern_return_t mi_enumerator(task_t task, void *,
unsigned type_mask, vm_address_t zone_address,
memory_reader_t reader,
vm_range_recorder_t recorder) {
// Should enumerate all the pointers we have. Seems like a lot of work.
return KERN_FAILURE;
}
size_t mi_good_size(malloc_zone_t *zone, size_t size) {
// I think it's always safe to return size, but we maybe could do better.
return size;
}
boolean_t mi_check(malloc_zone_t *zone) {
return MallocExtension::instance()->VerifyAllMemory();
}
void mi_print(malloc_zone_t *zone, boolean_t verbose) {
int bufsize = 8192;
if (verbose)
bufsize = 102400; // I picked this size arbitrarily
char* buffer = new char[bufsize];
MallocExtension::instance()->GetStats(buffer, bufsize);
fprintf(stdout, "%s", buffer);
delete[] buffer;
}
void mi_log(malloc_zone_t *zone, void *address) {
// I don't think we support anything like this
}
void mi_force_lock(malloc_zone_t *zone) {
tcmalloc::CentralCacheLockAll();
}
void mi_force_unlock(malloc_zone_t *zone) {
tcmalloc::CentralCacheUnlockAll();
}
void mi_statistics(malloc_zone_t *zone, malloc_statistics_t *stats) {
// TODO(csilvers): figure out how to fill these out
stats->blocks_in_use = 0;
stats->size_in_use = 0;
stats->max_size_in_use = 0;
stats->size_allocated = 0;
}
boolean_t mi_zone_locked(malloc_zone_t *zone) {
return false; // Hopefully unneeded by us!
}
} // unnamed namespace
// OS X doesn't have pvalloc, cfree, malloc_statc, etc, so we can just
// define our own. :-) OS X supplies posix_memalign in some versions
// but not others, either strongly or weakly linked, in a way that's
// difficult enough to code to correctly, that I just don't try to
// support either memalign() or posix_memalign(). If you need them
// and are willing to code to tcmalloc, you can use tc_posix_memalign().
extern "C" {
void cfree(void* p) { tc_cfree(p); }
void* pvalloc(size_t s) { return tc_pvalloc(s); }
void malloc_stats(void) { tc_malloc_stats(); }
int mallopt(int cmd, int v) { return tc_mallopt(cmd, v); }
// No struct mallinfo on OS X, so don't define mallinfo().
// An alias for malloc_size(), which OS X defines.
size_t malloc_usable_size(void* p) { return tc_malloc_size(p); }
} // extern "C"
static malloc_zone_t *get_default_zone() {
malloc_zone_t **zones = NULL;
unsigned int num_zones = 0;
/*
* On OSX 10.12, malloc_default_zone returns a special zone that is not
* present in the list of registered zones. That zone uses a "lite zone"
* if one is present (apparently enabled when malloc stack logging is
* enabled), or the first registered zone otherwise. In practice this
* means unless malloc stack logging is enabled, the first registered
* zone is the default.
* So get the list of zones to get the first one, instead of relying on
* malloc_default_zone.
*/
if (KERN_SUCCESS != malloc_get_all_zones(0, NULL, (vm_address_t**) &zones,
&num_zones)) {
/* Reset the value in case the failure happened after it was set. */
num_zones = 0;
}
if (num_zones)
return zones[0];
return malloc_default_zone();
}
static void ReplaceSystemAlloc() {
static malloc_introspection_t tcmalloc_introspection;
memset(&tcmalloc_introspection, 0, sizeof(tcmalloc_introspection));
tcmalloc_introspection.enumerator = &mi_enumerator;
tcmalloc_introspection.good_size = &mi_good_size;
tcmalloc_introspection.check = &mi_check;
tcmalloc_introspection.print = &mi_print;
tcmalloc_introspection.log = &mi_log;
tcmalloc_introspection.force_lock = &mi_force_lock;
tcmalloc_introspection.force_unlock = &mi_force_unlock;
static malloc_zone_t tcmalloc_zone;
memset(&tcmalloc_zone, 0, sizeof(malloc_zone_t));
// Start with a version 4 zone which is used for OS X 10.4 and 10.5.
tcmalloc_zone.version = 4;
tcmalloc_zone.zone_name = "tcmalloc";
tcmalloc_zone.size = &mz_size;
tcmalloc_zone.malloc = &mz_malloc;
tcmalloc_zone.calloc = &mz_calloc;
tcmalloc_zone.valloc = &mz_valloc;
tcmalloc_zone.free = &mz_free;
tcmalloc_zone.realloc = &mz_realloc;
tcmalloc_zone.destroy = &mz_destroy;
tcmalloc_zone.batch_malloc = NULL;
tcmalloc_zone.batch_free = NULL;
tcmalloc_zone.introspect = &tcmalloc_introspection;
// from AvailabilityMacros.h
#if defined(MAC_OS_X_VERSION_10_6) && \
MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6
// Switch to version 6 on OSX 10.6 to support memalign.
tcmalloc_zone.version = 6;
tcmalloc_zone.free_definite_size = NULL;
tcmalloc_zone.memalign = &mz_memalign;
tcmalloc_introspection.zone_locked = &mi_zone_locked;
// Request the default purgable zone to force its creation. The
// current default zone is registered with the purgable zone for
// doing tiny and small allocs. Sadly, it assumes that the default
// zone is the szone implementation from OS X and will crash if it
// isn't. By creating the zone now, this will be true and changing
// the default zone won't cause a problem. This only needs to
// happen when actually running on OS X 10.6 and higher (note the
// ifdef above only checks if we were *compiled* with 10.6 or
// higher; at runtime we have to check if this symbol is defined.)
if (malloc_default_purgeable_zone) {
malloc_default_purgeable_zone();
}
#endif
// Register the tcmalloc zone. At this point, it will not be the
// default zone.
malloc_zone_register(&tcmalloc_zone);
// Unregister and reregister the default zone. Unregistering swaps
// the specified zone with the last one registered which for the
// default zone makes the more recently registered zone the default
// zone. The default zone is then re-registered to ensure that
// allocations made from it earlier will be handled correctly.
// Things are not guaranteed to work that way, but it's how they work now.
malloc_zone_t *default_zone = get_default_zone();
malloc_zone_unregister(default_zone);
malloc_zone_register(default_zone);
}
#endif // TCMALLOC_LIBC_OVERRIDE_OSX_INL_H_

View file

@ -0,0 +1,131 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2011, 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: Craig Silverstein <opensource@google.com>
//
// Used on systems that don't have their own definition of
// malloc/new/etc. (Typically this will be a windows msvcrt.dll that
// has been edited to remove the definitions.) We can just define our
// own as normal functions.
//
// This should also work on systems were all the malloc routines are
// defined as weak symbols, and there's no support for aliasing.
#ifndef TCMALLOC_LIBC_OVERRIDE_REDEFINE_H_
#define TCMALLOC_LIBC_OVERRIDE_REDEFINE_H_
void* operator new(size_t size) { return tc_new(size); }
void operator delete(void* p) CPP_NOTHROW { tc_delete(p); }
void* operator new[](size_t size) { return tc_newarray(size); }
void operator delete[](void* p) CPP_NOTHROW { tc_deletearray(p); }
void* operator new(size_t size, const std::nothrow_t& nt) CPP_NOTHROW {
return tc_new_nothrow(size, nt);
}
void* operator new[](size_t size, const std::nothrow_t& nt) CPP_NOTHROW {
return tc_newarray_nothrow(size, nt);
}
void operator delete(void* ptr, const std::nothrow_t& nt) CPP_NOTHROW {
return tc_delete_nothrow(ptr, nt);
}
void operator delete[](void* ptr, const std::nothrow_t& nt) CPP_NOTHROW {
return tc_deletearray_nothrow(ptr, nt);
}
#ifdef ENABLE_SIZED_DELETE
void operator delete(void* p, size_t s) CPP_NOTHROW { tc_delete_sized(p, s); }
void operator delete[](void* p, size_t s) CPP_NOTHROW{ tc_deletearray_sized(p, s);}
#endif
#if defined(ENABLE_ALIGNED_NEW_DELETE)
void* operator new(size_t size, std::align_val_t al) {
return tc_new_aligned(size, al);
}
void operator delete(void* p, std::align_val_t al) CPP_NOTHROW {
tc_delete_aligned(p, al);
}
void* operator new[](size_t size, std::align_val_t al) {
return tc_newarray_aligned(size, al);
}
void operator delete[](void* p, std::align_val_t al) CPP_NOTHROW {
tc_deletearray_aligned(p, al);
}
void* operator new(size_t size, std::align_val_t al, const std::nothrow_t& nt) CPP_NOTHROW {
return tc_new_aligned_nothrow(size, al, nt);
}
void* operator new[](size_t size, std::align_val_t al, const std::nothrow_t& nt) CPP_NOTHROW {
return tc_newarray_aligned_nothrow(size, al, nt);
}
void operator delete(void* ptr, std::align_val_t al, const std::nothrow_t& nt) CPP_NOTHROW {
return tc_delete_aligned_nothrow(ptr, al, nt);
}
void operator delete[](void* ptr, std::align_val_t al, const std::nothrow_t& nt) CPP_NOTHROW {
return tc_deletearray_aligned_nothrow(ptr, al, nt);
}
#ifdef ENABLE_SIZED_DELETE
void operator delete(void* p, size_t s, std::align_val_t al) CPP_NOTHROW {
tc_delete_sized_aligned(p, s, al);
}
void operator delete[](void* p, size_t s, std::align_val_t al) CPP_NOTHROW {
tc_deletearray_sized_aligned(p, s, al);
}
#endif
#endif // defined(ENABLE_ALIGNED_NEW_DELETE)
extern "C" {
void* malloc(size_t s) { return tc_malloc(s); }
void free(void* p) { tc_free(p); }
void* realloc(void* p, size_t s) { return tc_realloc(p, s); }
void* calloc(size_t n, size_t s) { return tc_calloc(n, s); }
void cfree(void* p) { tc_cfree(p); }
void* memalign(size_t a, size_t s) { return tc_memalign(a, s); }
void* aligned_alloc(size_t a, size_t s) { return tc_memalign(a, s); }
void* valloc(size_t s) { return tc_valloc(s); }
void* pvalloc(size_t s) { return tc_pvalloc(s); }
int posix_memalign(void** r, size_t a, size_t s) {
return tc_posix_memalign(r, a, s);
}
void malloc_stats(void) { tc_malloc_stats(); }
int mallopt(int cmd, int v) { return tc_mallopt(cmd, v); }
#ifdef HAVE_STRUCT_MALLINFO
struct mallinfo mallinfo(void) { return tc_mallinfo(); }
#endif
size_t malloc_size(void* p) { return tc_malloc_size(p); }
size_t malloc_usable_size(void* p) { return tc_malloc_size(p); }
} // extern "C"
// No need to do anything at tcmalloc-registration time: we do it all
// via overriding weak symbols (at link time).
static void ReplaceSystemAlloc() { }
#endif // TCMALLOC_LIBC_OVERRIDE_REDEFINE_H_

View file

@ -0,0 +1,115 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2008, 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 <opensource@google.com>
//
// Some very basic linked list functions for dealing with using void * as
// storage.
#ifndef TCMALLOC_LINKED_LIST_H_
#define TCMALLOC_LINKED_LIST_H_
#include <stddef.h>
namespace tcmalloc {
inline void *SLL_Next(void *t) {
return *(reinterpret_cast<void**>(t));
}
inline void SLL_SetNext(void *t, void *n) {
*(reinterpret_cast<void**>(t)) = n;
}
inline void SLL_Push(void **list, void *element) {
void *next = *list;
*list = element;
SLL_SetNext(element, next);
}
inline void *SLL_Pop(void **list) {
void *result = *list;
*list = SLL_Next(*list);
return result;
}
inline bool SLL_TryPop(void **list, void **rv) {
void *result = *list;
if (!result) {
return false;
}
void *next = SLL_Next(*list);
*list = next;
*rv = result;
return true;
}
// Remove N elements from a linked list to which head points. head will be
// modified to point to the new head. start and end will point to the first
// and last nodes of the range. Note that end will point to NULL after this
// function is called.
inline void SLL_PopRange(void **head, int N, void **start, void **end) {
if (N == 0) {
*start = NULL;
*end = NULL;
return;
}
void *tmp = *head;
for (int i = 1; i < N; ++i) {
tmp = SLL_Next(tmp);
}
*start = *head;
*end = tmp;
*head = SLL_Next(tmp);
// Unlink range from list.
SLL_SetNext(tmp, NULL);
}
inline void SLL_PushRange(void **head, void *start, void *end) {
if (!start) return;
SLL_SetNext(end, *head);
*head = start;
}
inline size_t SLL_Size(void *head) {
int count = 0;
while (head) {
count++;
head = SLL_Next(head);
}
return count;
}
} // namespace tcmalloc
#endif // TCMALLOC_LINKED_LIST_H_

View file

@ -0,0 +1,388 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2005, 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 <opensource@google.com>
#include <config.h>
#include <assert.h>
#include <string.h>
#include <stdio.h>
#if defined HAVE_STDINT_H
#include <stdint.h>
#elif defined HAVE_INTTYPES_H
#include <inttypes.h>
#else
#include <sys/types.h>
#endif
#include <string>
#include "base/dynamic_annotations.h"
#include "base/sysinfo.h" // for FillProcSelfMaps
#ifndef NO_HEAP_CHECK
#include "gperftools/heap-checker.h"
#endif
#include "gperftools/malloc_extension.h"
#include "gperftools/malloc_extension_c.h"
#include "maybe_threads.h"
#include "base/googleinit.h"
using std::string;
using std::vector;
static void DumpAddressMap(string* result) {
*result += "\nMAPPED_LIBRARIES:\n";
// We keep doubling until we get a fit
const size_t old_resultlen = result->size();
for (int amap_size = 10240; amap_size < 10000000; amap_size *= 2) {
result->resize(old_resultlen + amap_size);
bool wrote_all = false;
const int bytes_written =
tcmalloc::FillProcSelfMaps(&((*result)[old_resultlen]), amap_size,
&wrote_all);
if (wrote_all) { // we fit!
(*result)[old_resultlen + bytes_written] = '\0';
result->resize(old_resultlen + bytes_written);
return;
}
}
result->reserve(old_resultlen); // just don't print anything
}
// Note: this routine is meant to be called before threads are spawned.
void MallocExtension::Initialize() {
static bool initialize_called = false;
if (initialize_called) return;
initialize_called = true;
#ifdef __GLIBC__
// GNU libc++ versions 3.3 and 3.4 obey the environment variables
// GLIBCPP_FORCE_NEW and GLIBCXX_FORCE_NEW respectively. Setting
// one of these variables forces the STL default allocator to call
// new() or delete() for each allocation or deletion. Otherwise
// the STL allocator tries to avoid the high cost of doing
// allocations by pooling memory internally. However, tcmalloc
// does allocations really fast, especially for the types of small
// items one sees in STL, so it's better off just using us.
// TODO: control whether we do this via an environment variable?
setenv("GLIBCPP_FORCE_NEW", "1", false /* no overwrite*/);
setenv("GLIBCXX_FORCE_NEW", "1", false /* no overwrite*/);
// Now we need to make the setenv 'stick', which it may not do since
// the env is flakey before main() is called. But luckily stl only
// looks at this env var the first time it tries to do an alloc, and
// caches what it finds. So we just cause an stl alloc here.
string dummy("I need to be allocated");
dummy += "!"; // so the definition of dummy isn't optimized out
#endif /* __GLIBC__ */
}
// SysAllocator implementation
SysAllocator::~SysAllocator() {}
// Default implementation -- does nothing
MallocExtension::~MallocExtension() { }
bool MallocExtension::VerifyAllMemory() { return true; }
bool MallocExtension::VerifyNewMemory(const void* p) { return true; }
bool MallocExtension::VerifyArrayNewMemory(const void* p) { return true; }
bool MallocExtension::VerifyMallocMemory(const void* p) { return true; }
bool MallocExtension::GetNumericProperty(const char* property, size_t* value) {
return false;
}
bool MallocExtension::SetNumericProperty(const char* property, size_t value) {
return false;
}
void MallocExtension::GetStats(char* buffer, int length) {
assert(length > 0);
buffer[0] = '\0';
}
bool MallocExtension::MallocMemoryStats(int* blocks, size_t* total,
int histogram[kMallocHistogramSize]) {
*blocks = 0;
*total = 0;
memset(histogram, 0, sizeof(*histogram) * kMallocHistogramSize);
return true;
}
void** MallocExtension::ReadStackTraces(int* sample_period) {
return NULL;
}
void** MallocExtension::ReadHeapGrowthStackTraces() {
return NULL;
}
void MallocExtension::MarkThreadIdle() {
// Default implementation does nothing
}
void MallocExtension::MarkThreadBusy() {
// Default implementation does nothing
}
SysAllocator* MallocExtension::GetSystemAllocator() {
return NULL;
}
void MallocExtension::SetSystemAllocator(SysAllocator *a) {
// Default implementation does nothing
}
void MallocExtension::ReleaseToSystem(size_t num_bytes) {
// Default implementation does nothing
}
void MallocExtension::ReleaseFreeMemory() {
ReleaseToSystem(static_cast<size_t>(-1)); // SIZE_T_MAX
}
void MallocExtension::SetMemoryReleaseRate(double rate) {
// Default implementation does nothing
}
double MallocExtension::GetMemoryReleaseRate() {
return -1.0;
}
size_t MallocExtension::GetEstimatedAllocatedSize(size_t size) {
return size;
}
size_t MallocExtension::GetAllocatedSize(const void* p) {
assert(GetOwnership(p) != kNotOwned);
return 0;
}
MallocExtension::Ownership MallocExtension::GetOwnership(const void* p) {
return kUnknownOwnership;
}
void MallocExtension::GetFreeListSizes(
vector<MallocExtension::FreeListInfo>* v) {
v->clear();
}
size_t MallocExtension::GetThreadCacheSize() {
return 0;
}
void MallocExtension::MarkThreadTemporarilyIdle() {
// Default implementation does nothing
}
// The current malloc extension object.
static MallocExtension* current_instance;
static void InitModule() {
if (current_instance != NULL) {
return;
}
current_instance = new MallocExtension;
#ifndef NO_HEAP_CHECK
HeapLeakChecker::IgnoreObject(current_instance);
#endif
}
REGISTER_MODULE_INITIALIZER(malloc_extension_init, InitModule())
MallocExtension* MallocExtension::instance() {
InitModule();
return current_instance;
}
void MallocExtension::Register(MallocExtension* implementation) {
InitModule();
// When running under valgrind, our custom malloc is replaced with
// valgrind's one and malloc extensions will not work. (Note:
// callers should be responsible for checking that they are the
// malloc that is really being run, before calling Register. This
// is just here as an extra sanity check.)
if (!RunningOnValgrind()) {
current_instance = implementation;
}
}
// -----------------------------------------------------------------------
// Heap sampling support
// -----------------------------------------------------------------------
namespace {
// Accessors
uintptr_t Count(void** entry) {
return reinterpret_cast<uintptr_t>(entry[0]);
}
uintptr_t Size(void** entry) {
return reinterpret_cast<uintptr_t>(entry[1]);
}
uintptr_t Depth(void** entry) {
return reinterpret_cast<uintptr_t>(entry[2]);
}
void* PC(void** entry, int i) {
return entry[3+i];
}
void PrintCountAndSize(MallocExtensionWriter* writer,
uintptr_t count, uintptr_t size) {
char buf[100];
snprintf(buf, sizeof(buf),
"%6" PRIu64 ": %8" PRIu64 " [%6" PRIu64 ": %8" PRIu64 "] @",
static_cast<uint64>(count),
static_cast<uint64>(size),
static_cast<uint64>(count),
static_cast<uint64>(size));
writer->append(buf, strlen(buf));
}
void PrintHeader(MallocExtensionWriter* writer,
const char* label, void** entries) {
// Compute the total count and total size
uintptr_t total_count = 0;
uintptr_t total_size = 0;
for (void** entry = entries; Count(entry) != 0; entry += 3 + Depth(entry)) {
total_count += Count(entry);
total_size += Size(entry);
}
const char* const kTitle = "heap profile: ";
writer->append(kTitle, strlen(kTitle));
PrintCountAndSize(writer, total_count, total_size);
writer->append(" ", 1);
writer->append(label, strlen(label));
writer->append("\n", 1);
}
void PrintStackEntry(MallocExtensionWriter* writer, void** entry) {
PrintCountAndSize(writer, Count(entry), Size(entry));
for (int i = 0; i < Depth(entry); i++) {
char buf[32];
snprintf(buf, sizeof(buf), " %p", PC(entry, i));
writer->append(buf, strlen(buf));
}
writer->append("\n", 1);
}
}
void MallocExtension::GetHeapSample(MallocExtensionWriter* writer) {
int sample_period = 0;
void** entries = ReadStackTraces(&sample_period);
if (entries == NULL) {
const char* const kErrorMsg =
"This malloc implementation does not support sampling.\n"
"As of 2005/01/26, only tcmalloc supports sampling, and\n"
"you are probably running a binary that does not use\n"
"tcmalloc.\n";
writer->append(kErrorMsg, strlen(kErrorMsg));
return;
}
char label[32];
sprintf(label, "heap_v2/%d", sample_period);
PrintHeader(writer, label, entries);
for (void** entry = entries; Count(entry) != 0; entry += 3 + Depth(entry)) {
PrintStackEntry(writer, entry);
}
delete[] entries;
DumpAddressMap(writer);
}
void MallocExtension::GetHeapGrowthStacks(MallocExtensionWriter* writer) {
void** entries = ReadHeapGrowthStackTraces();
if (entries == NULL) {
const char* const kErrorMsg =
"This malloc implementation does not support "
"ReadHeapGrowthStackTraces().\n"
"As of 2005/09/27, only tcmalloc supports this, and you\n"
"are probably running a binary that does not use tcmalloc.\n";
writer->append(kErrorMsg, strlen(kErrorMsg));
return;
}
// Do not canonicalize the stack entries, so that we get a
// time-ordered list of stack traces, which may be useful if the
// client wants to focus on the latest stack traces.
PrintHeader(writer, "growth", entries);
for (void** entry = entries; Count(entry) != 0; entry += 3 + Depth(entry)) {
PrintStackEntry(writer, entry);
}
delete[] entries;
DumpAddressMap(writer);
}
void MallocExtension::Ranges(void* arg, RangeFunction func) {
// No callbacks by default
}
// These are C shims that work on the current instance.
#define C_SHIM(fn, retval, paramlist, arglist) \
extern "C" PERFTOOLS_DLL_DECL retval MallocExtension_##fn paramlist { \
return MallocExtension::instance()->fn arglist; \
}
C_SHIM(VerifyAllMemory, int, (void), ());
C_SHIM(VerifyNewMemory, int, (const void* p), (p));
C_SHIM(VerifyArrayNewMemory, int, (const void* p), (p));
C_SHIM(VerifyMallocMemory, int, (const void* p), (p));
C_SHIM(MallocMemoryStats, int,
(int* blocks, size_t* total, int histogram[kMallocHistogramSize]),
(blocks, total, histogram));
C_SHIM(GetStats, void,
(char* buffer, int buffer_length), (buffer, buffer_length));
C_SHIM(GetNumericProperty, int,
(const char* property, size_t* value), (property, value));
C_SHIM(SetNumericProperty, int,
(const char* property, size_t value), (property, value));
C_SHIM(MarkThreadIdle, void, (void), ());
C_SHIM(MarkThreadBusy, void, (void), ());
C_SHIM(ReleaseFreeMemory, void, (void), ());
C_SHIM(ReleaseToSystem, void, (size_t num_bytes), (num_bytes));
C_SHIM(GetEstimatedAllocatedSize, size_t, (size_t size), (size));
C_SHIM(GetAllocatedSize, size_t, (const void* p), (p));
C_SHIM(GetThreadCacheSize, size_t, (void), ());
C_SHIM(MarkThreadTemporarilyIdle, void, (void), ());
// Can't use the shim here because of the need to translate the enums.
extern "C"
MallocExtension_Ownership MallocExtension_GetOwnership(const void* p) {
return static_cast<MallocExtension_Ownership>(
MallocExtension::instance()->GetOwnership(p));
}

View file

@ -0,0 +1,249 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2005, 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
//
// This has the implementation details of malloc_hook that are needed
// to use malloc-hook inside the tcmalloc system. It does not hold
// any of the client-facing calls that are used to add new hooks.
#ifndef _MALLOC_HOOK_INL_H_
#define _MALLOC_HOOK_INL_H_
#include <stddef.h>
#include <sys/types.h>
#include "base/atomicops.h"
#include "base/basictypes.h"
#include <gperftools/malloc_hook.h>
#include "common.h" // for UNLIKELY
namespace base { namespace internal {
// Capacity of 8 means that HookList is 9 words.
static const int kHookListCapacity = 8;
// last entry is reserved for deprecated "singular" hooks. So we have
// 7 "normal" hooks per list
static const int kHookListMaxValues = 7;
static const int kHookListSingularIdx = 7;
// HookList: a class that provides synchronized insertions and removals and
// lockless traversal. Most of the implementation is in malloc_hook.cc.
template <typename T>
struct PERFTOOLS_DLL_DECL HookList {
COMPILE_ASSERT(sizeof(T) <= sizeof(AtomicWord), T_should_fit_in_AtomicWord);
// Adds value to the list. Note that duplicates are allowed. Thread-safe and
// blocking (acquires hooklist_spinlock). Returns true on success; false
// otherwise (failures include invalid value and no space left).
bool Add(T value);
void FixupPrivEndLocked();
// Removes the first entry matching value from the list. Thread-safe and
// blocking (acquires hooklist_spinlock). Returns true on success; false
// otherwise (failures include invalid value and no value found).
bool Remove(T value);
// Store up to n values of the list in output_array, and return the number of
// elements stored. Thread-safe and non-blocking. This is fast (one memory
// access) if the list is empty.
int Traverse(T* output_array, int n) const;
// Fast inline implementation for fast path of Invoke*Hook.
bool empty() const {
return base::subtle::NoBarrier_Load(&priv_end) == 0;
}
// Used purely to handle deprecated singular hooks
T GetSingular() const {
const AtomicWord *place = &priv_data[kHookListSingularIdx];
return bit_cast<T>(base::subtle::NoBarrier_Load(place));
}
T ExchangeSingular(T new_val);
// This internal data is not private so that the class is an aggregate and can
// be initialized by the linker. Don't access this directly. Use the
// INIT_HOOK_LIST macro in malloc_hook.cc.
// One more than the index of the last valid element in priv_data. During
// 'Remove' this may be past the last valid element in priv_data, but
// subsequent values will be 0.
//
// Index kHookListCapacity-1 is reserved as 'deprecated' single hook pointer
AtomicWord priv_end;
AtomicWord priv_data[kHookListCapacity];
};
ATTRIBUTE_VISIBILITY_HIDDEN extern HookList<MallocHook::NewHook> new_hooks_;
ATTRIBUTE_VISIBILITY_HIDDEN extern HookList<MallocHook::DeleteHook> delete_hooks_;
ATTRIBUTE_VISIBILITY_HIDDEN extern HookList<MallocHook::PreMmapHook> premmap_hooks_;
ATTRIBUTE_VISIBILITY_HIDDEN extern HookList<MallocHook::MmapHook> mmap_hooks_;
ATTRIBUTE_VISIBILITY_HIDDEN extern HookList<MallocHook::MmapReplacement> mmap_replacement_;
ATTRIBUTE_VISIBILITY_HIDDEN extern HookList<MallocHook::MunmapHook> munmap_hooks_;
ATTRIBUTE_VISIBILITY_HIDDEN extern HookList<MallocHook::MunmapReplacement> munmap_replacement_;
ATTRIBUTE_VISIBILITY_HIDDEN extern HookList<MallocHook::MremapHook> mremap_hooks_;
ATTRIBUTE_VISIBILITY_HIDDEN extern HookList<MallocHook::PreSbrkHook> presbrk_hooks_;
ATTRIBUTE_VISIBILITY_HIDDEN extern HookList<MallocHook::SbrkHook> sbrk_hooks_;
} } // namespace base::internal
// The following method is DEPRECATED
inline MallocHook::NewHook MallocHook::GetNewHook() {
return base::internal::new_hooks_.GetSingular();
}
inline void MallocHook::InvokeNewHook(const void* p, size_t s) {
if (PREDICT_FALSE(!base::internal::new_hooks_.empty())) {
InvokeNewHookSlow(p, s);
}
}
// The following method is DEPRECATED
inline MallocHook::DeleteHook MallocHook::GetDeleteHook() {
return base::internal::delete_hooks_.GetSingular();
}
inline void MallocHook::InvokeDeleteHook(const void* p) {
if (PREDICT_FALSE(!base::internal::delete_hooks_.empty())) {
InvokeDeleteHookSlow(p);
}
}
// The following method is DEPRECATED
inline MallocHook::PreMmapHook MallocHook::GetPreMmapHook() {
return base::internal::premmap_hooks_.GetSingular();
}
inline void MallocHook::InvokePreMmapHook(const void* start,
size_t size,
int protection,
int flags,
int fd,
off_t offset) {
if (!base::internal::premmap_hooks_.empty()) {
InvokePreMmapHookSlow(start, size, protection, flags, fd, offset);
}
}
// The following method is DEPRECATED
inline MallocHook::MmapHook MallocHook::GetMmapHook() {
return base::internal::mmap_hooks_.GetSingular();
}
inline void MallocHook::InvokeMmapHook(const void* result,
const void* start,
size_t size,
int protection,
int flags,
int fd,
off_t offset) {
if (!base::internal::mmap_hooks_.empty()) {
InvokeMmapHookSlow(result, start, size, protection, flags, fd, offset);
}
}
inline bool MallocHook::InvokeMmapReplacement(const void* start,
size_t size,
int protection,
int flags,
int fd,
off_t offset,
void** result) {
if (!base::internal::mmap_replacement_.empty()) {
return InvokeMmapReplacementSlow(start, size,
protection, flags,
fd, offset,
result);
}
return false;
}
// The following method is DEPRECATED
inline MallocHook::MunmapHook MallocHook::GetMunmapHook() {
return base::internal::munmap_hooks_.GetSingular();
}
inline void MallocHook::InvokeMunmapHook(const void* p, size_t size) {
if (!base::internal::munmap_hooks_.empty()) {
InvokeMunmapHookSlow(p, size);
}
}
inline bool MallocHook::InvokeMunmapReplacement(
const void* p, size_t size, int* result) {
if (!base::internal::mmap_replacement_.empty()) {
return InvokeMunmapReplacementSlow(p, size, result);
}
return false;
}
// The following method is DEPRECATED
inline MallocHook::MremapHook MallocHook::GetMremapHook() {
return base::internal::mremap_hooks_.GetSingular();
}
inline void MallocHook::InvokeMremapHook(const void* result,
const void* old_addr,
size_t old_size,
size_t new_size,
int flags,
const void* new_addr) {
if (!base::internal::mremap_hooks_.empty()) {
InvokeMremapHookSlow(result, old_addr, old_size, new_size, flags, new_addr);
}
}
// The following method is DEPRECATED
inline MallocHook::PreSbrkHook MallocHook::GetPreSbrkHook() {
return base::internal::presbrk_hooks_.GetSingular();
}
inline void MallocHook::InvokePreSbrkHook(ptrdiff_t increment) {
if (!base::internal::presbrk_hooks_.empty() && increment != 0) {
InvokePreSbrkHookSlow(increment);
}
}
// The following method is DEPRECATED
inline MallocHook::SbrkHook MallocHook::GetSbrkHook() {
return base::internal::sbrk_hooks_.GetSingular();
}
inline void MallocHook::InvokeSbrkHook(const void* result,
ptrdiff_t increment) {
if (!base::internal::sbrk_hooks_.empty() && increment != 0) {
InvokeSbrkHookSlow(result, increment);
}
}
#endif /* _MALLOC_HOOK_INL_H_ */

View file

@ -0,0 +1,711 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2005, 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 <opensource@google.com>
#include <config.h>
// Disable the glibc prototype of mremap(), as older versions of the
// system headers define this function with only four arguments,
// whereas newer versions allow an optional fifth argument:
#ifdef HAVE_MMAP
# define mremap glibc_mremap
# include <sys/mman.h>
# undef mremap
#endif
#include <stddef.h>
#ifdef HAVE_STDINT_H
#include <stdint.h>
#endif
#include <algorithm>
#include "base/logging.h"
#include "base/spinlock.h"
#include "maybe_emergency_malloc.h"
#include "maybe_threads.h"
#include "malloc_hook-inl.h"
#include <gperftools/malloc_hook.h>
// This #ifdef should almost never be set. Set NO_TCMALLOC_SAMPLES if
// you're porting to a system where you really can't get a stacktrace.
#ifdef NO_TCMALLOC_SAMPLES
// We use #define so code compiles even if you #include stacktrace.h somehow.
# define GetStackTrace(stack, depth, skip) (0)
#else
# include <gperftools/stacktrace.h>
#endif
// __THROW is defined in glibc systems. It means, counter-intuitively,
// "This function will never throw an exception." It's an optional
// optimization tool, but we may need to use it to match glibc prototypes.
#ifndef __THROW // I guess we're not on a glibc system
# define __THROW // __THROW is just an optimization, so ok to make it ""
#endif
using std::copy;
// Declaration of default weak initialization function, that can be overridden
// by linking-in a strong definition (as heap-checker.cc does). This is
// extern "C" so that it doesn't trigger gold's --detect-odr-violations warning,
// which only looks at C++ symbols.
//
// This function is declared here as weak, and defined later, rather than a more
// straightforward simple weak definition, as a workround for an icc compiler
// issue ((Intel reference 290819). This issue causes icc to resolve weak
// symbols too early, at compile rather than link time. By declaring it (weak)
// here, then defining it below after its use, we can avoid the problem.
extern "C" {
ATTRIBUTE_WEAK void MallocHook_InitAtFirstAllocation_HeapLeakChecker();
}
namespace {
void RemoveInitialHooksAndCallInitializers(); // below.
pthread_once_t once = PTHREAD_ONCE_INIT;
// These hooks are installed in MallocHook as the only initial hooks. The first
// hook that is called will run RemoveInitialHooksAndCallInitializers (see the
// definition below) and then redispatch to any malloc hooks installed by
// RemoveInitialHooksAndCallInitializers.
//
// Note(llib): there is a possibility of a race in the event that there are
// multiple threads running before the first allocation. This is pretty
// difficult to achieve, but if it is then multiple threads may concurrently do
// allocations. The first caller will call
// RemoveInitialHooksAndCallInitializers via one of the initial hooks. A
// concurrent allocation may, depending on timing either:
// * still have its initial malloc hook installed, run that and block on waiting
// for the first caller to finish its call to
// RemoveInitialHooksAndCallInitializers, and proceed normally.
// * occur some time during the RemoveInitialHooksAndCallInitializers call, at
// which point there could be no initial hooks and the subsequent hooks that
// are about to be set up by RemoveInitialHooksAndCallInitializers haven't
// been installed yet. I think the worst we can get is that some allocations
// will not get reported to some hooks set by the initializers called from
// RemoveInitialHooksAndCallInitializers.
void InitialNewHook(const void* ptr, size_t size) {
perftools_pthread_once(&once, &RemoveInitialHooksAndCallInitializers);
MallocHook::InvokeNewHook(ptr, size);
}
void InitialPreMMapHook(const void* start,
size_t size,
int protection,
int flags,
int fd,
off_t offset) {
perftools_pthread_once(&once, &RemoveInitialHooksAndCallInitializers);
MallocHook::InvokePreMmapHook(start, size, protection, flags, fd, offset);
}
void InitialPreSbrkHook(ptrdiff_t increment) {
perftools_pthread_once(&once, &RemoveInitialHooksAndCallInitializers);
MallocHook::InvokePreSbrkHook(increment);
}
// This function is called at most once by one of the above initial malloc
// hooks. It removes all initial hooks and initializes all other clients that
// want to get control at the very first memory allocation. The initializers
// may assume that the initial malloc hooks have been removed. The initializers
// may set up malloc hooks and allocate memory.
void RemoveInitialHooksAndCallInitializers() {
RAW_CHECK(MallocHook::RemoveNewHook(&InitialNewHook), "");
RAW_CHECK(MallocHook::RemovePreMmapHook(&InitialPreMMapHook), "");
RAW_CHECK(MallocHook::RemovePreSbrkHook(&InitialPreSbrkHook), "");
// HeapLeakChecker is currently the only module that needs to get control on
// the first memory allocation, but one can add other modules by following the
// same weak/strong function pattern.
MallocHook_InitAtFirstAllocation_HeapLeakChecker();
}
} // namespace
// Weak default initialization function that must go after its use.
extern "C" void MallocHook_InitAtFirstAllocation_HeapLeakChecker() {
// Do nothing.
}
namespace base { namespace internal {
// This lock is shared between all implementations of HookList::Add & Remove.
// The potential for contention is very small. This needs to be a SpinLock and
// not a Mutex since it's possible for Mutex locking to allocate memory (e.g.,
// per-thread allocation in debug builds), which could cause infinite recursion.
static SpinLock hooklist_spinlock(base::LINKER_INITIALIZED);
template <typename T>
bool HookList<T>::Add(T value_as_t) {
AtomicWord value = bit_cast<AtomicWord>(value_as_t);
if (value == 0) {
return false;
}
SpinLockHolder l(&hooklist_spinlock);
// Find the first slot in data that is 0.
int index = 0;
while ((index < kHookListMaxValues) &&
(base::subtle::NoBarrier_Load(&priv_data[index]) != 0)) {
++index;
}
if (index == kHookListMaxValues) {
return false;
}
AtomicWord prev_num_hooks = base::subtle::Acquire_Load(&priv_end);
base::subtle::NoBarrier_Store(&priv_data[index], value);
if (prev_num_hooks <= index) {
base::subtle::NoBarrier_Store(&priv_end, index + 1);
}
return true;
}
template <typename T>
void HookList<T>::FixupPrivEndLocked() {
AtomicWord hooks_end = base::subtle::NoBarrier_Load(&priv_end);
while ((hooks_end > 0) &&
(base::subtle::NoBarrier_Load(&priv_data[hooks_end - 1]) == 0)) {
--hooks_end;
}
base::subtle::NoBarrier_Store(&priv_end, hooks_end);
}
template <typename T>
bool HookList<T>::Remove(T value_as_t) {
if (value_as_t == 0) {
return false;
}
SpinLockHolder l(&hooklist_spinlock);
AtomicWord hooks_end = base::subtle::NoBarrier_Load(&priv_end);
int index = 0;
while (index < hooks_end && value_as_t != bit_cast<T>(
base::subtle::NoBarrier_Load(&priv_data[index]))) {
++index;
}
if (index == hooks_end) {
return false;
}
base::subtle::NoBarrier_Store(&priv_data[index], 0);
FixupPrivEndLocked();
return true;
}
template <typename T>
int HookList<T>::Traverse(T* output_array, int n) const {
AtomicWord hooks_end = base::subtle::Acquire_Load(&priv_end);
int actual_hooks_end = 0;
for (int i = 0; i < hooks_end && n > 0; ++i) {
AtomicWord data = base::subtle::Acquire_Load(&priv_data[i]);
if (data != 0) {
*output_array++ = bit_cast<T>(data);
++actual_hooks_end;
--n;
}
}
return actual_hooks_end;
}
template <typename T>
T HookList<T>::ExchangeSingular(T value_as_t) {
AtomicWord value = bit_cast<AtomicWord>(value_as_t);
AtomicWord old_value;
SpinLockHolder l(&hooklist_spinlock);
old_value = base::subtle::NoBarrier_Load(&priv_data[kHookListSingularIdx]);
base::subtle::NoBarrier_Store(&priv_data[kHookListSingularIdx], value);
if (value != 0) {
base::subtle::NoBarrier_Store(&priv_end, kHookListSingularIdx + 1);
} else {
FixupPrivEndLocked();
}
return bit_cast<T>(old_value);
}
// Initialize a HookList (optionally with the given initial_value in index 0).
#define INIT_HOOK_LIST { 0 }
#define INIT_HOOK_LIST_WITH_VALUE(initial_value) \
{ 1, { reinterpret_cast<AtomicWord>(initial_value) } }
// Explicit instantiation for malloc_hook_test.cc. This ensures all the methods
// are instantiated.
template struct HookList<MallocHook::NewHook>;
HookList<MallocHook::NewHook> new_hooks_ =
INIT_HOOK_LIST_WITH_VALUE(&InitialNewHook);
HookList<MallocHook::DeleteHook> delete_hooks_ = INIT_HOOK_LIST;
HookList<MallocHook::PreMmapHook> premmap_hooks_ =
INIT_HOOK_LIST_WITH_VALUE(&InitialPreMMapHook);
HookList<MallocHook::MmapHook> mmap_hooks_ = INIT_HOOK_LIST;
HookList<MallocHook::MunmapHook> munmap_hooks_ = INIT_HOOK_LIST;
HookList<MallocHook::MremapHook> mremap_hooks_ = INIT_HOOK_LIST;
HookList<MallocHook::PreSbrkHook> presbrk_hooks_ =
INIT_HOOK_LIST_WITH_VALUE(InitialPreSbrkHook);
HookList<MallocHook::SbrkHook> sbrk_hooks_ = INIT_HOOK_LIST;
// These lists contain either 0 or 1 hooks.
HookList<MallocHook::MmapReplacement> mmap_replacement_ = { 0 };
HookList<MallocHook::MunmapReplacement> munmap_replacement_ = { 0 };
#undef INIT_HOOK_LIST_WITH_VALUE
#undef INIT_HOOK_LIST
} } // namespace base::internal
using base::internal::kHookListMaxValues;
using base::internal::new_hooks_;
using base::internal::delete_hooks_;
using base::internal::premmap_hooks_;
using base::internal::mmap_hooks_;
using base::internal::mmap_replacement_;
using base::internal::munmap_hooks_;
using base::internal::munmap_replacement_;
using base::internal::mremap_hooks_;
using base::internal::presbrk_hooks_;
using base::internal::sbrk_hooks_;
// These are available as C bindings as well as C++, hence their
// definition outside the MallocHook class.
extern "C"
int MallocHook_AddNewHook(MallocHook_NewHook hook) {
RAW_VLOG(10, "AddNewHook(%p)", hook);
return new_hooks_.Add(hook);
}
extern "C"
int MallocHook_RemoveNewHook(MallocHook_NewHook hook) {
RAW_VLOG(10, "RemoveNewHook(%p)", hook);
return new_hooks_.Remove(hook);
}
extern "C"
int MallocHook_AddDeleteHook(MallocHook_DeleteHook hook) {
RAW_VLOG(10, "AddDeleteHook(%p)", hook);
return delete_hooks_.Add(hook);
}
extern "C"
int MallocHook_RemoveDeleteHook(MallocHook_DeleteHook hook) {
RAW_VLOG(10, "RemoveDeleteHook(%p)", hook);
return delete_hooks_.Remove(hook);
}
extern "C"
int MallocHook_AddPreMmapHook(MallocHook_PreMmapHook hook) {
RAW_VLOG(10, "AddPreMmapHook(%p)", hook);
return premmap_hooks_.Add(hook);
}
extern "C"
int MallocHook_RemovePreMmapHook(MallocHook_PreMmapHook hook) {
RAW_VLOG(10, "RemovePreMmapHook(%p)", hook);
return premmap_hooks_.Remove(hook);
}
extern "C"
int MallocHook_SetMmapReplacement(MallocHook_MmapReplacement hook) {
RAW_VLOG(10, "SetMmapReplacement(%p)", hook);
// NOTE this is a best effort CHECK. Concurrent sets could succeed since
// this test is outside of the Add spin lock.
RAW_CHECK(mmap_replacement_.empty(), "Only one MMapReplacement is allowed.");
return mmap_replacement_.Add(hook);
}
extern "C"
int MallocHook_RemoveMmapReplacement(MallocHook_MmapReplacement hook) {
RAW_VLOG(10, "RemoveMmapReplacement(%p)", hook);
return mmap_replacement_.Remove(hook);
}
extern "C"
int MallocHook_AddMmapHook(MallocHook_MmapHook hook) {
RAW_VLOG(10, "AddMmapHook(%p)", hook);
return mmap_hooks_.Add(hook);
}
extern "C"
int MallocHook_RemoveMmapHook(MallocHook_MmapHook hook) {
RAW_VLOG(10, "RemoveMmapHook(%p)", hook);
return mmap_hooks_.Remove(hook);
}
extern "C"
int MallocHook_AddMunmapHook(MallocHook_MunmapHook hook) {
RAW_VLOG(10, "AddMunmapHook(%p)", hook);
return munmap_hooks_.Add(hook);
}
extern "C"
int MallocHook_RemoveMunmapHook(MallocHook_MunmapHook hook) {
RAW_VLOG(10, "RemoveMunmapHook(%p)", hook);
return munmap_hooks_.Remove(hook);
}
extern "C"
int MallocHook_SetMunmapReplacement(MallocHook_MunmapReplacement hook) {
RAW_VLOG(10, "SetMunmapReplacement(%p)", hook);
// NOTE this is a best effort CHECK. Concurrent sets could succeed since
// this test is outside of the Add spin lock.
RAW_CHECK(munmap_replacement_.empty(),
"Only one MunmapReplacement is allowed.");
return munmap_replacement_.Add(hook);
}
extern "C"
int MallocHook_RemoveMunmapReplacement(MallocHook_MunmapReplacement hook) {
RAW_VLOG(10, "RemoveMunmapReplacement(%p)", hook);
return munmap_replacement_.Remove(hook);
}
extern "C"
int MallocHook_AddMremapHook(MallocHook_MremapHook hook) {
RAW_VLOG(10, "AddMremapHook(%p)", hook);
return mremap_hooks_.Add(hook);
}
extern "C"
int MallocHook_RemoveMremapHook(MallocHook_MremapHook hook) {
RAW_VLOG(10, "RemoveMremapHook(%p)", hook);
return mremap_hooks_.Remove(hook);
}
extern "C"
int MallocHook_AddPreSbrkHook(MallocHook_PreSbrkHook hook) {
RAW_VLOG(10, "AddPreSbrkHook(%p)", hook);
return presbrk_hooks_.Add(hook);
}
extern "C"
int MallocHook_RemovePreSbrkHook(MallocHook_PreSbrkHook hook) {
RAW_VLOG(10, "RemovePreSbrkHook(%p)", hook);
return presbrk_hooks_.Remove(hook);
}
extern "C"
int MallocHook_AddSbrkHook(MallocHook_SbrkHook hook) {
RAW_VLOG(10, "AddSbrkHook(%p)", hook);
return sbrk_hooks_.Add(hook);
}
extern "C"
int MallocHook_RemoveSbrkHook(MallocHook_SbrkHook hook) {
RAW_VLOG(10, "RemoveSbrkHook(%p)", hook);
return sbrk_hooks_.Remove(hook);
}
// The code below is DEPRECATED.
extern "C"
MallocHook_NewHook MallocHook_SetNewHook(MallocHook_NewHook hook) {
RAW_VLOG(10, "SetNewHook(%p)", hook);
return new_hooks_.ExchangeSingular(hook);
}
extern "C"
MallocHook_DeleteHook MallocHook_SetDeleteHook(MallocHook_DeleteHook hook) {
RAW_VLOG(10, "SetDeleteHook(%p)", hook);
return delete_hooks_.ExchangeSingular(hook);
}
extern "C"
MallocHook_PreMmapHook MallocHook_SetPreMmapHook(MallocHook_PreMmapHook hook) {
RAW_VLOG(10, "SetPreMmapHook(%p)", hook);
return premmap_hooks_.ExchangeSingular(hook);
}
extern "C"
MallocHook_MmapHook MallocHook_SetMmapHook(MallocHook_MmapHook hook) {
RAW_VLOG(10, "SetMmapHook(%p)", hook);
return mmap_hooks_.ExchangeSingular(hook);
}
extern "C"
MallocHook_MunmapHook MallocHook_SetMunmapHook(MallocHook_MunmapHook hook) {
RAW_VLOG(10, "SetMunmapHook(%p)", hook);
return munmap_hooks_.ExchangeSingular(hook);
}
extern "C"
MallocHook_MremapHook MallocHook_SetMremapHook(MallocHook_MremapHook hook) {
RAW_VLOG(10, "SetMremapHook(%p)", hook);
return mremap_hooks_.ExchangeSingular(hook);
}
extern "C"
MallocHook_PreSbrkHook MallocHook_SetPreSbrkHook(MallocHook_PreSbrkHook hook) {
RAW_VLOG(10, "SetPreSbrkHook(%p)", hook);
return presbrk_hooks_.ExchangeSingular(hook);
}
extern "C"
MallocHook_SbrkHook MallocHook_SetSbrkHook(MallocHook_SbrkHook hook) {
RAW_VLOG(10, "SetSbrkHook(%p)", hook);
return sbrk_hooks_.ExchangeSingular(hook);
}
// End of DEPRECATED code section.
// Note: embedding the function calls inside the traversal of HookList would be
// very confusing, as it is legal for a hook to remove itself and add other
// hooks. Doing traversal first, and then calling the hooks ensures we only
// call the hooks registered at the start.
#define INVOKE_HOOKS(HookType, hook_list, args) do { \
HookType hooks[kHookListMaxValues]; \
int num_hooks = hook_list.Traverse(hooks, kHookListMaxValues); \
for (int i = 0; i < num_hooks; ++i) { \
(*hooks[i])args; \
} \
} while (0)
// There should only be one replacement. Return the result of the first
// one, or false if there is none.
#define INVOKE_REPLACEMENT(HookType, hook_list, args) do { \
HookType hooks[kHookListMaxValues]; \
int num_hooks = hook_list.Traverse(hooks, kHookListMaxValues); \
return (num_hooks > 0 && (*hooks[0])args); \
} while (0)
void MallocHook::InvokeNewHookSlow(const void* p, size_t s) {
if (tcmalloc::IsEmergencyPtr(p)) {
return;
}
INVOKE_HOOKS(NewHook, new_hooks_, (p, s));
}
void MallocHook::InvokeDeleteHookSlow(const void* p) {
if (tcmalloc::IsEmergencyPtr(p)) {
return;
}
INVOKE_HOOKS(DeleteHook, delete_hooks_, (p));
}
void MallocHook::InvokePreMmapHookSlow(const void* start,
size_t size,
int protection,
int flags,
int fd,
off_t offset) {
INVOKE_HOOKS(PreMmapHook, premmap_hooks_, (start, size, protection, flags, fd,
offset));
}
void MallocHook::InvokeMmapHookSlow(const void* result,
const void* start,
size_t size,
int protection,
int flags,
int fd,
off_t offset) {
INVOKE_HOOKS(MmapHook, mmap_hooks_, (result, start, size, protection, flags,
fd, offset));
}
bool MallocHook::InvokeMmapReplacementSlow(const void* start,
size_t size,
int protection,
int flags,
int fd,
off_t offset,
void** result) {
INVOKE_REPLACEMENT(MmapReplacement, mmap_replacement_,
(start, size, protection, flags, fd, offset, result));
}
void MallocHook::InvokeMunmapHookSlow(const void* p, size_t s) {
INVOKE_HOOKS(MunmapHook, munmap_hooks_, (p, s));
}
bool MallocHook::InvokeMunmapReplacementSlow(const void* p,
size_t s,
int* result) {
INVOKE_REPLACEMENT(MunmapReplacement, munmap_replacement_, (p, s, result));
}
void MallocHook::InvokeMremapHookSlow(const void* result,
const void* old_addr,
size_t old_size,
size_t new_size,
int flags,
const void* new_addr) {
INVOKE_HOOKS(MremapHook, mremap_hooks_, (result, old_addr, old_size, new_size,
flags, new_addr));
}
void MallocHook::InvokePreSbrkHookSlow(ptrdiff_t increment) {
INVOKE_HOOKS(PreSbrkHook, presbrk_hooks_, (increment));
}
void MallocHook::InvokeSbrkHookSlow(const void* result, ptrdiff_t increment) {
INVOKE_HOOKS(SbrkHook, sbrk_hooks_, (result, increment));
}
#undef INVOKE_HOOKS
#ifndef NO_TCMALLOC_SAMPLES
DEFINE_ATTRIBUTE_SECTION_VARS(google_malloc);
DECLARE_ATTRIBUTE_SECTION_VARS(google_malloc);
// actual functions are in debugallocation.cc or tcmalloc.cc
DEFINE_ATTRIBUTE_SECTION_VARS(malloc_hook);
DECLARE_ATTRIBUTE_SECTION_VARS(malloc_hook);
// actual functions are in this file, malloc_hook.cc, and low_level_alloc.cc
#define ADDR_IN_ATTRIBUTE_SECTION(addr, name) \
(reinterpret_cast<uintptr_t>(ATTRIBUTE_SECTION_START(name)) <= \
reinterpret_cast<uintptr_t>(addr) && \
reinterpret_cast<uintptr_t>(addr) < \
reinterpret_cast<uintptr_t>(ATTRIBUTE_SECTION_STOP(name)))
// Return true iff 'caller' is a return address within a function
// that calls one of our hooks via MallocHook:Invoke*.
// A helper for GetCallerStackTrace.
static inline bool InHookCaller(const void* caller) {
return ADDR_IN_ATTRIBUTE_SECTION(caller, google_malloc) ||
ADDR_IN_ATTRIBUTE_SECTION(caller, malloc_hook);
// We can use one section for everything except tcmalloc_or_debug
// due to its special linkage mode, which prevents merging of the sections.
}
#undef ADDR_IN_ATTRIBUTE_SECTION
static bool checked_sections = false;
static inline void CheckInHookCaller() {
if (!checked_sections) {
INIT_ATTRIBUTE_SECTION_VARS(google_malloc);
if (ATTRIBUTE_SECTION_START(google_malloc) ==
ATTRIBUTE_SECTION_STOP(google_malloc)) {
RAW_LOG(ERROR, "google_malloc section is missing, "
"thus InHookCaller is broken!");
}
INIT_ATTRIBUTE_SECTION_VARS(malloc_hook);
if (ATTRIBUTE_SECTION_START(malloc_hook) ==
ATTRIBUTE_SECTION_STOP(malloc_hook)) {
RAW_LOG(ERROR, "malloc_hook section is missing, "
"thus InHookCaller is broken!");
}
checked_sections = true;
}
}
#endif // !NO_TCMALLOC_SAMPLES
// We can improve behavior/compactness of this function
// if we pass a generic test function (with a generic arg)
// into the implementations for GetStackTrace instead of the skip_count.
extern "C" int MallocHook_GetCallerStackTrace(void** result, int max_depth,
int skip_count) {
#if defined(NO_TCMALLOC_SAMPLES)
return 0;
#elif !defined(HAVE_ATTRIBUTE_SECTION_START)
// Fall back to GetStackTrace and good old but fragile frame skip counts.
// Note: this path is inaccurate when a hook is not called directly by an
// allocation function but is daisy-chained through another hook,
// search for MallocHook::(Get|Set|Invoke)* to find such cases.
return GetStackTrace(result, max_depth, skip_count + int(DEBUG_MODE));
// due to -foptimize-sibling-calls in opt mode
// there's no need for extra frame skip here then
#else
CheckInHookCaller();
// MallocHook caller determination via InHookCaller works, use it:
static const int kMaxSkip = 32 + 6 + 3;
// Constant tuned to do just one GetStackTrace call below in practice
// and not get many frames that we don't actually need:
// currently max passsed max_depth is 32,
// max passed/needed skip_count is 6
// and 3 is to account for some hook daisy chaining.
static const int kStackSize = kMaxSkip + 1;
void* stack[kStackSize];
int depth = GetStackTrace(stack, kStackSize, 1); // skip this function frame
if (depth == 0) // silenty propagate cases when GetStackTrace does not work
return 0;
for (int i = 0; i < depth; ++i) { // stack[0] is our immediate caller
if (InHookCaller(stack[i])) {
// fast-path to slow-path calls may be implemented by compiler
// as non-tail calls. Causing two functions on stack trace to be
// inside google_malloc. In such case we're skipping to
// outermost such frame since this is where malloc stack frames
// really start.
while (i + 1 < depth && InHookCaller(stack[i+1])) {
i++;
}
RAW_VLOG(10, "Found hooked allocator at %d: %p <- %p",
i, stack[i], stack[i+1]);
i += 1; // skip hook caller frame
depth -= i; // correct depth
if (depth > max_depth) depth = max_depth;
copy(stack + i, stack + i + depth, result);
if (depth < max_depth && depth + i == kStackSize) {
// get frames for the missing depth
depth +=
GetStackTrace(result + depth, max_depth - depth, 1 + kStackSize);
}
return depth;
}
}
RAW_LOG(WARNING, "Hooked allocator frame not found, returning empty trace");
// If this happens try increasing kMaxSkip
// or else something must be wrong with InHookCaller,
// e.g. for every section used in InHookCaller
// all functions in that section must be inside the same library.
return 0;
#endif
}
// On systems where we know how, we override mmap/munmap/mremap/sbrk
// to provide support for calling the related hooks (in addition,
// of course, to doing what these functions normally do).
#if defined(__linux)
# include "malloc_hook_mmap_linux.h"
#elif defined(__FreeBSD__)
# include "malloc_hook_mmap_freebsd.h"
#else
/*static*/void* MallocHook::UnhookedMMap(void *start, size_t length, int prot,
int flags, int fd, off_t offset) {
void* result;
if (!MallocHook::InvokeMmapReplacement(
start, length, prot, flags, fd, offset, &result)) {
result = mmap(start, length, prot, flags, fd, offset);
}
return result;
}
/*static*/int MallocHook::UnhookedMUnmap(void *start, size_t length) {
int result;
if (!MallocHook::InvokeMunmapReplacement(start, length, &result)) {
result = munmap(start, length);
}
return result;
}
#endif

View file

@ -0,0 +1,135 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2011, 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.
// Override mmap/munmap/mremap/sbrk to provide support for calling the
// related hooks (in addition, of course, to doing what these
// functions normally do).
#ifndef __FreeBSD__
# error Should only be including malloc_hook_mmap_freebsd.h on FreeBSD systems.
#endif
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/mman.h>
#include <errno.h>
#include <dlfcn.h>
// Make sure mmap doesn't get #define'd away by <sys/mman.h>
#undef mmap
// According to the FreeBSD documentation, use syscall if you do not
// need 64-bit alignment otherwise use __syscall. Indeed, syscall
// doesn't work correctly in most situations on 64-bit. It's return
// type is 'int' so for things like SYS_mmap, it actually truncates
// the returned address to 32-bits.
#if defined(__amd64__) || defined(__x86_64__)
# define MALLOC_HOOK_SYSCALL __syscall
#else
# define MALLOC_HOOK_SYSCALL syscall
#endif
extern "C" {
void* mmap(void *start, size_t length,int prot, int flags,
int fd, off_t offset) __THROW
ATTRIBUTE_SECTION(malloc_hook);
int munmap(void* start, size_t length) __THROW
ATTRIBUTE_SECTION(malloc_hook);
void* sbrk(intptr_t increment) __THROW
ATTRIBUTE_SECTION(malloc_hook);
}
static inline void* do_mmap(void *start, size_t length,
int prot, int flags,
int fd, off_t offset) __THROW {
return (void *)MALLOC_HOOK_SYSCALL(SYS_mmap,
start, length, prot, flags, fd, offset);
}
static inline void* do_sbrk(intptr_t increment) {
static void *(*libc_sbrk)(intptr_t);
if (libc_sbrk == NULL)
libc_sbrk = (void *(*)(intptr_t))dlsym(RTLD_NEXT, "sbrk");
return libc_sbrk(increment);
}
extern "C" void* mmap(void *start, size_t length, int prot, int flags,
int fd, off_t offset) __THROW {
MallocHook::InvokePreMmapHook(start, length, prot, flags, fd, offset);
void *result;
if (!MallocHook::InvokeMmapReplacement(
start, length, prot, flags, fd, offset, &result)) {
result = do_mmap(start, length, prot, flags, fd,
static_cast<size_t>(offset)); // avoid sign extension
}
MallocHook::InvokeMmapHook(result, start, length, prot, flags, fd, offset);
return result;
}
extern "C" int munmap(void* start, size_t length) __THROW {
MallocHook::InvokeMunmapHook(start, length);
int result;
if (!MallocHook::InvokeMunmapReplacement(start, length, &result)) {
result = MALLOC_HOOK_SYSCALL(SYS_munmap, start, length);
}
return result;
}
extern "C" void* sbrk(intptr_t increment) __THROW {
MallocHook::InvokePreSbrkHook(increment);
void *result = do_sbrk(increment);
MallocHook::InvokeSbrkHook(result, increment);
return result;
}
/*static*/void* MallocHook::UnhookedMMap(void *start, size_t length, int prot,
int flags, int fd, off_t offset) {
void* result;
if (!MallocHook::InvokeMmapReplacement(
start, length, prot, flags, fd, offset, &result)) {
result = do_mmap(start, length, prot, flags, fd, offset);
}
return result;
}
/*static*/int MallocHook::UnhookedMUnmap(void *start, size_t length) {
int result;
if (!MallocHook::InvokeMunmapReplacement(start, length, &result)) {
result = MALLOC_HOOK_SYSCALL(SYS_munmap, start, length);
}
return result;
}
#undef MALLOC_HOOK_SYSCALL

View file

@ -0,0 +1,250 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2005, 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 <opensource@google.com>
// We define mmap() and mmap64(), which somewhat reimplements libc's mmap
// syscall stubs. Unfortunately libc only exports the stubs via weak symbols
// (which we're overriding with our mmap64() and mmap() wrappers) so we can't
// just call through to them.
#ifndef __linux
# error Should only be including malloc_hook_mmap_linux.h on linux systems.
#endif
#include <errno.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <unistd.h>
// The x86-32 case and the x86-64 case differ:
// 32b has a mmap2() syscall, 64b does not.
// 64b and 32b have different calling conventions for mmap().
// I test for 64-bit first so I don't have to do things like
// '#if (defined(__mips__) && !defined(__MIPS64__))' as a mips32 check.
#if defined(__x86_64__) \
|| defined(__PPC64__) \
|| defined(__aarch64__) \
|| (defined(_MIPS_SIM) && (_MIPS_SIM == _ABI64 || _MIPS_SIM == _ABIN32)) \
|| defined(__s390__) || (defined(__riscv) && __riscv_xlen == 64) \
|| defined(__e2k__)
static inline void* do_mmap64(void *start, size_t length,
int prot, int flags,
int fd, off64_t offset) __THROW {
#if defined(__s390__)
long args[6] = { (long)start, (long)length, (long)prot, (long)flags,
(long)fd, (long)offset };
return (void*)syscall(SYS_mmap, args);
#else
return (void*)syscall(SYS_mmap, start, length, prot, flags, fd, offset);
#endif
}
#define MALLOC_HOOK_HAVE_DO_MMAP64 1
#elif defined(__i386__) || defined(__PPC__) || defined(__mips__) || \
defined(__arm__)
static inline void* do_mmap64(void *start, size_t length,
int prot, int flags,
int fd, off64_t offset) __THROW {
void *result;
// Try mmap2() unless it's not supported
static bool have_mmap2 = true;
if (have_mmap2) {
static int pagesize = 0;
if (!pagesize) pagesize = getpagesize();
// Check that the offset is page aligned
if (offset & (pagesize - 1)) {
result = MAP_FAILED;
errno = EINVAL;
goto out;
}
result = (void *)syscall(SYS_mmap2,
start, length, prot, flags, fd,
(off_t) (offset / pagesize));
if (result != MAP_FAILED || errno != ENOSYS) goto out;
// We don't have mmap2() after all - don't bother trying it in future
have_mmap2 = false;
}
if (((off_t)offset) != offset) {
// If we're trying to map a 64-bit offset, fail now since we don't
// have 64-bit mmap() support.
result = MAP_FAILED;
errno = EINVAL;
goto out;
}
#ifdef __NR_mmap
{
// Fall back to old 32-bit offset mmap() call
// Old syscall interface cannot handle six args, so pass in an array
int32 args[6] = { (int32) start, (int32) length, prot, flags, fd,
(int32)(off_t) offset };
result = (void *)syscall(SYS_mmap, args);
}
#else
// Some Linux ports like ARM EABI Linux has no mmap, just mmap2.
result = MAP_FAILED;
#endif
out:
return result;
}
#define MALLOC_HOOK_HAVE_DO_MMAP64 1
#endif // #if defined(__x86_64__)
#ifdef MALLOC_HOOK_HAVE_DO_MMAP64
// We use do_mmap64 abstraction to put MallocHook::InvokeMmapHook
// calls right into mmap and mmap64, so that the stack frames in the caller's
// stack are at the same offsets for all the calls of memory allocating
// functions.
// Put all callers of MallocHook::Invoke* in this module into
// malloc_hook section,
// so that MallocHook::GetCallerStackTrace can function accurately:
// Make sure mmap64 and mmap doesn't get #define'd away by <sys/mman.h>
# undef mmap64
# undef mmap
extern "C" {
void* mmap64(void *start, size_t length, int prot, int flags,
int fd, off64_t offset ) __THROW
ATTRIBUTE_SECTION(malloc_hook);
void* mmap(void *start, size_t length,int prot, int flags,
int fd, off_t offset) __THROW
ATTRIBUTE_SECTION(malloc_hook);
int munmap(void* start, size_t length) __THROW
ATTRIBUTE_SECTION(malloc_hook);
void* mremap(void* old_addr, size_t old_size, size_t new_size,
int flags, ...) __THROW
ATTRIBUTE_SECTION(malloc_hook);
void* sbrk(intptr_t increment) __THROW
ATTRIBUTE_SECTION(malloc_hook);
}
extern "C" void* mmap64(void *start, size_t length, int prot, int flags,
int fd, off64_t offset) __THROW {
MallocHook::InvokePreMmapHook(start, length, prot, flags, fd, offset);
void *result;
if (!MallocHook::InvokeMmapReplacement(
start, length, prot, flags, fd, offset, &result)) {
result = do_mmap64(start, length, prot, flags, fd, offset);
}
MallocHook::InvokeMmapHook(result, start, length, prot, flags, fd, offset);
return result;
}
# if !defined(__USE_FILE_OFFSET64) || !defined(__REDIRECT_NTH)
extern "C" void* mmap(void *start, size_t length, int prot, int flags,
int fd, off_t offset) __THROW {
MallocHook::InvokePreMmapHook(start, length, prot, flags, fd, offset);
void *result;
if (!MallocHook::InvokeMmapReplacement(
start, length, prot, flags, fd, offset, &result)) {
result = do_mmap64(start, length, prot, flags, fd,
static_cast<size_t>(offset)); // avoid sign extension
}
MallocHook::InvokeMmapHook(result, start, length, prot, flags, fd, offset);
return result;
}
# endif // !defined(__USE_FILE_OFFSET64) || !defined(__REDIRECT_NTH)
extern "C" int munmap(void* start, size_t length) __THROW {
MallocHook::InvokeMunmapHook(start, length);
int result;
if (!MallocHook::InvokeMunmapReplacement(start, length, &result)) {
result = syscall(SYS_munmap, start, length);
}
return result;
}
extern "C" void* mremap(void* old_addr, size_t old_size, size_t new_size,
int flags, ...) __THROW {
va_list ap;
va_start(ap, flags);
void *new_address = va_arg(ap, void *);
va_end(ap);
void* result = (void*)syscall(SYS_mremap, old_addr, old_size, new_size, flags,
new_address);
MallocHook::InvokeMremapHook(result, old_addr, old_size, new_size, flags,
new_address);
return result;
}
#ifdef HAVE___SBRK
// libc's version:
extern "C" void* __sbrk(intptr_t increment);
extern "C" void* sbrk(intptr_t increment) __THROW {
MallocHook::InvokePreSbrkHook(increment);
void *result = __sbrk(increment);
MallocHook::InvokeSbrkHook(result, increment);
return result;
}
#endif
/*static*/void* MallocHook::UnhookedMMap(void *start, size_t length, int prot,
int flags, int fd, off_t offset) {
void* result;
if (!MallocHook::InvokeMmapReplacement(
start, length, prot, flags, fd, offset, &result)) {
result = do_mmap64(start, length, prot, flags, fd, offset);
}
return result;
}
/*static*/int MallocHook::UnhookedMUnmap(void *start, size_t length) {
int result;
if (!MallocHook::InvokeMunmapReplacement(start, length, &result)) {
result = syscall(SYS_munmap, start, length);
}
return result;
}
#undef MALLOC_HOOK_HAVE_DO_MMAP64
#endif // #ifdef MALLOC_HOOK_HAVE_DO_MMAP64

View file

@ -0,0 +1,55 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2014, gperftools Contributors
// 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.
#ifndef MAYBE_EMERGENCY_MALLOC_H
#define MAYBE_EMERGENCY_MALLOC_H
#include "config.h"
#ifdef ENABLE_EMERGENCY_MALLOC
#include "emergency_malloc.h"
#else
namespace tcmalloc {
static inline void *EmergencyMalloc(size_t size) {return NULL;}
static inline void EmergencyFree(void *p) {}
static inline void *EmergencyCalloc(size_t n, size_t elem_size) {return NULL;}
static inline void *EmergencyRealloc(void *old_ptr, size_t new_size) {return NULL;}
static inline bool IsEmergencyPtr(const void *_ptr) {
return false;
}
}
#endif // ENABLE_EMERGENCY_MALLOC
#endif

View file

@ -0,0 +1,177 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2005, 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: Paul Menage <opensource@google.com>
//
// Some wrappers for pthread functions so that we can be LD_PRELOADed
// against non-pthreads apps.
//
// This module will behave very strangely if some pthreads functions
// exist and others don't.
#include "config.h"
#include <assert.h>
#include <string.h> // for memcmp
#include <stdio.h> // for __isthreaded on FreeBSD
// We don't actually need strings. But including this header seems to
// stop the compiler trying to short-circuit our pthreads existence
// tests and claiming that the address of a function is always
// non-zero. I have no idea why ...
#include <string>
#include "maybe_threads.h"
#include "base/basictypes.h"
#include "base/logging.h"
// __THROW is defined in glibc systems. It means, counter-intuitively,
// "This function will never throw an exception." It's an optional
// optimization tool, but we may need to use it to match glibc prototypes.
#ifndef __THROW // I guess we're not on a glibc system
# define __THROW // __THROW is just an optimization, so ok to make it ""
#endif
// These are the methods we're going to conditionally include.
extern "C" {
int pthread_key_create (pthread_key_t*, void (*)(void*))
__THROW ATTRIBUTE_WEAK;
int pthread_key_delete (pthread_key_t)
__THROW ATTRIBUTE_WEAK;
void *pthread_getspecific(pthread_key_t)
__THROW ATTRIBUTE_WEAK;
int pthread_setspecific(pthread_key_t, const void*)
__THROW ATTRIBUTE_WEAK;
int pthread_once(pthread_once_t *, void (*)(void))
ATTRIBUTE_WEAK;
#ifdef HAVE_FORK
int pthread_atfork(void (*__prepare) (void),
void (*__parent) (void),
void (*__child) (void))
__THROW ATTRIBUTE_WEAK;
#endif
}
#define MAX_PERTHREAD_VALS 16
static void *perftools_pthread_specific_vals[MAX_PERTHREAD_VALS];
static int next_key;
// NOTE: it's similar to bitcast defined in basic_types.h with
// exception of ignoring sizes mismatch
template <typename T1, typename T2>
static T2 memcpy_cast(const T1 &input) {
T2 output;
size_t s = sizeof(input);
if (sizeof(output) < s) {
s = sizeof(output);
}
memcpy(&output, &input, s);
return output;
}
int perftools_pthread_key_create(pthread_key_t *key,
void (*destr_function) (void *)) {
if (pthread_key_create) {
return pthread_key_create(key, destr_function);
} else {
assert(next_key < MAX_PERTHREAD_VALS);
*key = memcpy_cast<int, pthread_key_t>(next_key++);
return 0;
}
}
int perftools_pthread_key_delete(pthread_key_t key) {
if (pthread_key_delete) {
return pthread_key_delete(key);
} else {
return 0;
}
}
void *perftools_pthread_getspecific(pthread_key_t key) {
if (pthread_getspecific) {
return pthread_getspecific(key);
} else {
return perftools_pthread_specific_vals[memcpy_cast<pthread_key_t, int>(key)];
}
}
int perftools_pthread_setspecific(pthread_key_t key, void *val) {
if (pthread_setspecific) {
return pthread_setspecific(key, val);
} else {
perftools_pthread_specific_vals[memcpy_cast<pthread_key_t, int>(key)] = val;
return 0;
}
}
static pthread_once_t pthread_once_init = PTHREAD_ONCE_INIT;
int perftools_pthread_once(pthread_once_t *ctl,
void (*init_routine) (void)) {
#ifdef __FreeBSD__
// On __FreeBSD__, calling pthread_once on a system that is not
// linked with -pthread is silently a noop. :-( Luckily, we have a
// workaround: FreeBSD exposes __isthreaded in <stdio.h>, which is
// set to 1 when the first thread is spawned. So on those systems,
// we can use our own separate pthreads-once mechanism, which is
// used until __isthreaded is 1 (which will never be true if the app
// is not linked with -pthread).
static bool pthread_once_ran_before_threads = false;
if (pthread_once_ran_before_threads) {
return 0;
}
if (!__isthreaded) {
init_routine();
pthread_once_ran_before_threads = true;
return 0;
}
#endif
if (pthread_once) {
return pthread_once(ctl, init_routine);
} else {
if (memcmp(ctl, &pthread_once_init, sizeof(*ctl)) == 0) {
init_routine();
++*(char*)(ctl); // make it so it's no longer equal to init
}
return 0;
}
}
#ifdef HAVE_FORK
void perftools_pthread_atfork(void (*before)(),
void (*parent_after)(),
void (*child_after)()) {
if (pthread_atfork) {
int rv = pthread_atfork(before, parent_after, child_after);
CHECK(rv == 0);
}
}
#endif

View file

@ -0,0 +1,61 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
// Copyright (c) 2005, 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: Paul Menage <opensource@google.com>
//-------------------------------------------------------------------
// Some wrappers for pthread functions so that we can be LD_PRELOADed
// against non-pthreads apps.
//-------------------------------------------------------------------
#ifndef GOOGLE_MAYBE_THREADS_H_
#define GOOGLE_MAYBE_THREADS_H_
#ifdef HAVE_PTHREAD
#include <pthread.h>
#endif
int perftools_pthread_key_create(pthread_key_t *key,
void (*destr_function) (void *));
int perftools_pthread_key_delete(pthread_key_t key);
void *perftools_pthread_getspecific(pthread_key_t key);
int perftools_pthread_setspecific(pthread_key_t key, void *val);
int perftools_pthread_once(pthread_once_t *ctl,
void (*init_routine) (void));
// Our wrapper for pthread_atfork. Does _nothing_ when there are no
// threads. See static_vars.cc:SetupAtForkLocksHandler for only user
// of this.
void perftools_pthread_atfork(void (*before)(),
void (*parent_after)(),
void (*child_after)());
#endif /* GOOGLE_MAYBE_THREADS_H_ */

View file

@ -0,0 +1,279 @@
// -*- 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: Arun Sharma
//
// A tcmalloc system allocator that uses a memory based filesystem such as
// tmpfs or hugetlbfs
//
// Since these only exist on linux, we only register this allocator there.
#ifdef __linux
#include <config.h>
#include <errno.h> // for errno, EINVAL
#include <inttypes.h> // for PRId64
#include <limits.h> // for PATH_MAX
#include <stddef.h> // for size_t, NULL
#ifdef HAVE_STDINT_H
#include <stdint.h> // for int64_t, uintptr_t
#endif
#include <stdio.h> // for snprintf
#include <stdlib.h> // for mkstemp
#include <string.h> // for strerror
#include <sys/mman.h> // for mmap, MAP_FAILED, etc
#include <sys/statfs.h> // for fstatfs, statfs
#include <unistd.h> // for ftruncate, off_t, unlink
#include <new> // for operator new
#include <string>
#include <gperftools/malloc_extension.h>
#include "base/basictypes.h"
#include "base/googleinit.h"
#include "base/sysinfo.h"
#include "internal_logging.h"
// TODO(sanjay): Move the code below into the tcmalloc namespace
using tcmalloc::kLog;
using tcmalloc::kCrash;
using tcmalloc::Log;
using std::string;
DEFINE_string(memfs_malloc_path, EnvToString("TCMALLOC_MEMFS_MALLOC_PATH", ""),
"Path where hugetlbfs or tmpfs is mounted. The caller is "
"responsible for ensuring that the path is unique and does "
"not conflict with another process");
DEFINE_int64(memfs_malloc_limit_mb,
EnvToInt("TCMALLOC_MEMFS_LIMIT_MB", 0),
"Limit total allocation size to the "
"specified number of MiB. 0 == no limit.");
DEFINE_bool(memfs_malloc_abort_on_fail,
EnvToBool("TCMALLOC_MEMFS_ABORT_ON_FAIL", false),
"abort() whenever memfs_malloc fails to satisfy an allocation "
"for any reason.");
DEFINE_bool(memfs_malloc_ignore_mmap_fail,
EnvToBool("TCMALLOC_MEMFS_IGNORE_MMAP_FAIL", false),
"Ignore failures from mmap");
DEFINE_bool(memfs_malloc_map_private,
EnvToBool("TCMALLOC_MEMFS_MAP_PRIVATE", false),
"Use MAP_PRIVATE with mmap");
DEFINE_bool(memfs_malloc_disable_fallback,
EnvToBool("TCMALLOC_MEMFS_DISABLE_FALLBACK", false),
"If we run out of hugepage memory don't fallback to default "
"allocator.");
// Hugetlbfs based allocator for tcmalloc
class HugetlbSysAllocator: public SysAllocator {
public:
explicit HugetlbSysAllocator(SysAllocator* fallback)
: failed_(true), // To disable allocator until Initialize() is called.
big_page_size_(0),
hugetlb_fd_(-1),
hugetlb_base_(0),
fallback_(fallback) {
}
void* Alloc(size_t size, size_t *actual_size, size_t alignment);
bool Initialize();
bool failed_; // Whether failed to allocate memory.
private:
void* AllocInternal(size_t size, size_t *actual_size, size_t alignment);
int64 big_page_size_;
int hugetlb_fd_; // file descriptor for hugetlb
off_t hugetlb_base_;
SysAllocator* fallback_; // Default system allocator to fall back to.
};
static union {
char buf[sizeof(HugetlbSysAllocator)];
void *ptr;
} hugetlb_space;
// No locking needed here since we assume that tcmalloc calls
// us with an internal lock held (see tcmalloc/system-alloc.cc).
void* HugetlbSysAllocator::Alloc(size_t size, size_t *actual_size,
size_t alignment) {
if (!FLAGS_memfs_malloc_disable_fallback && failed_) {
return fallback_->Alloc(size, actual_size, alignment);
}
// We don't respond to allocation requests smaller than big_page_size_ unless
// the caller is ok to take more than they asked for. Used by MetaDataAlloc.
if (!FLAGS_memfs_malloc_disable_fallback &&
actual_size == NULL && size < big_page_size_) {
return fallback_->Alloc(size, actual_size, alignment);
}
// Enforce huge page alignment. Be careful to deal with overflow.
size_t new_alignment = alignment;
if (new_alignment < big_page_size_) new_alignment = big_page_size_;
size_t aligned_size = ((size + new_alignment - 1) /
new_alignment) * new_alignment;
if (!FLAGS_memfs_malloc_disable_fallback && aligned_size < size) {
return fallback_->Alloc(size, actual_size, alignment);
}
void* result = AllocInternal(aligned_size, actual_size, new_alignment);
if (result != NULL) {
return result;
} else if (FLAGS_memfs_malloc_disable_fallback) {
return NULL;
}
Log(kLog, __FILE__, __LINE__,
"HugetlbSysAllocator: (failed, allocated)", failed_, hugetlb_base_);
if (FLAGS_memfs_malloc_abort_on_fail) {
Log(kCrash, __FILE__, __LINE__,
"memfs_malloc_abort_on_fail is set");
}
return fallback_->Alloc(size, actual_size, alignment);
}
void* HugetlbSysAllocator::AllocInternal(size_t size, size_t* actual_size,
size_t alignment) {
// Ask for extra memory if alignment > pagesize
size_t extra = 0;
if (alignment > big_page_size_) {
extra = alignment - big_page_size_;
}
// Test if this allocation would put us over the limit.
off_t limit = FLAGS_memfs_malloc_limit_mb*1024*1024;
if (limit > 0 && hugetlb_base_ + size + extra > limit) {
// Disable the allocator when there's less than one page left.
if (limit - hugetlb_base_ < big_page_size_) {
Log(kLog, __FILE__, __LINE__, "reached memfs_malloc_limit_mb");
failed_ = true;
}
else {
Log(kLog, __FILE__, __LINE__,
"alloc too large (size, bytes left)", size, limit-hugetlb_base_);
}
return NULL;
}
// This is not needed for hugetlbfs, but needed for tmpfs. Annoyingly
// hugetlbfs returns EINVAL for ftruncate.
int ret = ftruncate(hugetlb_fd_, hugetlb_base_ + size + extra);
if (ret != 0 && errno != EINVAL) {
Log(kLog, __FILE__, __LINE__,
"ftruncate failed", strerror(errno));
failed_ = true;
return NULL;
}
// Note: size + extra does not overflow since:
// size + alignment < (1<<NBITS).
// and extra <= alignment
// therefore size + extra < (1<<NBITS)
void *result;
result = mmap(0, size + extra, PROT_WRITE|PROT_READ,
FLAGS_memfs_malloc_map_private ? MAP_PRIVATE : MAP_SHARED,
hugetlb_fd_, hugetlb_base_);
if (result == reinterpret_cast<void*>(MAP_FAILED)) {
if (!FLAGS_memfs_malloc_ignore_mmap_fail) {
Log(kLog, __FILE__, __LINE__,
"mmap failed (size, error)", size + extra, strerror(errno));
failed_ = true;
}
return NULL;
}
uintptr_t ptr = reinterpret_cast<uintptr_t>(result);
// Adjust the return memory so it is aligned
size_t adjust = 0;
if ((ptr & (alignment - 1)) != 0) {
adjust = alignment - (ptr & (alignment - 1));
}
ptr += adjust;
hugetlb_base_ += (size + extra);
if (actual_size) {
*actual_size = size + extra - adjust;
}
return reinterpret_cast<void*>(ptr);
}
bool HugetlbSysAllocator::Initialize() {
char path[PATH_MAX];
const int pathlen = FLAGS_memfs_malloc_path.size();
if (pathlen + 8 > sizeof(path)) {
Log(kCrash, __FILE__, __LINE__, "XX fatal: memfs_malloc_path too long");
return false;
}
memcpy(path, FLAGS_memfs_malloc_path.data(), pathlen);
memcpy(path + pathlen, ".XXXXXX", 8); // Also copies terminating \0
int hugetlb_fd = mkstemp(path);
if (hugetlb_fd == -1) {
Log(kLog, __FILE__, __LINE__,
"warning: unable to create memfs_malloc_path",
path, strerror(errno));
return false;
}
// Cleanup memory on process exit
if (unlink(path) == -1) {
Log(kCrash, __FILE__, __LINE__,
"fatal: error unlinking memfs_malloc_path", path, strerror(errno));
return false;
}
// Use fstatfs to figure out the default page size for memfs
struct statfs sfs;
if (fstatfs(hugetlb_fd, &sfs) == -1) {
Log(kCrash, __FILE__, __LINE__,
"fatal: error fstatfs of memfs_malloc_path", strerror(errno));
return false;
}
int64 page_size = sfs.f_bsize;
hugetlb_fd_ = hugetlb_fd;
big_page_size_ = page_size;
failed_ = false;
return true;
}
REGISTER_MODULE_INITIALIZER(memfs_malloc, {
if (FLAGS_memfs_malloc_path.length()) {
SysAllocator* alloc = MallocExtension::instance()->GetSystemAllocator();
HugetlbSysAllocator* hp =
new (hugetlb_space.buf) HugetlbSysAllocator(alloc);
if (hp->Initialize()) {
MallocExtension::instance()->SetSystemAllocator(hp);
}
}
});
#endif /* ifdef __linux */

View file

@ -0,0 +1,838 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2006, 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: Maxim Lifantsev
*/
//
// Background and key design points of MemoryRegionMap.
//
// MemoryRegionMap is a low-level module with quite atypical requirements that
// result in some degree of non-triviality of the implementation and design.
//
// MemoryRegionMap collects info about *all* memory regions created with
// mmap, munmap, mremap, sbrk.
// They key word above is 'all': all that are happening in a process
// during its lifetime frequently starting even before global object
// constructor execution.
//
// This is needed by the primary client of MemoryRegionMap:
// HeapLeakChecker uses the regions and the associated stack traces
// to figure out what part of the memory is the heap:
// if MemoryRegionMap were to miss some (early) regions, leak checking would
// stop working correctly.
//
// To accomplish the goal of functioning before/during global object
// constructor execution MemoryRegionMap is done as a singleton service
// that relies on own on-demand initialized static constructor-less data,
// and only relies on other low-level modules that can also function properly
// even before global object constructors run.
//
// Accomplishing the goal of collecting data about all mmap, munmap, mremap,
// sbrk occurrences is a more involved: conceptually to do this one needs to
// record some bits of data in particular about any mmap or sbrk call,
// but to do that one needs to allocate memory for that data at some point,
// but all memory allocations in the end themselves come from an mmap
// or sbrk call (that's how the address space of the process grows).
//
// Also note that we need to do all the above recording from
// within an mmap/sbrk hook which is sometimes/frequently is made by a memory
// allocator, including the allocator MemoryRegionMap itself must rely on.
// In the case of heap-checker usage this includes even the very first
// mmap/sbrk call happening in the program: heap-checker gets activated due to
// a link-time installed mmap/sbrk hook and it initializes MemoryRegionMap
// and asks it to record info about this very first call right from that
// very first hook invocation.
//
// MemoryRegionMap is doing its memory allocations via LowLevelAlloc:
// unlike more complex standard memory allocator, LowLevelAlloc cooperates with
// MemoryRegionMap by not holding any of its own locks while it calls mmap
// to get memory, thus we are able to call LowLevelAlloc from
// our mmap/sbrk hooks without causing a deadlock in it.
// For the same reason of deadlock prevention the locking in MemoryRegionMap
// itself is write-recursive which is an exception to Google's mutex usage.
//
// We still need to break the infinite cycle of mmap calling our hook,
// which asks LowLevelAlloc for memory to record this mmap,
// which (sometimes) causes mmap, which calls our hook, and so on.
// We do this as follows: on a recursive call of MemoryRegionMap's
// mmap/sbrk/mremap hook we record the data about the allocation in a
// static fixed-sized stack (saved_regions and saved_buckets), when the
// recursion unwinds but before returning from the outer hook call we unwind
// this stack and move the data from saved_regions and saved_buckets to its
// permanent place in the RegionSet and "bucket_table" respectively,
// which can cause more allocations and mmap-s and recursion and unwinding,
// but the whole process ends eventually due to the fact that for the small
// allocations we are doing LowLevelAlloc reuses one mmap call and parcels out
// the memory it created to satisfy several of our allocation requests.
//
// ========================================================================= //
#include <config.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_INTTYPES_H
#include <inttypes.h>
#endif
#ifdef HAVE_MMAP
#include <sys/mman.h>
#elif !defined(MAP_FAILED)
#define MAP_FAILED -1 // the only thing we need from mman.h
#endif
#ifdef HAVE_PTHREAD
#include <pthread.h> // for pthread_t, pthread_self()
#endif
#include <stddef.h>
#include <algorithm>
#include <set>
#include "memory_region_map.h"
#include "base/googleinit.h"
#include "base/logging.h"
#include "base/low_level_alloc.h"
#include "malloc_hook-inl.h"
#include <gperftools/stacktrace.h>
#include <gperftools/malloc_hook.h>
// MREMAP_FIXED is a linux extension. How it's used in this file,
// setting it to 0 is equivalent to saying, "This feature isn't
// supported", which is right.
#ifndef MREMAP_FIXED
# define MREMAP_FIXED 0
#endif
using std::max;
// ========================================================================= //
int MemoryRegionMap::client_count_ = 0;
int MemoryRegionMap::max_stack_depth_ = 0;
MemoryRegionMap::RegionSet* MemoryRegionMap::regions_ = NULL;
LowLevelAlloc::Arena* MemoryRegionMap::arena_ = NULL;
SpinLock MemoryRegionMap::lock_(SpinLock::LINKER_INITIALIZED);
SpinLock MemoryRegionMap::owner_lock_( // ACQUIRED_AFTER(lock_)
SpinLock::LINKER_INITIALIZED);
int MemoryRegionMap::recursion_count_ = 0; // GUARDED_BY(owner_lock_)
pthread_t MemoryRegionMap::lock_owner_tid_; // GUARDED_BY(owner_lock_)
int64 MemoryRegionMap::map_size_ = 0;
int64 MemoryRegionMap::unmap_size_ = 0;
HeapProfileBucket** MemoryRegionMap::bucket_table_ = NULL; // GUARDED_BY(lock_)
int MemoryRegionMap::num_buckets_ = 0; // GUARDED_BY(lock_)
int MemoryRegionMap::saved_buckets_count_ = 0; // GUARDED_BY(lock_)
HeapProfileBucket MemoryRegionMap::saved_buckets_[20]; // GUARDED_BY(lock_)
// GUARDED_BY(lock_)
const void* MemoryRegionMap::saved_buckets_keys_[20][kMaxStackDepth];
// ========================================================================= //
// Simple hook into execution of global object constructors,
// so that we do not call pthread_self() when it does not yet work.
static bool libpthread_initialized = false;
REGISTER_MODULE_INITIALIZER(libpthread_initialized_setter,
libpthread_initialized = true);
static inline bool current_thread_is(pthread_t should_be) {
// Before main() runs, there's only one thread, so we're always that thread
if (!libpthread_initialized) return true;
// this starts working only sometime well into global constructor execution:
return pthread_equal(pthread_self(), should_be);
}
// ========================================================================= //
// Constructor-less place-holder to store a RegionSet in.
union MemoryRegionMap::RegionSetRep {
char rep[sizeof(RegionSet)];
void* align_it; // do not need a better alignment for 'rep' than this
RegionSet* region_set() { return reinterpret_cast<RegionSet*>(rep); }
};
// The bytes where MemoryRegionMap::regions_ will point to.
// We use RegionSetRep with noop c-tor so that global construction
// does not interfere.
static MemoryRegionMap::RegionSetRep regions_rep;
// ========================================================================= //
// Has InsertRegionLocked been called recursively
// (or rather should we *not* use regions_ to record a hooked mmap).
static bool recursive_insert = false;
void MemoryRegionMap::Init(int max_stack_depth, bool use_buckets) {
RAW_VLOG(10, "MemoryRegionMap Init");
RAW_CHECK(max_stack_depth >= 0, "");
// Make sure we don't overflow the memory in region stacks:
RAW_CHECK(max_stack_depth <= kMaxStackDepth,
"need to increase kMaxStackDepth?");
Lock();
client_count_ += 1;
max_stack_depth_ = max(max_stack_depth_, max_stack_depth);
if (client_count_ > 1) {
// not first client: already did initialization-proper
Unlock();
RAW_VLOG(10, "MemoryRegionMap Init increment done");
return;
}
// Set our hooks and make sure they were installed:
RAW_CHECK(MallocHook::AddMmapHook(&MmapHook), "");
RAW_CHECK(MallocHook::AddMremapHook(&MremapHook), "");
RAW_CHECK(MallocHook::AddSbrkHook(&SbrkHook), "");
RAW_CHECK(MallocHook::AddMunmapHook(&MunmapHook), "");
// We need to set recursive_insert since the NewArena call itself
// will already do some allocations with mmap which our hooks will catch
// recursive_insert allows us to buffer info about these mmap calls.
// Note that Init() can be (and is) sometimes called
// already from within an mmap/sbrk hook.
recursive_insert = true;
arena_ = LowLevelAlloc::NewArena(0, LowLevelAlloc::DefaultArena());
recursive_insert = false;
HandleSavedRegionsLocked(&InsertRegionLocked); // flush the buffered ones
// Can't instead use HandleSavedRegionsLocked(&DoInsertRegionLocked) before
// recursive_insert = false; as InsertRegionLocked will also construct
// regions_ on demand for us.
if (use_buckets) {
const int table_bytes = kHashTableSize * sizeof(*bucket_table_);
recursive_insert = true;
bucket_table_ = static_cast<HeapProfileBucket**>(
MyAllocator::Allocate(table_bytes));
recursive_insert = false;
memset(bucket_table_, 0, table_bytes);
num_buckets_ = 0;
}
if (regions_ == NULL) { // init regions_
InitRegionSetLocked();
}
Unlock();
RAW_VLOG(10, "MemoryRegionMap Init done");
}
bool MemoryRegionMap::Shutdown() {
RAW_VLOG(10, "MemoryRegionMap Shutdown");
Lock();
RAW_CHECK(client_count_ > 0, "");
client_count_ -= 1;
if (client_count_ != 0) { // not last client; need not really shutdown
Unlock();
RAW_VLOG(10, "MemoryRegionMap Shutdown decrement done");
return true;
}
if (bucket_table_ != NULL) {
for (int i = 0; i < kHashTableSize; i++) {
for (HeapProfileBucket* curr = bucket_table_[i]; curr != 0; /**/) {
HeapProfileBucket* bucket = curr;
curr = curr->next;
MyAllocator::Free(bucket->stack, 0);
MyAllocator::Free(bucket, 0);
}
}
MyAllocator::Free(bucket_table_, 0);
num_buckets_ = 0;
bucket_table_ = NULL;
}
RAW_CHECK(MallocHook::RemoveMmapHook(&MmapHook), "");
RAW_CHECK(MallocHook::RemoveMremapHook(&MremapHook), "");
RAW_CHECK(MallocHook::RemoveSbrkHook(&SbrkHook), "");
RAW_CHECK(MallocHook::RemoveMunmapHook(&MunmapHook), "");
if (regions_) regions_->~RegionSet();
regions_ = NULL;
bool deleted_arena = LowLevelAlloc::DeleteArena(arena_);
if (deleted_arena) {
arena_ = 0;
} else {
RAW_LOG(WARNING, "Can't delete LowLevelAlloc arena: it's being used");
}
Unlock();
RAW_VLOG(10, "MemoryRegionMap Shutdown done");
return deleted_arena;
}
bool MemoryRegionMap::IsRecordingLocked() {
RAW_CHECK(LockIsHeld(), "should be held (by this thread)");
return client_count_ > 0;
}
// Invariants (once libpthread_initialized is true):
// * While lock_ is not held, recursion_count_ is 0 (and
// lock_owner_tid_ is the previous owner, but we don't rely on
// that).
// * recursion_count_ and lock_owner_tid_ are only written while
// both lock_ and owner_lock_ are held. They may be read under
// just owner_lock_.
// * At entry and exit of Lock() and Unlock(), the current thread
// owns lock_ iff pthread_equal(lock_owner_tid_, pthread_self())
// && recursion_count_ > 0.
void MemoryRegionMap::Lock() {
{
SpinLockHolder l(&owner_lock_);
if (recursion_count_ > 0 && current_thread_is(lock_owner_tid_)) {
RAW_CHECK(lock_.IsHeld(), "Invariants violated");
recursion_count_++;
RAW_CHECK(recursion_count_ <= 5,
"recursive lock nesting unexpectedly deep");
return;
}
}
lock_.Lock();
{
SpinLockHolder l(&owner_lock_);
RAW_CHECK(recursion_count_ == 0,
"Last Unlock didn't reset recursion_count_");
if (libpthread_initialized)
lock_owner_tid_ = pthread_self();
recursion_count_ = 1;
}
}
void MemoryRegionMap::Unlock() {
SpinLockHolder l(&owner_lock_);
RAW_CHECK(recursion_count_ > 0, "unlock when not held");
RAW_CHECK(lock_.IsHeld(),
"unlock when not held, and recursion_count_ is wrong");
RAW_CHECK(current_thread_is(lock_owner_tid_), "unlock by non-holder");
recursion_count_--;
if (recursion_count_ == 0) {
lock_.Unlock();
}
}
bool MemoryRegionMap::LockIsHeld() {
SpinLockHolder l(&owner_lock_);
return lock_.IsHeld() && current_thread_is(lock_owner_tid_);
}
const MemoryRegionMap::Region*
MemoryRegionMap::DoFindRegionLocked(uintptr_t addr) {
RAW_CHECK(LockIsHeld(), "should be held (by this thread)");
if (regions_ != NULL) {
Region sample;
sample.SetRegionSetKey(addr);
RegionSet::iterator region = regions_->lower_bound(sample);
if (region != regions_->end()) {
RAW_CHECK(addr <= region->end_addr, "");
if (region->start_addr <= addr && addr < region->end_addr) {
return &(*region);
}
}
}
return NULL;
}
bool MemoryRegionMap::FindRegion(uintptr_t addr, Region* result) {
Lock();
const Region* region = DoFindRegionLocked(addr);
if (region != NULL) *result = *region; // create it as an independent copy
Unlock();
return region != NULL;
}
bool MemoryRegionMap::FindAndMarkStackRegion(uintptr_t stack_top,
Region* result) {
Lock();
const Region* region = DoFindRegionLocked(stack_top);
if (region != NULL) {
RAW_VLOG(10, "Stack at %p is inside region %p..%p",
reinterpret_cast<void*>(stack_top),
reinterpret_cast<void*>(region->start_addr),
reinterpret_cast<void*>(region->end_addr));
const_cast<Region*>(region)->set_is_stack(); // now we know
// cast is safe (set_is_stack does not change the set ordering key)
*result = *region; // create *result as an independent copy
}
Unlock();
return region != NULL;
}
HeapProfileBucket* MemoryRegionMap::GetBucket(int depth,
const void* const key[]) {
RAW_CHECK(LockIsHeld(), "should be held (by this thread)");
// Make hash-value
uintptr_t hash = 0;
for (int i = 0; i < depth; i++) {
hash += reinterpret_cast<uintptr_t>(key[i]);
hash += hash << 10;
hash ^= hash >> 6;
}
hash += hash << 3;
hash ^= hash >> 11;
// Lookup stack trace in table
unsigned int hash_index = (static_cast<unsigned int>(hash)) % kHashTableSize;
for (HeapProfileBucket* bucket = bucket_table_[hash_index];
bucket != 0;
bucket = bucket->next) {
if ((bucket->hash == hash) && (bucket->depth == depth) &&
std::equal(key, key + depth, bucket->stack)) {
return bucket;
}
}
// Create new bucket
const size_t key_size = sizeof(key[0]) * depth;
HeapProfileBucket* bucket;
if (recursive_insert) { // recursion: save in saved_buckets_
const void** key_copy = saved_buckets_keys_[saved_buckets_count_];
std::copy(key, key + depth, key_copy);
bucket = &saved_buckets_[saved_buckets_count_];
memset(bucket, 0, sizeof(*bucket));
++saved_buckets_count_;
bucket->stack = key_copy;
bucket->next = NULL;
} else {
recursive_insert = true;
const void** key_copy = static_cast<const void**>(
MyAllocator::Allocate(key_size));
recursive_insert = false;
std::copy(key, key + depth, key_copy);
recursive_insert = true;
bucket = static_cast<HeapProfileBucket*>(
MyAllocator::Allocate(sizeof(HeapProfileBucket)));
recursive_insert = false;
memset(bucket, 0, sizeof(*bucket));
bucket->stack = key_copy;
bucket->next = bucket_table_[hash_index];
}
bucket->hash = hash;
bucket->depth = depth;
bucket_table_[hash_index] = bucket;
++num_buckets_;
return bucket;
}
MemoryRegionMap::RegionIterator MemoryRegionMap::BeginRegionLocked() {
RAW_CHECK(LockIsHeld(), "should be held (by this thread)");
RAW_CHECK(regions_ != NULL, "");
return regions_->begin();
}
MemoryRegionMap::RegionIterator MemoryRegionMap::EndRegionLocked() {
RAW_CHECK(LockIsHeld(), "should be held (by this thread)");
RAW_CHECK(regions_ != NULL, "");
return regions_->end();
}
inline void MemoryRegionMap::DoInsertRegionLocked(const Region& region) {
RAW_VLOG(12, "Inserting region %p..%p from %p",
reinterpret_cast<void*>(region.start_addr),
reinterpret_cast<void*>(region.end_addr),
reinterpret_cast<void*>(region.caller()));
RegionSet::const_iterator i = regions_->lower_bound(region);
if (i != regions_->end() && i->start_addr <= region.start_addr) {
RAW_DCHECK(region.end_addr <= i->end_addr, ""); // lower_bound ensures this
return; // 'region' is a subset of an already recorded region; do nothing
// We can be stricter and allow this only when *i has been created via
// an mmap with MAP_NORESERVE flag set.
}
if (DEBUG_MODE) {
RAW_CHECK(i == regions_->end() || !region.Overlaps(*i),
"Wow, overlapping memory regions");
Region sample;
sample.SetRegionSetKey(region.start_addr);
i = regions_->lower_bound(sample);
RAW_CHECK(i == regions_->end() || !region.Overlaps(*i),
"Wow, overlapping memory regions");
}
region.AssertIsConsistent(); // just making sure
// This inserts and allocates permanent storage for region
// and its call stack data: it's safe to do it now:
regions_->insert(region);
RAW_VLOG(12, "Inserted region %p..%p :",
reinterpret_cast<void*>(region.start_addr),
reinterpret_cast<void*>(region.end_addr));
if (VLOG_IS_ON(12)) LogAllLocked();
}
// These variables are local to MemoryRegionMap::InsertRegionLocked()
// and MemoryRegionMap::HandleSavedRegionsLocked()
// and are file-level to ensure that they are initialized at load time.
// Number of unprocessed region inserts.
static int saved_regions_count = 0;
// Unprocessed inserts (must be big enough to hold all allocations that can
// be caused by a InsertRegionLocked call).
// Region has no constructor, so that c-tor execution does not interfere
// with the any-time use of the static memory behind saved_regions.
static MemoryRegionMap::Region saved_regions[20];
inline void MemoryRegionMap::HandleSavedRegionsLocked(
void (*insert_func)(const Region& region)) {
while (saved_regions_count > 0) {
// Making a local-var copy of the region argument to insert_func
// including its stack (w/o doing any memory allocations) is important:
// in many cases the memory in saved_regions
// will get written-to during the (*insert_func)(r) call below.
Region r = saved_regions[--saved_regions_count];
(*insert_func)(r);
}
}
void MemoryRegionMap::RestoreSavedBucketsLocked() {
RAW_CHECK(LockIsHeld(), "should be held (by this thread)");
while (saved_buckets_count_ > 0) {
HeapProfileBucket bucket = saved_buckets_[--saved_buckets_count_];
unsigned int hash_index =
static_cast<unsigned int>(bucket.hash) % kHashTableSize;
bool is_found = false;
for (HeapProfileBucket* curr = bucket_table_[hash_index];
curr != 0;
curr = curr->next) {
if ((curr->hash == bucket.hash) && (curr->depth == bucket.depth) &&
std::equal(bucket.stack, bucket.stack + bucket.depth, curr->stack)) {
curr->allocs += bucket.allocs;
curr->alloc_size += bucket.alloc_size;
curr->frees += bucket.frees;
curr->free_size += bucket.free_size;
is_found = true;
break;
}
}
if (is_found) continue;
const size_t key_size = sizeof(bucket.stack[0]) * bucket.depth;
const void** key_copy = static_cast<const void**>(
MyAllocator::Allocate(key_size));
std::copy(bucket.stack, bucket.stack + bucket.depth, key_copy);
HeapProfileBucket* new_bucket = static_cast<HeapProfileBucket*>(
MyAllocator::Allocate(sizeof(HeapProfileBucket)));
memset(new_bucket, 0, sizeof(*new_bucket));
new_bucket->hash = bucket.hash;
new_bucket->depth = bucket.depth;
new_bucket->stack = key_copy;
new_bucket->next = bucket_table_[hash_index];
bucket_table_[hash_index] = new_bucket;
++num_buckets_;
}
}
inline void MemoryRegionMap::InitRegionSetLocked() {
RAW_VLOG(12, "Initializing region set");
regions_ = regions_rep.region_set();
recursive_insert = true;
new (regions_) RegionSet();
HandleSavedRegionsLocked(&DoInsertRegionLocked);
recursive_insert = false;
}
inline void MemoryRegionMap::InsertRegionLocked(const Region& region) {
RAW_CHECK(LockIsHeld(), "should be held (by this thread)");
// We can be called recursively, because RegionSet constructor
// and DoInsertRegionLocked() (called below) can call the allocator.
// recursive_insert tells us if that's the case. When this happens,
// region insertion information is recorded in saved_regions[],
// and taken into account when the recursion unwinds.
// Do the insert:
if (recursive_insert) { // recursion: save in saved_regions
RAW_VLOG(12, "Saving recursive insert of region %p..%p from %p",
reinterpret_cast<void*>(region.start_addr),
reinterpret_cast<void*>(region.end_addr),
reinterpret_cast<void*>(region.caller()));
RAW_CHECK(saved_regions_count < arraysize(saved_regions), "");
// Copy 'region' to saved_regions[saved_regions_count]
// together with the contents of its call_stack,
// then increment saved_regions_count.
saved_regions[saved_regions_count++] = region;
} else { // not a recusrive call
if (regions_ == NULL) { // init regions_
InitRegionSetLocked();
}
recursive_insert = true;
// Do the actual insertion work to put new regions into regions_:
DoInsertRegionLocked(region);
HandleSavedRegionsLocked(&DoInsertRegionLocked);
recursive_insert = false;
}
}
// We strip out different number of stack frames in debug mode
// because less inlining happens in that case
#ifdef NDEBUG
static const int kStripFrames = 1;
#else
static const int kStripFrames = 3;
#endif
void MemoryRegionMap::RecordRegionAddition(const void* start, size_t size) {
// Record start/end info about this memory acquisition call in a new region:
Region region;
region.Create(start, size);
// First get the call stack info into the local varible 'region':
int depth = 0;
// NOTE: libunwind also does mmap and very much likely while holding
// it's own lock(s). So some threads may first take libunwind lock,
// and then take region map lock (necessary to record mmap done from
// inside libunwind). On the other hand other thread(s) may do
// normal mmap. Which would call this method to record it. Which
// would then proceed with installing that record to region map
// while holding region map lock. That may cause mmap from our own
// internal allocators, so attempt to unwind in this case may cause
// reverse order of taking libuwind and region map locks. Which is
// obvious deadlock.
//
// Thankfully, we can easily detect if we're holding region map lock
// and avoid recording backtrace in this (rare and largely
// irrelevant) case. By doing this we "declare" that thread needing
// both locks must take region map lock last. In other words we do
// not allow taking libuwind lock when we already have region map
// lock. Note, this is generally impossible when somebody tries to
// mix cpu profiling and heap checking/profiling, because cpu
// profiler grabs backtraces at arbitrary places. But at least such
// combination is rarer and less relevant.
if (max_stack_depth_ > 0 && !LockIsHeld()) {
depth = MallocHook::GetCallerStackTrace(const_cast<void**>(region.call_stack),
max_stack_depth_, kStripFrames + 1);
}
region.set_call_stack_depth(depth); // record stack info fully
RAW_VLOG(10, "New global region %p..%p from %p",
reinterpret_cast<void*>(region.start_addr),
reinterpret_cast<void*>(region.end_addr),
reinterpret_cast<void*>(region.caller()));
// Note: none of the above allocates memory.
Lock(); // recursively lock
map_size_ += size;
InsertRegionLocked(region);
// This will (eventually) allocate storage for and copy over the stack data
// from region.call_stack_data_ that is pointed by region.call_stack().
if (bucket_table_ != NULL) {
HeapProfileBucket* b = GetBucket(depth, region.call_stack);
++b->allocs;
b->alloc_size += size;
if (!recursive_insert) {
recursive_insert = true;
RestoreSavedBucketsLocked();
recursive_insert = false;
}
}
Unlock();
}
void MemoryRegionMap::RecordRegionRemoval(const void* start, size_t size) {
Lock();
if (recursive_insert) {
// First remove the removed region from saved_regions, if it's
// there, to prevent overrunning saved_regions in recursive
// map/unmap call sequences, and also from later inserting regions
// which have already been unmapped.
uintptr_t start_addr = reinterpret_cast<uintptr_t>(start);
uintptr_t end_addr = start_addr + size;
int put_pos = 0;
int old_count = saved_regions_count;
for (int i = 0; i < old_count; ++i, ++put_pos) {
Region& r = saved_regions[i];
if (r.start_addr == start_addr && r.end_addr == end_addr) {
// An exact match, so it's safe to remove.
RecordRegionRemovalInBucket(r.call_stack_depth, r.call_stack, size);
--saved_regions_count;
--put_pos;
RAW_VLOG(10, ("Insta-Removing saved region %p..%p; "
"now have %d saved regions"),
reinterpret_cast<void*>(start_addr),
reinterpret_cast<void*>(end_addr),
saved_regions_count);
} else {
if (put_pos < i) {
saved_regions[put_pos] = saved_regions[i];
}
}
}
}
if (regions_ == NULL) { // We must have just unset the hooks,
// but this thread was already inside the hook.
Unlock();
return;
}
if (!recursive_insert) {
HandleSavedRegionsLocked(&InsertRegionLocked);
}
// first handle adding saved regions if any
uintptr_t start_addr = reinterpret_cast<uintptr_t>(start);
uintptr_t end_addr = start_addr + size;
// subtract start_addr, end_addr from all the regions
RAW_VLOG(10, "Removing global region %p..%p; have %zu regions",
reinterpret_cast<void*>(start_addr),
reinterpret_cast<void*>(end_addr),
regions_->size());
Region sample;
sample.SetRegionSetKey(start_addr);
// Only iterate over the regions that might overlap start_addr..end_addr:
for (RegionSet::iterator region = regions_->lower_bound(sample);
region != regions_->end() && region->start_addr < end_addr;
/*noop*/) {
RAW_VLOG(13, "Looking at region %p..%p",
reinterpret_cast<void*>(region->start_addr),
reinterpret_cast<void*>(region->end_addr));
if (start_addr <= region->start_addr &&
region->end_addr <= end_addr) { // full deletion
RAW_VLOG(12, "Deleting region %p..%p",
reinterpret_cast<void*>(region->start_addr),
reinterpret_cast<void*>(region->end_addr));
RecordRegionRemovalInBucket(region->call_stack_depth, region->call_stack,
region->end_addr - region->start_addr);
RegionSet::iterator d = region;
++region;
regions_->erase(d);
continue;
} else if (region->start_addr < start_addr &&
end_addr < region->end_addr) { // cutting-out split
RAW_VLOG(12, "Splitting region %p..%p in two",
reinterpret_cast<void*>(region->start_addr),
reinterpret_cast<void*>(region->end_addr));
RecordRegionRemovalInBucket(region->call_stack_depth, region->call_stack,
end_addr - start_addr);
// Make another region for the start portion:
// The new region has to be the start portion because we can't
// just modify region->end_addr as it's the sorting key.
Region r = *region;
r.set_end_addr(start_addr);
InsertRegionLocked(r);
// cut *region from start:
const_cast<Region&>(*region).set_start_addr(end_addr);
} else if (end_addr > region->start_addr &&
start_addr <= region->start_addr) { // cut from start
RAW_VLOG(12, "Start-chopping region %p..%p",
reinterpret_cast<void*>(region->start_addr),
reinterpret_cast<void*>(region->end_addr));
RecordRegionRemovalInBucket(region->call_stack_depth, region->call_stack,
end_addr - region->start_addr);
const_cast<Region&>(*region).set_start_addr(end_addr);
} else if (start_addr > region->start_addr &&
start_addr < region->end_addr) { // cut from end
RAW_VLOG(12, "End-chopping region %p..%p",
reinterpret_cast<void*>(region->start_addr),
reinterpret_cast<void*>(region->end_addr));
RecordRegionRemovalInBucket(region->call_stack_depth, region->call_stack,
region->end_addr - start_addr);
// Can't just modify region->end_addr (it's the sorting key):
Region r = *region;
r.set_end_addr(start_addr);
RegionSet::iterator d = region;
++region;
// It's safe to erase before inserting since r is independent of *d:
// r contains an own copy of the call stack:
regions_->erase(d);
InsertRegionLocked(r);
continue;
}
++region;
}
RAW_VLOG(12, "Removed region %p..%p; have %zu regions",
reinterpret_cast<void*>(start_addr),
reinterpret_cast<void*>(end_addr),
regions_->size());
if (VLOG_IS_ON(12)) LogAllLocked();
unmap_size_ += size;
Unlock();
}
void MemoryRegionMap::RecordRegionRemovalInBucket(int depth,
const void* const stack[],
size_t size) {
RAW_CHECK(LockIsHeld(), "should be held (by this thread)");
if (bucket_table_ == NULL) return;
HeapProfileBucket* b = GetBucket(depth, stack);
++b->frees;
b->free_size += size;
}
void MemoryRegionMap::MmapHook(const void* result,
const void* start, size_t size,
int prot, int flags,
int fd, off_t offset) {
// TODO(maxim): replace all 0x%" PRIxPTR " by %p when RAW_VLOG uses a safe
// snprintf reimplementation that does not malloc to pretty-print NULL
RAW_VLOG(10, "MMap = 0x%" PRIxPTR " of %zu at %" PRIu64 " "
"prot %d flags %d fd %d offs %" PRId64,
reinterpret_cast<uintptr_t>(result), size,
reinterpret_cast<uint64>(start), prot, flags, fd,
static_cast<int64>(offset));
if (result != reinterpret_cast<void*>(MAP_FAILED) && size != 0) {
RecordRegionAddition(result, size);
}
}
void MemoryRegionMap::MunmapHook(const void* ptr, size_t size) {
RAW_VLOG(10, "MUnmap of %p %zu", ptr, size);
if (size != 0) {
RecordRegionRemoval(ptr, size);
}
}
void MemoryRegionMap::MremapHook(const void* result,
const void* old_addr, size_t old_size,
size_t new_size, int flags,
const void* new_addr) {
RAW_VLOG(10, "MRemap = 0x%" PRIxPTR " of 0x%" PRIxPTR " %zu "
"to %zu flags %d new_addr=0x%" PRIxPTR,
(uintptr_t)result, (uintptr_t)old_addr,
old_size, new_size, flags,
flags & MREMAP_FIXED ? (uintptr_t)new_addr : 0);
if (result != reinterpret_cast<void*>(-1)) {
RecordRegionRemoval(old_addr, old_size);
RecordRegionAddition(result, new_size);
}
}
void MemoryRegionMap::SbrkHook(const void* result, ptrdiff_t increment) {
RAW_VLOG(10, "Sbrk = 0x%" PRIxPTR " of %zd", (uintptr_t)result, increment);
if (result != reinterpret_cast<void*>(-1)) {
if (increment > 0) {
void* new_end = sbrk(0);
RecordRegionAddition(result, reinterpret_cast<uintptr_t>(new_end) -
reinterpret_cast<uintptr_t>(result));
} else if (increment < 0) {
void* new_end = sbrk(0);
RecordRegionRemoval(new_end, reinterpret_cast<uintptr_t>(result) -
reinterpret_cast<uintptr_t>(new_end));
}
}
}
void MemoryRegionMap::LogAllLocked() {
RAW_CHECK(LockIsHeld(), "should be held (by this thread)");
RAW_LOG(INFO, "List of regions:");
uintptr_t previous = 0;
for (RegionSet::const_iterator r = regions_->begin();
r != regions_->end(); ++r) {
RAW_LOG(INFO, "Memory region 0x%" PRIxPTR "..0x%" PRIxPTR " "
"from 0x%" PRIxPTR " stack=%d",
r->start_addr, r->end_addr, r->caller(), r->is_stack);
RAW_CHECK(previous < r->end_addr, "wow, we messed up the set order");
// this must be caused by uncontrolled recursive operations on regions_
previous = r->end_addr;
}
RAW_LOG(INFO, "End of regions list");
}

View file

@ -0,0 +1,416 @@
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
/* Copyright (c) 2006, 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: Maxim Lifantsev
*/
#ifndef BASE_MEMORY_REGION_MAP_H_
#define BASE_MEMORY_REGION_MAP_H_
#include <config.h>
#ifdef HAVE_PTHREAD
#include <pthread.h>
#endif
#include <stddef.h>
#include <set>
#include "base/stl_allocator.h"
#include "base/spinlock.h"
#include "base/thread_annotations.h"
#include "base/low_level_alloc.h"
#include "heap-profile-stats.h"
// TODO(maxim): add a unittest:
// execute a bunch of mmaps and compare memory map what strace logs
// execute a bunch of mmap/munmup and compare memory map with
// own accounting of what those mmaps generated
// Thread-safe class to collect and query the map of all memory regions
// in a process that have been created with mmap, munmap, mremap, sbrk.
// For each memory region, we keep track of (and provide to users)
// the stack trace that allocated that memory region.
// The recorded stack trace depth is bounded by
// a user-supplied max_stack_depth parameter of Init().
// After initialization with Init()
// (which can happened even before global object constructor execution)
// we collect the map by installing and monitoring MallocHook-s
// to mmap, munmap, mremap, sbrk.
// At any time one can query this map via provided interface.
// For more details on the design of MemoryRegionMap
// see the comment at the top of our .cc file.
class MemoryRegionMap {
private:
// Max call stack recording depth supported by Init(). Set it to be
// high enough for all our clients. Note: we do not define storage
// for this (doing that requires special handling in windows), so
// don't take the address of it!
static const int kMaxStackDepth = 32;
// Size of the hash table of buckets. A structure of the bucket table is
// described in heap-profile-stats.h.
static const int kHashTableSize = 179999;
public:
// interface ================================================================
// Every client of MemoryRegionMap must call Init() before first use,
// and Shutdown() after last use. This allows us to reference count
// this (singleton) class properly. MemoryRegionMap assumes it's the
// only client of MallocHooks, so a client can only register other
// MallocHooks after calling Init() and must unregister them before
// calling Shutdown().
// Initialize this module to record memory allocation stack traces.
// Stack traces that have more than "max_stack_depth" frames
// are automatically shrunk to "max_stack_depth" when they are recorded.
// Init() can be called more than once w/o harm, largest max_stack_depth
// will be the effective one.
// When "use_buckets" is true, then counts of mmap and munmap sizes will be
// recorded with each stack trace. If Init() is called more than once, then
// counting will be effective after any call contained "use_buckets" of true.
// It will install mmap, munmap, mremap, sbrk hooks
// and initialize arena_ and our hook and locks, hence one can use
// MemoryRegionMap::Lock()/Unlock() to manage the locks.
// Uses Lock/Unlock inside.
static void Init(int max_stack_depth, bool use_buckets);
// Try to shutdown this module undoing what Init() did.
// Returns true iff could do full shutdown (or it was not attempted).
// Full shutdown is attempted when the number of Shutdown() calls equals
// the number of Init() calls.
static bool Shutdown();
// Return true if MemoryRegionMap is initialized and recording, i.e. when
// then number of Init() calls are more than the number of Shutdown() calls.
static bool IsRecordingLocked();
// Locks to protect our internal data structures.
// These also protect use of arena_ if our Init() has been done.
// The lock is recursive.
static void Lock() EXCLUSIVE_LOCK_FUNCTION(lock_);
static void Unlock() UNLOCK_FUNCTION(lock_);
// Returns true when the lock is held by this thread (for use in RAW_CHECK-s).
static bool LockIsHeld();
// Locker object that acquires the MemoryRegionMap::Lock
// for the duration of its lifetime (a C++ scope).
class LockHolder {
public:
LockHolder() { Lock(); }
~LockHolder() { Unlock(); }
private:
DISALLOW_COPY_AND_ASSIGN(LockHolder);
};
// A memory region that we know about through malloc_hook-s.
// This is essentially an interface through which MemoryRegionMap
// exports the collected data to its clients. Thread-compatible.
struct Region {
uintptr_t start_addr; // region start address
uintptr_t end_addr; // region end address
int call_stack_depth; // number of caller stack frames that we saved
const void* call_stack[kMaxStackDepth]; // caller address stack array
// filled to call_stack_depth size
bool is_stack; // does this region contain a thread's stack:
// a user of MemoryRegionMap supplies this info
// Convenience accessor for call_stack[0],
// i.e. (the program counter of) the immediate caller
// of this region's allocation function,
// but it also returns NULL when call_stack_depth is 0,
// i.e whe we weren't able to get the call stack.
// This usually happens in recursive calls, when the stack-unwinder
// calls mmap() which in turn calls the stack-unwinder.
uintptr_t caller() const {
return reinterpret_cast<uintptr_t>(call_stack_depth >= 1
? call_stack[0] : NULL);
}
// Return true iff this region overlaps region x.
bool Overlaps(const Region& x) const {
return start_addr < x.end_addr && end_addr > x.start_addr;
}
private: // helpers for MemoryRegionMap
friend class MemoryRegionMap;
// The ways we create Region-s:
void Create(const void* start, size_t size) {
start_addr = reinterpret_cast<uintptr_t>(start);
end_addr = start_addr + size;
is_stack = false; // not a stack till marked such
call_stack_depth = 0;
AssertIsConsistent();
}
void set_call_stack_depth(int depth) {
RAW_DCHECK(call_stack_depth == 0, ""); // only one such set is allowed
call_stack_depth = depth;
AssertIsConsistent();
}
// The ways we modify Region-s:
void set_is_stack() { is_stack = true; }
void set_start_addr(uintptr_t addr) {
start_addr = addr;
AssertIsConsistent();
}
void set_end_addr(uintptr_t addr) {
end_addr = addr;
AssertIsConsistent();
}
// Verifies that *this contains consistent data, crashes if not the case.
void AssertIsConsistent() const {
RAW_DCHECK(start_addr < end_addr, "");
RAW_DCHECK(call_stack_depth >= 0 &&
call_stack_depth <= kMaxStackDepth, "");
}
// Post-default construction helper to make a Region suitable
// for searching in RegionSet regions_.
void SetRegionSetKey(uintptr_t addr) {
// make sure *this has no usable data:
if (DEBUG_MODE) memset(this, 0xFF, sizeof(*this));
end_addr = addr;
}
// Note: call_stack[kMaxStackDepth] as a member lets us make Region
// a simple self-contained struct with correctly behaving bit-vise copying.
// This simplifies the code of this module but wastes some memory:
// in most-often use case of this module (leak checking)
// only one call_stack element out of kMaxStackDepth is actually needed.
// Making the storage for call_stack variable-sized,
// substantially complicates memory management for the Region-s:
// as they need to be created and manipulated for some time
// w/o any memory allocations, yet are also given out to the users.
};
// Find the region that covers addr and write its data into *result if found,
// in which case *result gets filled so that it stays fully functional
// even when the underlying region gets removed from MemoryRegionMap.
// Returns success. Uses Lock/Unlock inside.
static bool FindRegion(uintptr_t addr, Region* result);
// Find the region that contains stack_top, mark that region as
// a stack region, and write its data into *result if found,
// in which case *result gets filled so that it stays fully functional
// even when the underlying region gets removed from MemoryRegionMap.
// Returns success. Uses Lock/Unlock inside.
static bool FindAndMarkStackRegion(uintptr_t stack_top, Region* result);
// Iterate over the buckets which store mmap and munmap counts per stack
// trace. It calls "callback" for each bucket, and passes "arg" to it.
template<class Type>
static void IterateBuckets(void (*callback)(const HeapProfileBucket*, Type),
Type arg);
// Get the bucket whose caller stack trace is "key". The stack trace is
// used to a depth of "depth" at most. The requested bucket is created if
// needed.
// The bucket table is described in heap-profile-stats.h.
static HeapProfileBucket* GetBucket(int depth, const void* const key[]);
private: // our internal types ==============================================
// Region comparator for sorting with STL
struct RegionCmp {
bool operator()(const Region& x, const Region& y) const {
return x.end_addr < y.end_addr;
}
};
// We allocate STL objects in our own arena.
struct MyAllocator {
static void *Allocate(size_t n) {
return LowLevelAlloc::AllocWithArena(n, arena_);
}
static void Free(const void *p, size_t /* n */) {
LowLevelAlloc::Free(const_cast<void*>(p));
}
};
// Set of the memory regions
typedef std::set<Region, RegionCmp,
STL_Allocator<Region, MyAllocator> > RegionSet;
public: // more in-depth interface ==========================================
// STL iterator with values of Region
typedef RegionSet::const_iterator RegionIterator;
// Return the begin/end iterators to all the regions.
// These need Lock/Unlock protection around their whole usage (loop).
// Even when the same thread causes modifications during such a loop
// (which are permitted due to recursive locking)
// the loop iterator will still be valid as long as its region
// has not been deleted, but EndRegionLocked should be
// re-evaluated whenever the set of regions has changed.
static RegionIterator BeginRegionLocked();
static RegionIterator EndRegionLocked();
// Return the accumulated sizes of mapped and unmapped regions.
static int64 MapSize() { return map_size_; }
static int64 UnmapSize() { return unmap_size_; }
// Effectively private type from our .cc =================================
// public to let us declare global objects:
union RegionSetRep;
private:
// representation ===========================================================
// Counter of clients of this module that have called Init().
static int client_count_;
// Maximal number of caller stack frames to save (>= 0).
static int max_stack_depth_;
// Arena used for our allocations in regions_.
static LowLevelAlloc::Arena* arena_;
// Set of the mmap/sbrk/mremap-ed memory regions
// To be accessed *only* when Lock() is held.
// Hence we protect the non-recursive lock used inside of arena_
// with our recursive Lock(). This lets a user prevent deadlocks
// when threads are stopped by TCMalloc_ListAllProcessThreads at random spots
// simply by acquiring our recursive Lock() before that.
static RegionSet* regions_;
// Lock to protect regions_ and buckets_ variables and the data behind.
static SpinLock lock_;
// Lock to protect the recursive lock itself.
static SpinLock owner_lock_;
// Recursion count for the recursive lock.
static int recursion_count_;
// The thread id of the thread that's inside the recursive lock.
static pthread_t lock_owner_tid_;
// Total size of all mapped pages so far
static int64 map_size_;
// Total size of all unmapped pages so far
static int64 unmap_size_;
// Bucket hash table which is described in heap-profile-stats.h.
static HeapProfileBucket** bucket_table_ GUARDED_BY(lock_);
static int num_buckets_ GUARDED_BY(lock_);
// The following members are local to MemoryRegionMap::GetBucket()
// and MemoryRegionMap::HandleSavedBucketsLocked()
// and are file-level to ensure that they are initialized at load time.
//
// These are used as temporary storage to break the infinite cycle of mmap
// calling our hook which (sometimes) causes mmap. It must be a static
// fixed-size array. The size 20 is just an expected value for safety.
// The details are described in memory_region_map.cc.
// Number of unprocessed bucket inserts.
static int saved_buckets_count_ GUARDED_BY(lock_);
// Unprocessed inserts (must be big enough to hold all mmaps that can be
// caused by a GetBucket call).
// Bucket has no constructor, so that c-tor execution does not interfere
// with the any-time use of the static memory behind saved_buckets.
static HeapProfileBucket saved_buckets_[20] GUARDED_BY(lock_);
static const void* saved_buckets_keys_[20][kMaxStackDepth] GUARDED_BY(lock_);
// helpers ==================================================================
// Helper for FindRegion and FindAndMarkStackRegion:
// returns the region covering 'addr' or NULL; assumes our lock_ is held.
static const Region* DoFindRegionLocked(uintptr_t addr);
// Verifying wrapper around regions_->insert(region)
// To be called to do InsertRegionLocked's work only!
inline static void DoInsertRegionLocked(const Region& region);
// Handle regions saved by InsertRegionLocked into a tmp static array
// by calling insert_func on them.
inline static void HandleSavedRegionsLocked(
void (*insert_func)(const Region& region));
// Restore buckets saved in a tmp static array by GetBucket to the bucket
// table where all buckets eventually should be.
static void RestoreSavedBucketsLocked();
// Initialize RegionSet regions_.
inline static void InitRegionSetLocked();
// Wrapper around DoInsertRegionLocked
// that handles the case of recursive allocator calls.
inline static void InsertRegionLocked(const Region& region);
// Record addition of a memory region at address "start" of size "size"
// (called from our mmap/mremap/sbrk hooks).
static void RecordRegionAddition(const void* start, size_t size);
// Record deletion of a memory region at address "start" of size "size"
// (called from our munmap/mremap/sbrk hooks).
static void RecordRegionRemoval(const void* start, size_t size);
// Record deletion of a memory region of size "size" in a bucket whose
// caller stack trace is "key". The stack trace is used to a depth of
// "depth" at most.
static void RecordRegionRemovalInBucket(int depth,
const void* const key[],
size_t size);
// Hooks for MallocHook
static void MmapHook(const void* result,
const void* start, size_t size,
int prot, int flags,
int fd, off_t offset);
static void MunmapHook(const void* ptr, size_t size);
static void MremapHook(const void* result, const void* old_addr,
size_t old_size, size_t new_size, int flags,
const void* new_addr);
static void SbrkHook(const void* result, ptrdiff_t increment);
// Log all memory regions; Useful for debugging only.
// Assumes Lock() is held
static void LogAllLocked();
DISALLOW_COPY_AND_ASSIGN(MemoryRegionMap);
};
template <class Type>
void MemoryRegionMap::IterateBuckets(
void (*callback)(const HeapProfileBucket*, Type), Type callback_arg) {
for (int index = 0; index < kHashTableSize; index++) {
for (HeapProfileBucket* bucket = bucket_table_[index];
bucket != NULL;
bucket = bucket->next) {
callback(bucket, callback_arg);
}
}
}
#endif // BASE_MEMORY_REGION_MAP_H_

Some files were not shown because too many files have changed in this diff Show more