Merge branch 'master' of code.uocat.com:tqcq/sled
This commit is contained in:
commit
069aa09d6d
5
3party/cppuprofile/.gitignore
vendored
Normal file
5
3party/cppuprofile/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
build
|
||||
CMakeFiles
|
||||
CMakeCache
|
||||
.vscode
|
||||
.idea
|
14
3party/cppuprofile/.pre-commit-config.yaml
Normal file
14
3party/cppuprofile/.pre-commit-config.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
# See https://pre-commit.com for more information
|
||||
# See https://pre-commit.com/hooks.html for more hooks
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v3.2.0
|
||||
hooks:
|
||||
- id: trailing-whitespace
|
||||
- id: end-of-file-fixer
|
||||
- id: check-yaml
|
||||
- id: check-added-large-files
|
||||
- repo: https://github.com/pre-commit/mirrors-clang-format
|
||||
rev: v16.0.6
|
||||
hooks:
|
||||
- id: clang-format
|
13
3party/cppuprofile/CMakeLists.txt
Normal file
13
3party/cppuprofile/CMakeLists.txt
Normal file
@ -0,0 +1,13 @@
|
||||
PROJECT(cppuprofile)
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12)
|
||||
|
||||
OPTION(PROFILE_ENABLED "Whether library performs profiling operations or does nothing" ON)
|
||||
OPTION(SAMPLE_ENABLED "Whether sample binary is built or not" OFF)
|
||||
OPTION(BUILD_SHARED_LIBS "Build shared libraries" OFF)
|
||||
OPTION(GPU_MONITOR_NVIDIA "Whether NVidiaMonitor class for monitoring NVidia GPUs is compiled and embedded to the library" OFF)
|
||||
|
||||
ADD_SUBDIRECTORY(lib)
|
||||
|
||||
IF(SAMPLE_ENABLED)
|
||||
ADD_SUBDIRECTORY(sample)
|
||||
ENDIF()
|
26
3party/cppuprofile/LICENSE
Normal file
26
3party/cppuprofile/LICENSE
Normal file
@ -0,0 +1,26 @@
|
||||
Copyright (c) 2022, Orange
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. 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.
|
||||
|
||||
3. Neither the name of the copyright holder 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 HOLDER 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 HOLDER 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.
|
153
3party/cppuprofile/README.md
Normal file
153
3party/cppuprofile/README.md
Normal file
@ -0,0 +1,153 @@
|
||||
# cppuprofile
|
||||
|
||||
![GitHub tag](https://img.shields.io/github/v/tag/orange-opensource/cppuprofile)
|
||||
![Licensing](https://img.shields.io/github/license/Orange-OpenSource/cppuprofile)
|
||||
[![Documentation](https://img.shields.io/badge/documentation-ok-green)](https://orange-opensource.github.io/cppuprofile/)
|
||||
|
||||
![Linux](https://img.shields.io/badge/Linux-full_support-green?logo=linux&logoColor=white)
|
||||
![Windows](https://img.shields.io/badge/Windows-partial_support-orange?&logo=windows&logoColor=white)
|
||||
![MacOS](https://img.shields.io/badge/MacOS-not_tested-orange?&logo=apple&logoColor=white)
|
||||
|
||||
This project provides a tiny C++ profiling library for monitoring:
|
||||
* execution time
|
||||
* CPU usage
|
||||
* memory usage
|
||||
* GPU usage and memory
|
||||
|
||||
This library aims at collecting metrics on embedded devices to monitor device
|
||||
performance while operating heavy tasks or booting for example. Those metrics can
|
||||
be useful to check that load is properly spread onto all CPU cores or
|
||||
that memory is not starved.
|
||||
|
||||
This library can also run on non-embedded devices like servers or desktop PCs. It is
|
||||
compatible with Linux and Windows.
|
||||
|
||||
Metrics are stored in a CSV file (the path is configurable).
|
||||
|
||||
## Usage
|
||||
|
||||
The library is lightweight, simple and easy to use. It can be easily used from an existing application or integrated in a dedicated monitoring application.
|
||||
|
||||
Full API documentation is available [here](https://orange-opensource.github.io/cppuprofile/).
|
||||
|
||||
### System memory and CPU usage monitoring
|
||||
|
||||
```cpp
|
||||
#include <uprofile/uprofile.h>
|
||||
...
|
||||
uprofile::start("uprofile.log");
|
||||
uprofile::startSystemMemoryMonitoring(200);
|
||||
uprofile::startCPUUsageMonitoring(200);
|
||||
...
|
||||
uprofile::stop();
|
||||
```
|
||||
|
||||
### Record time execution
|
||||
|
||||
```cpp
|
||||
uprofile::timeBegin("my_custom_function");
|
||||
...
|
||||
uprofile::timeEnd("my_custom_function");
|
||||
```
|
||||
|
||||
### GPU monitoring
|
||||
|
||||
The library also supports GPU metrics monitoring like usage and memory. Since GPU monitoring is specific to each vendor, an interface `IGPUMonitor` is available to abstract each vendor monitor system.
|
||||
|
||||
To monitor a specific GPU, you must subclass `IGPUMonitor`:
|
||||
|
||||
```cpp
|
||||
#include <uprofile/igpumonitor.h>
|
||||
|
||||
class MyGPUMonitor: public uprofile::IGPUMonitor {
|
||||
public:
|
||||
float getUsage() override;
|
||||
void getMemory(int& usedMem, int& totalMem) override;
|
||||
}
|
||||
```
|
||||
|
||||
And then inject it at runtime to the `uprofile` monitoring system:
|
||||
|
||||
```cpp
|
||||
uprofile::addGPUMonitor(new MyGPUMonitor);
|
||||
uprofile::start("uprofile.log");
|
||||
uprofile::startGPUMemoryMonitoring(200);
|
||||
uprofile::startGPUUsageMonitoring(200);
|
||||
```
|
||||
|
||||
#### Supported GPU monitoring
|
||||
|
||||
Here is the list of GPUs supported by `cppuprofile`
|
||||
|
||||
* NVidia Graphics Cards (through `nvidia-smi`). Pass `-DGPU_MONITOR_NVIDIA=ON` as compile option and inject `uprofile::NvidiaMonitor` from `monitors/nvidiamonitor.h` as `GPUMonitor`. The `nvidia-smi` tool should be installed into `/usr/bin` directory.
|
||||
|
||||
## Build
|
||||
|
||||
The build process is based on CMake. Minimum version is 2.8.
|
||||
|
||||
```commandline
|
||||
$ cmake --configure . -B ../build-cppuprofile
|
||||
$ cmake --build ../build-cppuprofile
|
||||
```
|
||||
|
||||
### Shared/dynamic library
|
||||
|
||||
By default, it generates a shared library on Linux and a dynamic library (DLL) on Windows. To link with this library on Windows, you must
|
||||
pass `-DUPROFILE_DLL` definition to CMake.
|
||||
|
||||
### Static library
|
||||
|
||||
If you want to generate a static library, you must use `-DBUILD_SHARED_LIBS=OFF` CMake option.
|
||||
|
||||
### Disable profiling in Release mode
|
||||
|
||||
If you want to disable profiling in Release mode or if you want to only enable profiling in particular cases, you can use the `PROFILE_ENABLED` option (set to `ON` by default).
|
||||
|
||||
To disable the profiling:
|
||||
|
||||
```commandline
|
||||
$ cmake --configure . -B ../build-cppuprofile -DPROFILE_ENABLED=OFF
|
||||
```
|
||||
|
||||
## Tools
|
||||
|
||||
The project also brings a tool for displaying the different metrics in
|
||||
a single view:
|
||||
|
||||
![ScreenshotShowGraph](doc/show-graph-screenshot.png)
|
||||
|
||||
This tool is written in Python3. It requires a set of dependency packages. To install them:
|
||||
|
||||
```commandline
|
||||
$ pip3 install -r requirements.txt
|
||||
```
|
||||
|
||||
Then
|
||||
|
||||
```commandline
|
||||
$ ./tools/show-graph uprofile.log
|
||||
```
|
||||
|
||||
Note that you can filter the metrics to display with `--metric` argument.
|
||||
|
||||
## Sample
|
||||
|
||||
The project provides a C++ sample application called `uprof-sample`
|
||||
that shows how to use the `cppuprofile` library. You can build it with `SAMPLE_ENABLED` option:
|
||||
|
||||
```commandline
|
||||
$ cmake --configure . -B ../build-cppuprofile -DSAMPLE_ENABLED=ON
|
||||
$ cmake --build ../build-cppuprofile
|
||||
$ ../build-cppuprofile/sample/uprof-sample
|
||||
```
|
||||
|
||||
## Windows support limitations
|
||||
|
||||
The library compiles on Windows but only time execution is supported so far. Monitoring metrics like CPU Usage and system, process and nvidia GPU memory are not supported.
|
||||
|
||||
Contributions are welcomed.
|
||||
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under BSD-3-Clause license. See LICENSE file for any further information.
|
2579
3party/cppuprofile/doc/doxygen.cfg
Normal file
2579
3party/cppuprofile/doc/doxygen.cfg
Normal file
File diff suppressed because it is too large
Load Diff
BIN
3party/cppuprofile/doc/show-graph-screenshot.png
Normal file
BIN
3party/cppuprofile/doc/show-graph-screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 54 KiB |
114
3party/cppuprofile/lib/CMakeLists.txt
Normal file
114
3party/cppuprofile/lib/CMakeLists.txt
Normal file
@ -0,0 +1,114 @@
|
||||
PROJECT(cppuprofile DESCRIPTION "CppProfiling library")
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12)
|
||||
|
||||
SET( CMAKE_USE_RELATIVE_PATHS ON)
|
||||
|
||||
IF(CMAKE_COMPILER_IS_GNUCXX)
|
||||
ADD_DEFINITIONS( -std=c++0x )
|
||||
ADD_DEFINITIONS( -D_GLIBCXX_USE_NANOSLEEP )
|
||||
ENDIF()
|
||||
|
||||
IF(WIN32)
|
||||
add_compile_options(/W4)
|
||||
ELSE()
|
||||
add_compile_options(-Wall -Werror)
|
||||
ENDIF()
|
||||
|
||||
IF(BUILD_SHARED_LIBS)
|
||||
IF(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
|
||||
ADD_COMPILE_DEFINITIONS(_UPROFILE_BUILD_SHARED)
|
||||
ELSE()
|
||||
ADD_DEFINITIONS(-D_UPROFILE_BUILD_SHARED)
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
IF(PROFILE_ENABLED)
|
||||
IF(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
|
||||
ADD_COMPILE_DEFINITIONS(PROFILE_ON)
|
||||
ADD_DEFINITIONS( -DPROFILE_ON )
|
||||
ELSE()
|
||||
ADD_DEFINITIONS(-DPROFILE_ON)
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
SET(UProfile_PUBLIC_HEADERS
|
||||
api.h
|
||||
uprofile.h
|
||||
timestampunit.h
|
||||
igpumonitor.h
|
||||
)
|
||||
|
||||
SET(UProfile_IMPL
|
||||
uprofile.cpp
|
||||
uprofileimpl.cpp
|
||||
util/timer.cpp
|
||||
util/cpumonitor.cpp
|
||||
)
|
||||
|
||||
IF(GPU_MONITOR_NVIDIA)
|
||||
LIST(APPEND UProfile_PUBLIC_HEADERS monitors/nvidiamonitor.h)
|
||||
LIST(APPEND UProfile_IMPL monitors/nvidiamonitor.cpp)
|
||||
ENDIF()
|
||||
|
||||
SET(UProfile_HEADERS
|
||||
${UProfile_PUBLIC_HEADERS}
|
||||
uprofileimpl.h
|
||||
util/timer.h
|
||||
util/cpumonitor.h
|
||||
)
|
||||
|
||||
SET(UProfile_SRCS
|
||||
${UProfile_HEADERS}
|
||||
${UProfile_IMPL}
|
||||
)
|
||||
|
||||
SET(LIBRARY_NAME ${PROJECT_NAME})
|
||||
|
||||
ADD_LIBRARY(${LIBRARY_NAME} ${UProfile_SRCS}) # STATIC or SHARED are determined through BUILD_SHARED_LIBS flag
|
||||
|
||||
IF(BUILD_SHARED_LIBS AND UNIX)
|
||||
# Hide symbols not explicitly tagged for export from the shared library
|
||||
TARGET_COMPILE_OPTIONS(${LIBRARY_NAME} PRIVATE "-fvisibility=hidden")
|
||||
ENDIF()
|
||||
|
||||
|
||||
# Specify the include directories exported by this library
|
||||
TARGET_INCLUDE_DIRECTORIES(${LIBRARY_NAME} PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
IF(UNIX)
|
||||
TARGET_LINK_LIBRARIES(${LIBRARY_NAME} pthread)
|
||||
ENDIF()
|
||||
|
||||
# Set specific pkg-config variables
|
||||
SET(PKG_CONFIG_LIBDIR
|
||||
"\${prefix}/lib"
|
||||
)
|
||||
SET(PKG_CONFIG_INCLUDEDIR
|
||||
"\${prefix}/include/${LIBRARY_NAME}"
|
||||
)
|
||||
SET(PKG_CONFIG_LIBS
|
||||
"-L\${libdir} -l${LIBRARY_NAME}"
|
||||
)
|
||||
SET(PKG_CONFIG_CFLAGS
|
||||
"-I\${includedir}"
|
||||
)
|
||||
|
||||
# Generate the pkg-config file
|
||||
CONFIGURE_FILE(
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/pkg-config.pc.cmake"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc"
|
||||
)
|
||||
|
||||
# Install library project files
|
||||
INSTALL(FILES ${UProfile_PUBLIC_HEADERS}
|
||||
DESTINATION include/${LIBRARY_NAME}
|
||||
)
|
||||
INSTALL(TARGETS ${LIBRARY_NAME}
|
||||
LIBRARY DESTINATION lib
|
||||
ARCHIVE DESTINATION lib
|
||||
)
|
||||
INSTALL(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc"
|
||||
DESTINATION lib/pkgconfig
|
||||
)
|
31
3party/cppuprofile/lib/api.h
Normal file
31
3party/cppuprofile/lib/api.h
Normal file
@ -0,0 +1,31 @@
|
||||
// Software Name : cppuprofile
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2023 Orange
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//
|
||||
// This software is distributed under the BSD License;
|
||||
// see the LICENSE file for more details.
|
||||
//
|
||||
// Author: Cédric CHEDALEUX <cedric.chedaleux@orange.com> et al.
|
||||
|
||||
#pragma once
|
||||
|
||||
// UPROFAPI is used to export public API functions from the DLL / shared library.
|
||||
#if defined(_UPROFILE_BUILD_SHARED)
|
||||
#if defined(_WIN32)
|
||||
/* Build as a Win32 DLL */
|
||||
#define UPROFAPI __declspec(dllexport)
|
||||
#elif defined(__linux__)
|
||||
/* Build as a shared library */
|
||||
#define UPROFAPI __attribute__((visibility("default")))
|
||||
#endif // if defined(_UPROFILE_BUILD_SHARED)
|
||||
|
||||
#elif defined(UPROFILE_DLL)
|
||||
#if defined(_WIN32)
|
||||
/* Call uprofile as a Win32 DLL */
|
||||
#define UPROFAPI __declspec(dllimport)
|
||||
#endif // if defined(_WIN32)
|
||||
#endif // if defined(UPROFILE_DLL)
|
||||
|
||||
#if !defined(UPROFAPI)
|
||||
#define UPROFAPI
|
||||
#endif
|
44
3party/cppuprofile/lib/igpumonitor.h
Normal file
44
3party/cppuprofile/lib/igpumonitor.h
Normal file
@ -0,0 +1,44 @@
|
||||
// Software Name : cppuprofile
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2023 Orange
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//
|
||||
// This software is distributed under the BSD License;
|
||||
// see the LICENSE file for more details.
|
||||
//
|
||||
// Author: Cédric CHEDALEUX <cedric.chedaleux@orange.com> et al.
|
||||
|
||||
#ifndef IGPUMONITOR_H_
|
||||
#define IGPUMONITOR_H_
|
||||
|
||||
namespace uprofile
|
||||
{
|
||||
|
||||
/**
|
||||
* Interface to implement for monitoring GPU usage and memory
|
||||
*
|
||||
* No generic abstraction of GPU metrics exists
|
||||
* on Linux nor Windows. So specific IGPUMonitor class should
|
||||
* be defined to retrieve metrics from GPU vendor (Nvidia, AMD, Broadcom
|
||||
* for RPI...)
|
||||
*/
|
||||
class IGPUMonitor
|
||||
{
|
||||
public:
|
||||
virtual ~IGPUMonitor() {}
|
||||
|
||||
// Start monitoring
|
||||
virtual void start(int period) = 0;
|
||||
// Stop monitoring
|
||||
virtual void stop() = 0;
|
||||
// Return if monitor is currently watching data
|
||||
virtual bool watching() const = 0;
|
||||
|
||||
// Usage should be in percentage
|
||||
virtual float getUsage() const = 0;
|
||||
// usedMem and totalMem should be returned as KiB
|
||||
virtual void getMemory(int& usedMem, int& totalMem) const = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* IGPUMONITOR_H_ */
|
171
3party/cppuprofile/lib/monitors/nvidiamonitor.cpp
Normal file
171
3party/cppuprofile/lib/monitors/nvidiamonitor.cpp
Normal file
@ -0,0 +1,171 @@
|
||||
// Software Name : cppuprofile
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2023 Orange
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//
|
||||
// This software is distributed under the BSD License;
|
||||
// see the LICENSE file for more details.
|
||||
//
|
||||
// Author: Cédric CHEDALEUX <cedric.chedaleux@orange.com> et al.
|
||||
|
||||
#include "nvidiamonitor.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
const string errorMsg = "Failed to monitor nvidia-smi process";
|
||||
|
||||
#if defined(__linux__)
|
||||
int read_nvidia_smi_stdout(int fd, string& gpuUsage, string& usedMem, string& totalMem)
|
||||
{
|
||||
string line;
|
||||
while (line.find('\n') == string::npos) { // full line read
|
||||
char buffer[4096];
|
||||
ssize_t count = read(fd, buffer, sizeof(buffer)); // if child process crashes, we gonna be blocked here forever
|
||||
if (count == -1) {
|
||||
return errno;
|
||||
} else if (count > 0) { // there is something to read
|
||||
line += string(buffer, count);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove colon to have only spaces and use istringstream
|
||||
auto noSpaceEnd = remove(line.begin(), line.end(), ',');
|
||||
if (noSpaceEnd == line.end()) { // output trace does not have comma so something went wrong with the command
|
||||
return ENODATA;
|
||||
}
|
||||
|
||||
line.erase(noSpaceEnd, line.end());
|
||||
std::istringstream ss(line);
|
||||
ss >> gpuUsage >> usedMem >> totalMem;
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
uprofile::NvidiaMonitor::NvidiaMonitor()
|
||||
{
|
||||
}
|
||||
|
||||
uprofile::NvidiaMonitor::~NvidiaMonitor()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void uprofile::NvidiaMonitor::start(int period)
|
||||
{
|
||||
watchGPU(period);
|
||||
}
|
||||
|
||||
void uprofile::NvidiaMonitor::stop()
|
||||
{
|
||||
abortWatchGPU();
|
||||
}
|
||||
|
||||
float uprofile::NvidiaMonitor::getUsage() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
return m_gpuUsage;
|
||||
}
|
||||
|
||||
void uprofile::NvidiaMonitor::getMemory(int& usedMem, int& totalMem) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
usedMem = m_usedMem;
|
||||
totalMem = m_totalMem;
|
||||
}
|
||||
|
||||
void uprofile::NvidiaMonitor::watchGPU(int period)
|
||||
{
|
||||
if (m_watching) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
char* args[5];
|
||||
args[0] = (char*)"/usr/bin/nvidia-smi";
|
||||
string period_arg = "-lms=" + to_string(period); // lms stands for continuous watching
|
||||
args[1] = (char*)period_arg.c_str();
|
||||
args[2] = (char*)"--query-gpu=utilization.gpu,memory.used,memory.total";
|
||||
args[3] = (char*)"--format=csv,noheader,nounits";
|
||||
args[4] = NULL;
|
||||
string output;
|
||||
int pipes[2];
|
||||
|
||||
// Create the pipe
|
||||
if (pipe(pipes) == -1) {
|
||||
cerr << errorMsg << ": pipe creation failed" << endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a child process for calling nvidia-smi
|
||||
pid_t pid = fork();
|
||||
|
||||
switch (pid) {
|
||||
case -1: /* Error */
|
||||
cerr << errorMsg << ": process fork failed" << endl;
|
||||
return;
|
||||
case 0: /* We are in the child process */
|
||||
while ((dup2(pipes[1], STDOUT_FILENO) == -1) && (errno == EINTR)) {
|
||||
}
|
||||
close(pipes[1]);
|
||||
close(pipes[0]);
|
||||
execv(args[0], args);
|
||||
cerr << "Failed to execute '" << args[0] << "': " << strerror(errno) << endl; /* execl doesn't return unless there's an error */
|
||||
exit(1);
|
||||
default: /* We are in the parent process */
|
||||
int stdout_fd = pipes[0];
|
||||
|
||||
// Start a thread to retrieve the child process stdout
|
||||
m_watching = true;
|
||||
m_watcherThread = unique_ptr<std::thread>(new thread([stdout_fd, pid, this]() {
|
||||
while (watching()) {
|
||||
string gpuUsage, usedMem, totalMem;
|
||||
// if the child process crashes, an error is raised here and threads ends up
|
||||
int err = read_nvidia_smi_stdout(stdout_fd, gpuUsage, usedMem, totalMem);
|
||||
if (err != 0) {
|
||||
cerr << errorMsg << ": read_error = " << strerror(err) << endl;
|
||||
m_mutex.lock();
|
||||
m_watching = false;
|
||||
m_mutex.unlock();
|
||||
break;
|
||||
}
|
||||
m_mutex.lock();
|
||||
m_gpuUsage = !gpuUsage.empty() ? stof(gpuUsage) : 0.f;
|
||||
m_usedMem = !usedMem.empty() ? stoi(usedMem) * 1024 : 0; // MiB to KiB
|
||||
m_totalMem = !totalMem.empty() ? stoi(totalMem) * 1024 : 0; // MiB to KiB
|
||||
m_mutex.unlock();
|
||||
}
|
||||
}));
|
||||
}
|
||||
#else
|
||||
cerr << errorMsg << endl;
|
||||
#endif
|
||||
}
|
||||
|
||||
void uprofile::NvidiaMonitor::abortWatchGPU()
|
||||
{
|
||||
#if defined(__linux__)
|
||||
if (m_watcherThread) {
|
||||
m_mutex.lock();
|
||||
m_watching = false;
|
||||
m_mutex.unlock();
|
||||
m_watcherThread->join();
|
||||
m_watcherThread.reset();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool uprofile::NvidiaMonitor::watching() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
return m_watching;
|
||||
}
|
49
3party/cppuprofile/lib/monitors/nvidiamonitor.h
Normal file
49
3party/cppuprofile/lib/monitors/nvidiamonitor.h
Normal file
@ -0,0 +1,49 @@
|
||||
// Software Name : cppuprofile
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2023 Orange
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//
|
||||
// This software is distributed under the BSD License;
|
||||
// see the LICENSE file for more details.
|
||||
//
|
||||
// Author: Cédric CHEDALEUX <cedric.chedaleux@orange.com> et al.
|
||||
|
||||
#ifndef NVIDIAMONITOR_H_
|
||||
#define NVIDIAMONITOR_H_
|
||||
|
||||
#include "api.h"
|
||||
#include "igpumonitor.h"
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace uprofile
|
||||
{
|
||||
class NvidiaMonitor : public IGPUMonitor
|
||||
{
|
||||
public:
|
||||
UPROFAPI explicit NvidiaMonitor();
|
||||
UPROFAPI virtual ~NvidiaMonitor();
|
||||
|
||||
UPROFAPI void start(int period) override;
|
||||
UPROFAPI void stop() override;
|
||||
UPROFAPI bool watching() const override;
|
||||
UPROFAPI float getUsage() const override;
|
||||
UPROFAPI void getMemory(int& usedMem, int& totalMem) const override;
|
||||
|
||||
private:
|
||||
void watchGPU(int period);
|
||||
void abortWatchGPU();
|
||||
|
||||
mutable std::mutex m_mutex;
|
||||
std::unique_ptr<std::thread> m_watcherThread;
|
||||
bool m_watching = false;
|
||||
int m_totalMem = 0;
|
||||
int m_usedMem = 0;
|
||||
float m_gpuUsage = 0.f;
|
||||
};
|
||||
|
||||
}
|
||||
#endif /* NVIDIAMONITOR_H_ */
|
8
3party/cppuprofile/lib/pkg-config.pc.cmake
Normal file
8
3party/cppuprofile/lib/pkg-config.pc.cmake
Normal file
@ -0,0 +1,8 @@
|
||||
Name: ${PROJECT_NAME}
|
||||
Description: ${PROJECT_DESCRIPTION}
|
||||
Version: ${PROJECT_VERSION}
|
||||
prefix=${CMAKE_INSTALL_PREFIX}
|
||||
includedir=${PKG_CONFIG_INCLUDEDIR}
|
||||
libdir=${PKG_CONFIG_LIBDIR}
|
||||
Libs: ${PKG_CONFIG_LIBS}
|
||||
Cflags: ${PKG_CONFIG_CFLAGS}
|
23
3party/cppuprofile/lib/timestampunit.h
Normal file
23
3party/cppuprofile/lib/timestampunit.h
Normal file
@ -0,0 +1,23 @@
|
||||
// Software Name : cppuprofile
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022 Orange
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//
|
||||
// This software is distributed under the BSD License;
|
||||
// see the LICENSE file for more details.
|
||||
//
|
||||
// Author: Cédric CHEDALEUX <cedric.chedaleux@orange.com> et al.
|
||||
|
||||
#ifndef TIMESTAMP_UNIT_H_
|
||||
#define TIMESTAMP_UNIT_H_
|
||||
|
||||
namespace uprofile
|
||||
{
|
||||
|
||||
enum class TimestampUnit {
|
||||
EPOCH_TIME, // Time since epoch
|
||||
UPTIME // Time since boot
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* TIMESTAMP_UNIT_H_ */
|
113
3party/cppuprofile/lib/uprofile.cpp
Normal file
113
3party/cppuprofile/lib/uprofile.cpp
Normal file
@ -0,0 +1,113 @@
|
||||
// Software Name : cppuprofile
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022 Orange
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//
|
||||
// This software is distributed under the BSD License;
|
||||
// see the LICENSE file for more details.
|
||||
//
|
||||
// Author: Cédric CHEDALEUX <cedric.chedaleux@orange.com> et al.
|
||||
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "uprofile.h"
|
||||
#include "uprofileimpl.h"
|
||||
|
||||
using namespace std::chrono;
|
||||
|
||||
#ifdef PROFILE_ON
|
||||
#define UPROFILE_INSTANCE_CALL(func, ...) \
|
||||
UProfileImpl::getInstance()->func(__VA_ARGS__);
|
||||
#define UPROFILE_INSTANCE_CALL_RETURN(func, ...) \
|
||||
return UProfileImpl::getInstance()->func(__VA_ARGS__);
|
||||
#define UPROFILE_DESTROY_INSTANCE() \
|
||||
UProfileImpl::destroyInstance();
|
||||
#else
|
||||
#define UPROFILE_INSTANCE_CALL(func, ...) (void)0;
|
||||
#define UPROFILE_INSTANCE_CALL_RETURN(func, ...) \
|
||||
return {}
|
||||
#define UPROFILE_DESTROY_INSTANCE() (void)0;
|
||||
#endif
|
||||
|
||||
namespace uprofile
|
||||
{
|
||||
|
||||
void start(const char* file)
|
||||
{
|
||||
UPROFILE_INSTANCE_CALL(start, file);
|
||||
}
|
||||
|
||||
void stop()
|
||||
{
|
||||
UPROFILE_INSTANCE_CALL(stop);
|
||||
UPROFILE_DESTROY_INSTANCE();
|
||||
}
|
||||
|
||||
void addGPUMonitor(IGPUMonitor* monitor)
|
||||
{
|
||||
UPROFILE_INSTANCE_CALL(addGPUMonitor, monitor);
|
||||
}
|
||||
|
||||
void removeGPUMonitor()
|
||||
{
|
||||
UPROFILE_INSTANCE_CALL(removeGPUMonitor);
|
||||
}
|
||||
|
||||
void setTimestampUnit(TimestampUnit tsUnit)
|
||||
{
|
||||
UPROFILE_INSTANCE_CALL(setTimestampUnit, tsUnit);
|
||||
}
|
||||
|
||||
void timeBegin(const std::string& step)
|
||||
{
|
||||
UPROFILE_INSTANCE_CALL(timeBegin, step);
|
||||
}
|
||||
|
||||
void timeEnd(const std::string& step)
|
||||
{
|
||||
UPROFILE_INSTANCE_CALL(timeEnd, step);
|
||||
}
|
||||
|
||||
void startProcessMemoryMonitoring(int period)
|
||||
{
|
||||
UPROFILE_INSTANCE_CALL(startProcessMemoryMonitoring, period);
|
||||
}
|
||||
|
||||
void startSystemMemoryMonitoring(int period)
|
||||
{
|
||||
UPROFILE_INSTANCE_CALL(startSystemMemoryMonitoring, period);
|
||||
}
|
||||
|
||||
void startCPUUsageMonitoring(int period)
|
||||
{
|
||||
UPROFILE_INSTANCE_CALL(startCPUUsageMonitoring, period);
|
||||
}
|
||||
|
||||
void startGPUUsageMonitoring(int period)
|
||||
{
|
||||
UPROFILE_INSTANCE_CALL(startGPUUsageMonitoring, period);
|
||||
}
|
||||
|
||||
void startGPUMemoryMonitoring(int period)
|
||||
{
|
||||
UPROFILE_INSTANCE_CALL(startGPUMemoryMonitoring, period);
|
||||
}
|
||||
|
||||
void getProcessMemory(int& rss, int& shared)
|
||||
{
|
||||
UPROFILE_INSTANCE_CALL(getProcessMemory, rss, shared);
|
||||
}
|
||||
|
||||
void getSystemMemory(int& totalMem, int& availableMem, int& freeMem)
|
||||
{
|
||||
UPROFILE_INSTANCE_CALL(getSystemMemory, totalMem, availableMem, freeMem);
|
||||
}
|
||||
|
||||
std::vector<float> getInstantCpuUsage()
|
||||
{
|
||||
UPROFILE_INSTANCE_CALL_RETURN(getInstantCpuUsage);
|
||||
}
|
||||
|
||||
}
|
142
3party/cppuprofile/lib/uprofile.h
Normal file
142
3party/cppuprofile/lib/uprofile.h
Normal file
@ -0,0 +1,142 @@
|
||||
// Software Name : cppuprofile
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022 Orange
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//
|
||||
// This software is distributed under the BSD License;
|
||||
// see the LICENSE file for more details.
|
||||
//
|
||||
// Author: Cédric CHEDALEUX <cedric.chedaleux@orange.com> et al.
|
||||
|
||||
#ifndef UPROFILE_H_
|
||||
#define UPROFILE_H_
|
||||
|
||||
#include <fstream>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "api.h"
|
||||
#include "igpumonitor.h"
|
||||
#include "timestampunit.h"
|
||||
|
||||
/**
|
||||
* @defgroup uprofile Functions for monitoring system metrics
|
||||
* @{
|
||||
*/
|
||||
namespace uprofile
|
||||
{
|
||||
/**
|
||||
* @ingroup uprofile
|
||||
* @brief Start profiling to periodically record monitored events to a file
|
||||
* @param file: file path where events will be saved
|
||||
*/
|
||||
UPROFAPI void start(const char* file);
|
||||
|
||||
/**
|
||||
* @ingroup uprofile
|
||||
* @brief Stop all monitorings
|
||||
*
|
||||
* Note: stopping profiler without prior call to start() has no effect
|
||||
*/
|
||||
UPROFAPI void stop();
|
||||
|
||||
/**
|
||||
* @ingroup uprofile
|
||||
* @brief Inject a GPUMonitor object that will be responsible for monitoring GPU metrics (usage and memory)
|
||||
* @param monitor: custom GPUMonitor object
|
||||
*
|
||||
* Note: uprofile takes ownership of the passed object.
|
||||
*/
|
||||
UPROFAPI void addGPUMonitor(IGPUMonitor* monitor);
|
||||
|
||||
/**
|
||||
* @ingroup uprofile
|
||||
* @brief Destroy injected GPUMonitor object
|
||||
*/
|
||||
UPROFAPI void removeGPUMonitor();
|
||||
|
||||
/**
|
||||
* @ingroup uprofile
|
||||
* @brief Change the timestamp unit to record profiling metrics
|
||||
*
|
||||
* It should be called before calling start() method.
|
||||
*
|
||||
* Note: default value is EPOCH_TIME
|
||||
*/
|
||||
UPROFAPI void setTimestampUnit(TimestampUnit tsUnit);
|
||||
|
||||
/**
|
||||
* @ingroup uprofile
|
||||
* @brief Start monitoring the execution time of the given event
|
||||
* @param title: event key
|
||||
*/
|
||||
UPROFAPI void timeBegin(const std::string& title);
|
||||
|
||||
/**
|
||||
* @ingroup uprofile
|
||||
* @brief Stop monitoring the execution time of the given event
|
||||
*
|
||||
* The library computes the duration for the given event and saves it into the report file.
|
||||
*
|
||||
* If no timeBegin() has been called with the given title, the call is ignored.
|
||||
*/
|
||||
UPROFAPI void timeEnd(const std::string& title);
|
||||
|
||||
/**
|
||||
* @ingroup uprofile
|
||||
* @brief Start monitoring of the memory used by the process
|
||||
* @param period: period between two memory dump (in ms)
|
||||
*/
|
||||
UPROFAPI void startProcessMemoryMonitoring(int period);
|
||||
|
||||
/**
|
||||
* @ingroup uprofile
|
||||
* @brief Start monitoring of the global memory used on the system
|
||||
* @param period: period between two memory dump (in ms)
|
||||
*/
|
||||
UPROFAPI void startSystemMemoryMonitoring(int period);
|
||||
|
||||
/**
|
||||
* @ingroup uprofile
|
||||
* @brief Start monitoring of the usage percentage of each CPU
|
||||
* @param period: period between two cpu usage dump (in ms)
|
||||
*/
|
||||
UPROFAPI void startCPUUsageMonitoring(int period);
|
||||
|
||||
/**
|
||||
* @ingroup uprofile
|
||||
* @brief Start monitoring of the usage of the GPU
|
||||
* @param period: period between two gpu usage dump (in ms)
|
||||
*/
|
||||
UPROFAPI void startGPUUsageMonitoring(int period);
|
||||
|
||||
/**
|
||||
* @ingroup uprofile
|
||||
* @brief Start monitoring of the usage of the GPU memory
|
||||
* @param period: period between two gpu usage dump (in ms)
|
||||
*/
|
||||
UPROFAPI void startGPUMemoryMonitoring(int period);
|
||||
|
||||
/**
|
||||
* @ingroup uprofile
|
||||
* @brief memory used by the current process
|
||||
*/
|
||||
UPROFAPI void getProcessMemory(int& rss, int& shared);
|
||||
|
||||
/**
|
||||
* @ingroup uprofile
|
||||
* @brief dump global system memory
|
||||
*/
|
||||
UPROFAPI void getSystemMemory(int& totalMem, int& availableMem, int& freeMem);
|
||||
|
||||
/**
|
||||
* @ingroup uprofile
|
||||
* @brief get usage of all cpu cores
|
||||
* @return vector holding the usage percentage of each CPU core
|
||||
*/
|
||||
UPROFAPI std::vector<float> getInstantCpuUsage();
|
||||
}
|
||||
/** @} */ // end of uprofile group
|
||||
|
||||
#endif /* UPROFILE_H_ */
|
353
3party/cppuprofile/lib/uprofileimpl.cpp
Normal file
353
3party/cppuprofile/lib/uprofileimpl.cpp
Normal file
@ -0,0 +1,353 @@
|
||||
// Software Name : cppuprofile
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022 Orange
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//
|
||||
// This software is distributed under the BSD License;
|
||||
// see the LICENSE file for more details.
|
||||
//
|
||||
// Author: Cédric CHEDALEUX <cedric.chedaleux@orange.com> et al.
|
||||
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#if defined(__linux__)
|
||||
#include <sys/sysinfo.h>
|
||||
#include <unistd.h>
|
||||
#elif defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#elif defined(__APPLE__)
|
||||
#include <sys/resource.h>
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include "uprofileimpl.h"
|
||||
|
||||
using namespace std::chrono;
|
||||
|
||||
namespace uprofile {
|
||||
|
||||
UProfileImpl *UProfileImpl::m_uprofiler = NULL;
|
||||
|
||||
UProfileImpl::UProfileImpl() : m_tsUnit(TimestampUnit::EPOCH_TIME), m_gpuMonitor(NULL) {}
|
||||
|
||||
UProfileImpl *
|
||||
UProfileImpl::getInstance()
|
||||
{
|
||||
if (!m_uprofiler) { m_uprofiler = new UProfileImpl; }
|
||||
return m_uprofiler;
|
||||
}
|
||||
|
||||
void
|
||||
UProfileImpl::destroyInstance()
|
||||
{
|
||||
delete m_uprofiler;
|
||||
m_uprofiler = NULL;
|
||||
}
|
||||
|
||||
UProfileImpl::~UProfileImpl() { removeGPUMonitor(); }
|
||||
|
||||
void
|
||||
UProfileImpl::start(const char *file)
|
||||
{
|
||||
m_file.open(file, std::ios::out);
|
||||
if (!m_file.is_open()) { std::cerr << "Failed to open file: " << file << std::endl; }
|
||||
}
|
||||
|
||||
void
|
||||
UProfileImpl::addGPUMonitor(IGPUMonitor *monitor)
|
||||
{
|
||||
if (!monitor) {
|
||||
std::cerr << "Invalid GPU Monitor" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_gpuMonitor) { removeGPUMonitor(); }
|
||||
|
||||
m_gpuMonitor = monitor;
|
||||
}
|
||||
|
||||
void
|
||||
UProfileImpl::removeGPUMonitor()
|
||||
{
|
||||
delete m_gpuMonitor;
|
||||
m_gpuMonitor = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
UProfileImpl::timeBegin(const std::string &title)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_stepsMutex);
|
||||
m_steps.insert(make_pair(title, getTimestamp()));
|
||||
}
|
||||
|
||||
void
|
||||
UProfileImpl::timeEnd(const std::string &title)
|
||||
{
|
||||
unsigned long long beginTimestamp = 0;
|
||||
|
||||
// Find step in the map
|
||||
m_stepsMutex.lock();
|
||||
auto it = m_steps.find(title);
|
||||
bool found = it != m_steps.end();
|
||||
if (found) {
|
||||
beginTimestamp = (*it).second;
|
||||
m_steps.erase(it);
|
||||
}
|
||||
m_stepsMutex.unlock();
|
||||
|
||||
if (found) {
|
||||
write(ProfilingType::TIME_EXEC, {std::to_string(beginTimestamp), title});
|
||||
} else {
|
||||
write(ProfilingType::TIME_EVENT, {title});
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
UProfileImpl::startProcessMemoryMonitoring(int period)
|
||||
{
|
||||
m_processMemoryMonitorTimer.setInterval(period);
|
||||
m_processMemoryMonitorTimer.setTimeout([=]() { dumpProcessMemory(); });
|
||||
m_processMemoryMonitorTimer.start();
|
||||
}
|
||||
|
||||
void
|
||||
UProfileImpl::startSystemMemoryMonitoring(int period)
|
||||
{
|
||||
m_systemMemoryMonitorTimer.setInterval(period);
|
||||
m_systemMemoryMonitorTimer.setTimeout([=]() { dumpSystemMemory(); });
|
||||
m_systemMemoryMonitorTimer.start();
|
||||
}
|
||||
|
||||
void
|
||||
UProfileImpl::startCPUUsageMonitoring(int period)
|
||||
{
|
||||
m_cpuMonitorTimer.setInterval(period);
|
||||
m_cpuMonitorTimer.setTimeout([=]() { dumpCpuUsage(); });
|
||||
m_cpuMonitorTimer.start();
|
||||
}
|
||||
|
||||
void
|
||||
UProfileImpl::startGPUUsageMonitoring(int period)
|
||||
{
|
||||
if (!m_gpuMonitor) {
|
||||
std::cerr << "Cannot monitor GPU usage: no GPUMonitor set!" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
m_gpuMonitor->start(period);
|
||||
|
||||
m_gpuUsageMonitorTimer.setInterval(period);
|
||||
m_gpuUsageMonitorTimer.setTimeout([=]() { dumpGpuUsage(); });
|
||||
m_gpuUsageMonitorTimer.start();
|
||||
}
|
||||
|
||||
void
|
||||
UProfileImpl::startGPUMemoryMonitoring(int period)
|
||||
{
|
||||
if (!m_gpuMonitor) {
|
||||
std::cerr << "Cannot monitor GPU memory: no GPUMonitor set!" << std::endl;
|
||||
return;
|
||||
}
|
||||
m_gpuMonitor->start(period);
|
||||
|
||||
m_gpuMemoryMonitorTimer.setInterval(period);
|
||||
m_gpuMemoryMonitorTimer.setTimeout([=]() { dumpGpuMemory(); });
|
||||
m_gpuMemoryMonitorTimer.start();
|
||||
}
|
||||
|
||||
void
|
||||
UProfileImpl::dumpProcessMemory()
|
||||
{
|
||||
int rss = 0, shared = 0;
|
||||
getProcessMemory(rss, shared);
|
||||
write(ProfilingType::PROCESS_MEMORY, {std::to_string(rss), std::to_string(shared)});
|
||||
}
|
||||
|
||||
void
|
||||
UProfileImpl::dumpSystemMemory()
|
||||
{
|
||||
int total = 0, available = 0, free = 0;
|
||||
getSystemMemory(total, available, free);
|
||||
write(ProfilingType::SYSTEM_MEMORY, {std::to_string(total), std::to_string(available), std::to_string(free)});
|
||||
}
|
||||
|
||||
void
|
||||
UProfileImpl::dumpCpuUsage()
|
||||
{
|
||||
vector<float> cpuLoads = m_cpuMonitor.getUsage();
|
||||
for (size_t index = 0; index < cpuLoads.size(); ++index) {
|
||||
write(ProfilingType::CPU, {std::to_string(index), std::to_string(cpuLoads.at(index))});
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
UProfileImpl::dumpGpuUsage()
|
||||
{
|
||||
if (!m_gpuMonitor || !m_gpuMonitor->watching()) { return; }
|
||||
|
||||
float usage = m_gpuMonitor->getUsage();
|
||||
write(ProfilingType::GPU_USAGE, {std::to_string(usage)});
|
||||
}
|
||||
|
||||
void
|
||||
UProfileImpl::dumpGpuMemory()
|
||||
{
|
||||
if (!m_gpuMonitor || !m_gpuMonitor->watching()) { return; }
|
||||
|
||||
int usedMem, totalMem;
|
||||
m_gpuMonitor->getMemory(usedMem, totalMem);
|
||||
write(ProfilingType::GPU_MEMORY, {std::to_string(usedMem), std::to_string(totalMem)});
|
||||
}
|
||||
|
||||
vector<float>
|
||||
UProfileImpl::getInstantCpuUsage()
|
||||
{
|
||||
// To get instaneous CPU usage, we should wait at least one unit between two polling (aka: 100 ms)
|
||||
m_cpuMonitor.getUsage();
|
||||
this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
return m_cpuMonitor.getUsage();
|
||||
}
|
||||
|
||||
void
|
||||
UProfileImpl::stop()
|
||||
{
|
||||
m_processMemoryMonitorTimer.stop();
|
||||
m_systemMemoryMonitorTimer.stop();
|
||||
m_cpuMonitorTimer.stop();
|
||||
m_gpuUsageMonitorTimer.stop();
|
||||
m_gpuMemoryMonitorTimer.stop();
|
||||
if (m_gpuMonitor) { m_gpuMonitor->stop(); }
|
||||
m_file.close();
|
||||
}
|
||||
|
||||
void
|
||||
UProfileImpl::setTimestampUnit(TimestampUnit tsUnit)
|
||||
{
|
||||
m_tsUnit = tsUnit;
|
||||
}
|
||||
|
||||
void
|
||||
UProfileImpl::write(ProfilingType type, const std::list<std::string> &data)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(m_fileMutex);
|
||||
if (m_file.is_open()) {
|
||||
const char csvSeparator = ';';
|
||||
std::string strType;
|
||||
switch (type) {
|
||||
case ProfilingType::TIME_EXEC:
|
||||
strType = "time_exec";
|
||||
break;
|
||||
case ProfilingType::TIME_EVENT:
|
||||
strType = "time_event";
|
||||
break;
|
||||
case ProfilingType::PROCESS_MEMORY:
|
||||
strType = "proc_mem";
|
||||
break;
|
||||
case ProfilingType::SYSTEM_MEMORY:
|
||||
strType = "sys_mem";
|
||||
break;
|
||||
case ProfilingType::CPU:
|
||||
strType = "cpu";
|
||||
break;
|
||||
case ProfilingType::GPU_USAGE:
|
||||
strType = "gpu";
|
||||
break;
|
||||
case ProfilingType::GPU_MEMORY:
|
||||
strType = "gpu_mem";
|
||||
break;
|
||||
default:
|
||||
strType = "undefined";
|
||||
break;
|
||||
}
|
||||
m_file << strType.c_str() << csvSeparator << getTimestamp();
|
||||
for (auto it = data.cbegin(); it != data.cend(); ++it) { m_file << csvSeparator << *it; }
|
||||
m_file << "\n";
|
||||
m_file.flush();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned long long
|
||||
UProfileImpl::getTimestamp() const
|
||||
{
|
||||
return (m_tsUnit == TimestampUnit::EPOCH_TIME ? getEpochTime() : getTimeSinceBoot());
|
||||
}
|
||||
|
||||
unsigned long long
|
||||
UProfileImpl::getEpochTime()
|
||||
{
|
||||
return duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count();
|
||||
}
|
||||
|
||||
unsigned long long
|
||||
UProfileImpl::getTimeSinceBoot()
|
||||
{
|
||||
#if defined(__linux__)
|
||||
double uptime_seconds;
|
||||
if (std::ifstream("/proc/uptime", std::ios::in) >> uptime_seconds) {
|
||||
return static_cast<unsigned long long>(uptime_seconds * 1000.0);
|
||||
}
|
||||
return 0;
|
||||
#elif defined(_WIN32)
|
||||
return GetTickCount64();
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
UProfileImpl::getSystemMemory(int &totalMem, int &availableMem, int &freeMem)
|
||||
{
|
||||
|
||||
#if defined(__linux__)
|
||||
// /proc/meminfo returns the dump here:
|
||||
// MemTotal: 515164 kB
|
||||
// MemFree: 7348 kB
|
||||
// MemAvailable: 7348 kB
|
||||
ifstream meminfo("/proc/meminfo");
|
||||
string line;
|
||||
while (std::getline(meminfo, line)) {
|
||||
if (line.find("MemTotal") != std::string::npos) {
|
||||
stringstream ls(line);
|
||||
ls.ignore(256, ' ');
|
||||
ls >> totalMem;
|
||||
} else if (line.find("MemFree") != std::string::npos) {
|
||||
stringstream ls(line);
|
||||
ls.ignore(256, ' ');
|
||||
ls >> freeMem;
|
||||
} else if (line.find("MemAvailable") != std::string::npos) {
|
||||
{
|
||||
stringstream ls(line);
|
||||
ls.ignore(256, ' ');
|
||||
ls >> availableMem;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
UProfileImpl::getProcessMemory(int &rss, int &shared)
|
||||
{
|
||||
|
||||
#if defined(__linux__)
|
||||
int tSize = 0, resident = 0, share = 0;
|
||||
ifstream buffer("/proc/self/statm");
|
||||
buffer >> tSize >> resident >> share;
|
||||
buffer.close();
|
||||
|
||||
long page_size_kb = getpagesize() / 1024;
|
||||
rss = resident * page_size_kb;
|
||||
shared = share * page_size_kb;
|
||||
#elif defined(__APPLE__)
|
||||
struct rusage usage;
|
||||
if (0 == getrusage(RUSAGE_SELF, &usage)) {
|
||||
rss = usage.ru_maxrss;
|
||||
shared = usage.ru_ixrss;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
}// namespace uprofile
|
91
3party/cppuprofile/lib/uprofileimpl.h
Normal file
91
3party/cppuprofile/lib/uprofileimpl.h
Normal file
@ -0,0 +1,91 @@
|
||||
// Software Name : cppuprofile
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022 Orange
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//
|
||||
// This software is distributed under the BSD License;
|
||||
// see the LICENSE file for more details.
|
||||
//
|
||||
// Author: Cédric CHEDALEUX <cedric.chedaleux@orange.com> et al.
|
||||
|
||||
#ifndef UPROFILEIMPL_H_
|
||||
#define UPROFILEIMPL_H_
|
||||
|
||||
#include "igpumonitor.h"
|
||||
#include "timestampunit.h"
|
||||
#include "util/cpumonitor.h"
|
||||
#include "util/timer.h"
|
||||
#include <fstream>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
|
||||
namespace uprofile
|
||||
{
|
||||
class UProfileImpl
|
||||
{
|
||||
public:
|
||||
enum class ProfilingType {
|
||||
TIME_EXEC,
|
||||
TIME_EVENT,
|
||||
PROCESS_MEMORY,
|
||||
SYSTEM_MEMORY,
|
||||
CPU,
|
||||
GPU_USAGE,
|
||||
GPU_MEMORY
|
||||
};
|
||||
|
||||
static UProfileImpl* getInstance();
|
||||
static void destroyInstance();
|
||||
virtual ~UProfileImpl();
|
||||
|
||||
// Implementation
|
||||
void start(const char* file);
|
||||
void stop();
|
||||
void addGPUMonitor(IGPUMonitor* monitor);
|
||||
void removeGPUMonitor();
|
||||
void setTimestampUnit(TimestampUnit tsUnit);
|
||||
void timeBegin(const std::string& title);
|
||||
void timeEnd(const std::string& title);
|
||||
void startProcessMemoryMonitoring(int period);
|
||||
void startSystemMemoryMonitoring(int period);
|
||||
void startCPUUsageMonitoring(int period);
|
||||
void startGPUUsageMonitoring(int period);
|
||||
void startGPUMemoryMonitoring(int period);
|
||||
void getProcessMemory(int& rss, int& shared);
|
||||
void getSystemMemory(int& totalMem, int& availableMem, int& freeMem);
|
||||
vector<float> getInstantCpuUsage();
|
||||
|
||||
private:
|
||||
static UProfileImpl* m_uprofiler;
|
||||
UProfileImpl();
|
||||
|
||||
void write(ProfilingType type, const std::list<std::string>& data);
|
||||
unsigned long long getTimestamp() const;
|
||||
static unsigned long long getEpochTime();
|
||||
static unsigned long long getTimeSinceBoot();
|
||||
|
||||
void dumpCpuUsage();
|
||||
void dumpProcessMemory();
|
||||
void dumpSystemMemory();
|
||||
void dumpGpuUsage();
|
||||
void dumpGpuMemory();
|
||||
|
||||
TimestampUnit m_tsUnit;
|
||||
std::map<std::string, unsigned long long> m_steps; // Store steps (title, start time)
|
||||
std::ofstream m_file;
|
||||
Timer m_processMemoryMonitorTimer;
|
||||
Timer m_systemMemoryMonitorTimer;
|
||||
Timer m_cpuMonitorTimer;
|
||||
Timer m_gpuUsageMonitorTimer;
|
||||
Timer m_gpuMemoryMonitorTimer;
|
||||
CpuMonitor m_cpuMonitor;
|
||||
IGPUMonitor* m_gpuMonitor;
|
||||
|
||||
std::mutex m_fileMutex;
|
||||
std::mutex m_stepsMutex;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* UPROFILEIMPL_H_ */
|
100
3party/cppuprofile/lib/util/cpumonitor.cpp
Normal file
100
3party/cppuprofile/lib/util/cpumonitor.cpp
Normal file
@ -0,0 +1,100 @@
|
||||
// Software Name : cppuprofile
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022 Orange
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//
|
||||
// This software is distributed under the BSD License;
|
||||
// see the LICENSE file for more details.
|
||||
//
|
||||
// Author: Cédric CHEDALEUX <cedric.chedaleux@orange.com> et al.
|
||||
|
||||
#include "cpumonitor.h"
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
using namespace std;
|
||||
|
||||
uprofile::CpuMonitor::CpuMonitor() :
|
||||
m_nbCpus(getNumberOfCPUCores())
|
||||
{
|
||||
m_lastIdleTimes.resize(m_nbCpus, 0);
|
||||
m_lastTotalTimes.resize(m_nbCpus, 0);
|
||||
}
|
||||
|
||||
uprofile::CpuMonitor::~CpuMonitor()
|
||||
{
|
||||
}
|
||||
|
||||
size_t uprofile::CpuMonitor::getNumberOfCPUCores()
|
||||
{
|
||||
size_t nbCores = 0;
|
||||
#if defined(__linux__)
|
||||
ifstream meminfo("/proc/cpuinfo");
|
||||
string str;
|
||||
while (getline(meminfo, str)) {
|
||||
if (str.rfind("processor", 0) == 0) {
|
||||
nbCores++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return nbCores;
|
||||
}
|
||||
|
||||
void uprofile::CpuMonitor::extractCpuTimes(const string& cpuInfo, size_t& idleTime, size_t& totalTime)
|
||||
{
|
||||
// Remove 'cpu<index' word and
|
||||
// extract the idle time and sum all other times
|
||||
size_t spacePos = cpuInfo.find(' ');
|
||||
if (spacePos == string::npos) {
|
||||
return;
|
||||
}
|
||||
|
||||
stringstream times(cpuInfo.substr(spacePos + 1));
|
||||
int index = 0;
|
||||
for (size_t time; times >> time; ++index) {
|
||||
if (index == 3) { // idle time i s the 4th param
|
||||
idleTime = time;
|
||||
}
|
||||
totalTime += time;
|
||||
}
|
||||
}
|
||||
|
||||
vector<float> uprofile::CpuMonitor::getUsage()
|
||||
{
|
||||
vector<float> usages;
|
||||
#if defined(__linux__)
|
||||
ifstream procStat("/proc/stat");
|
||||
// /proc/stat dumps the following info:
|
||||
// user nice system idle iowait irq softirq
|
||||
// cpu 2255 34 2290 22625563 6290 127 456
|
||||
// cpu0 1132 34 1441 11311718 3675 127 438
|
||||
// cpu1 1123 0 849 11313845 2614 0 18
|
||||
// ...
|
||||
// Each numbers represents the amount of time the CPU has spent performing
|
||||
// different kind of work
|
||||
|
||||
for (size_t cpuIndex = 0; cpuIndex < m_nbCpus; ++cpuIndex) {
|
||||
string cpuName("cpu");
|
||||
cpuName += to_string(cpuIndex);
|
||||
|
||||
// Look in /proc/stack the CPU info
|
||||
string line;
|
||||
while (getline(procStat, line)) {
|
||||
if (line.find(cpuName) != std::string::npos) {
|
||||
size_t idleTime = 0, totalTime = 0;
|
||||
extractCpuTimes(line, idleTime, totalTime);
|
||||
|
||||
// To compute CPU load, we compute the time the CPU has been idle since the last read.
|
||||
float cpuLoad = 100.0 * (1.0 - (float)(idleTime - m_lastIdleTimes[cpuIndex]) / (totalTime - m_lastTotalTimes[cpuIndex]));
|
||||
usages.push_back(cpuLoad);
|
||||
|
||||
// Save the times value for the next read
|
||||
m_lastIdleTimes[cpuIndex] = idleTime;
|
||||
m_lastTotalTimes[cpuIndex] = totalTime;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return usages;
|
||||
}
|
41
3party/cppuprofile/lib/util/cpumonitor.h
Normal file
41
3party/cppuprofile/lib/util/cpumonitor.h
Normal file
@ -0,0 +1,41 @@
|
||||
// Software Name : cppuprofile
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022 Orange
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//
|
||||
// This software is distributed under the BSD License;
|
||||
// see the LICENSE file for more details.
|
||||
//
|
||||
// Author: Cédric CHEDALEUX <cedric.chedaleux@orange.com> et al.
|
||||
|
||||
#ifndef CPUMONITOR_H_
|
||||
#define CPUMONITOR_H_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace std;
|
||||
|
||||
namespace uprofile
|
||||
{
|
||||
|
||||
class CpuMonitor
|
||||
{
|
||||
public:
|
||||
explicit CpuMonitor();
|
||||
virtual ~CpuMonitor();
|
||||
|
||||
vector<float> getUsage();
|
||||
|
||||
private:
|
||||
static size_t getNumberOfCPUCores();
|
||||
static void extractCpuTimes(const string& cpuInfo, size_t& idleTime, size_t& totalTime);
|
||||
|
||||
// Store the last idle and total time for each CPU
|
||||
vector<size_t> m_lastIdleTimes;
|
||||
vector<size_t> m_lastTotalTimes;
|
||||
|
||||
size_t m_nbCpus;
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* CPUMONITOR_H_ */
|
64
3party/cppuprofile/lib/util/timer.cpp
Normal file
64
3party/cppuprofile/lib/util/timer.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
// Software Name : cppuprofile
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022 Orange
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//
|
||||
// This software is distributed under the BSD License;
|
||||
// see the LICENSE file for more details.
|
||||
//
|
||||
// Author: Cédric CHEDALEUX <cedric.chedaleux@orange.com> et al.
|
||||
|
||||
#include "timer.h"
|
||||
|
||||
Timer::Timer(int interval) :
|
||||
m_th(NULL),
|
||||
m_interval(interval)
|
||||
{
|
||||
}
|
||||
|
||||
Timer::~Timer()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
void Timer::setInterval(int interval)
|
||||
{
|
||||
m_interval = interval;
|
||||
}
|
||||
|
||||
void Timer::setTimeout(const std::function<void(void)>& timeout)
|
||||
{
|
||||
m_timeout = timeout;
|
||||
}
|
||||
|
||||
bool Timer::isRunning()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_mutex);
|
||||
return m_running;
|
||||
}
|
||||
|
||||
void Timer::start()
|
||||
{
|
||||
if (m_interval > 0 && m_th == NULL) {
|
||||
m_running = true;
|
||||
m_th = new thread([=]() {
|
||||
while (isRunning()) {
|
||||
this_thread::sleep_for(chrono::milliseconds(m_interval));
|
||||
if (m_timeout) {
|
||||
m_timeout();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Timer::stop()
|
||||
{
|
||||
m_mutex.lock();
|
||||
m_running = false;
|
||||
m_mutex.unlock();
|
||||
if (m_th) {
|
||||
m_th->join();
|
||||
delete m_th;
|
||||
m_th = NULL;
|
||||
}
|
||||
}
|
41
3party/cppuprofile/lib/util/timer.h
Normal file
41
3party/cppuprofile/lib/util/timer.h
Normal file
@ -0,0 +1,41 @@
|
||||
// Software Name : cppuprofile
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022 Orange
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//
|
||||
// This software is distributed under the BSD License;
|
||||
// see the LICENSE file for more details.
|
||||
//
|
||||
// Author: Cédric CHEDALEUX <cedric.chedaleux@orange.com> et al.
|
||||
|
||||
#ifndef TIMER_H_
|
||||
#define TIMER_H_
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
using namespace std;
|
||||
|
||||
class Timer
|
||||
{
|
||||
public:
|
||||
explicit Timer(int interval /* ms */ = 0);
|
||||
virtual ~Timer();
|
||||
|
||||
void setTimeout(const function<void(void)>& timeout);
|
||||
void setInterval(int interval);
|
||||
void start();
|
||||
void stop();
|
||||
bool isRunning();
|
||||
|
||||
private:
|
||||
thread* m_th;
|
||||
bool m_running;
|
||||
int m_interval;
|
||||
std::mutex m_mutex;
|
||||
std::function<void(void)> m_timeout;
|
||||
};
|
||||
|
||||
#endif // TIMER_H_
|
3
3party/cppuprofile/requirements.txt
Normal file
3
3party/cppuprofile/requirements.txt
Normal file
@ -0,0 +1,3 @@
|
||||
numpy
|
||||
plotly
|
||||
pandas
|
37
3party/cppuprofile/sample/CMakeLists.txt
Normal file
37
3party/cppuprofile/sample/CMakeLists.txt
Normal file
@ -0,0 +1,37 @@
|
||||
PROJECT(uprof-sample DESCRIPTION "Sample using uprofile library")
|
||||
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12)
|
||||
|
||||
SET( CMAKE_USE_RELATIVE_PATHS ON)
|
||||
|
||||
IF(CMAKE_COMPILER_IS_GNUCXX)
|
||||
ADD_DEFINITIONS( -std=c++0x )
|
||||
ENDIF()
|
||||
|
||||
IF(BUILD_SHARED_LIBS)
|
||||
IF(CMAKE_VERSION VERSION_GREATER_EQUAL 3.12.0)
|
||||
ADD_COMPILE_DEFINITIONS(UPROFILE_DLL)
|
||||
ELSE()
|
||||
ADD_DEFINITIONS(-DUPROFILE_DLL)
|
||||
ENDIF()
|
||||
ENDIF()
|
||||
|
||||
IF(WIN32)
|
||||
add_compile_options(/W4)
|
||||
ELSE()
|
||||
add_compile_options(-Wall -Werror)
|
||||
ENDIF()
|
||||
|
||||
SET(Sample_SRCS
|
||||
main.cpp
|
||||
)
|
||||
|
||||
ADD_EXECUTABLE(${PROJECT_NAME}
|
||||
${Sample_SRCS}
|
||||
)
|
||||
|
||||
# Specify here the libraries this program depends on
|
||||
TARGET_LINK_LIBRARIES(${PROJECT_NAME}
|
||||
cppuprofile
|
||||
)
|
||||
|
||||
INSTALL(TARGETS ${PROJECT_NAME} DESTINATION bin)
|
74
3party/cppuprofile/sample/main.cpp
Normal file
74
3party/cppuprofile/sample/main.cpp
Normal file
@ -0,0 +1,74 @@
|
||||
// Software Name : cppuprofile
|
||||
// SPDX-FileCopyrightText: Copyright (c) 2022 Orange
|
||||
// SPDX-License-Identifier: BSD-3-Clause
|
||||
//
|
||||
// This software is distributed under the BSD License;
|
||||
// see the LICENSE file for more details.
|
||||
//
|
||||
// Author: Cédric CHEDALEUX <cedric.chedaleux@orange.com> et al
|
||||
|
||||
#include <chrono>
|
||||
#include <stdlib.h>
|
||||
#include <thread>
|
||||
#include <uprofile.h>
|
||||
|
||||
void printSystemMemory()
|
||||
{
|
||||
int total = 0, free = 0, available = 0;
|
||||
uprofile::getSystemMemory(total, free, available);
|
||||
printf("Memory: total = %i MB, free = %i MB, available = %i MB\n", total / 1000, free / 1000, available / 1000);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
uprofile::start("./test.log");
|
||||
|
||||
// --- DUMP CPU USAGE ---
|
||||
printf("CPU usage = (");
|
||||
std::vector<float> loads = uprofile::getInstantCpuUsage();
|
||||
for (auto it = loads.cbegin(); it != loads.cend(); ++it) {
|
||||
printf("%0.2f%% ", *it);
|
||||
}
|
||||
printf(")\n");
|
||||
|
||||
// --- START MONITORING ---
|
||||
uprofile::startCPUUsageMonitoring(200);
|
||||
uprofile::startSystemMemoryMonitoring(200);
|
||||
uprofile::startProcessMemoryMonitoring(200);
|
||||
|
||||
// --- USE MEMORY ---
|
||||
printSystemMemory();
|
||||
uprofile::timeBegin("UseMemory");
|
||||
|
||||
int lengthBuff = 100000000;
|
||||
char* buf;
|
||||
printf("Allocating %f MB\n", lengthBuff / sizeof(char) / 1000000.0f);
|
||||
buf = (char*)malloc(lengthBuff + 1);
|
||||
for (int i = 0; i < lengthBuff; i++) {
|
||||
buf[i] = rand() % 26 + 'a';
|
||||
}
|
||||
buf[lengthBuff] = '\0';
|
||||
uprofile::timeEnd("UseMemory");
|
||||
printSystemMemory();
|
||||
|
||||
// --- WAIT 5 SECONDS ---
|
||||
uprofile::timeBegin("Sleep1");
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
uprofile::timeEnd("Sleep1");
|
||||
|
||||
// --- RELEASE MEMORY ---
|
||||
uprofile::timeBegin("FreeMemory");
|
||||
free(buf);
|
||||
uprofile::timeEnd("FreeMemory");
|
||||
printf("Releasing memory\n");
|
||||
printSystemMemory();
|
||||
|
||||
// --- WAIT 5 SECONDS ---
|
||||
uprofile::timeBegin("Sleep2");
|
||||
std::this_thread::sleep_for(std::chrono::seconds(5));
|
||||
uprofile::timeEnd("Sleep2");
|
||||
|
||||
uprofile::stop();
|
||||
|
||||
return 0;
|
||||
}
|
239
3party/cppuprofile/tools/show-graph
Executable file
239
3party/cppuprofile/tools/show-graph
Executable file
@ -0,0 +1,239 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Software Name : uprofile
|
||||
# SPDX-FileCopyrightText: Copyright (c) 2022 Orange
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
#
|
||||
# This software is distributed under the BSD License;
|
||||
# see the LICENSE file for more details.
|
||||
#
|
||||
# Author: Cédric CHEDALEUX <cedric.chedaleux@orange.com> et al.
|
||||
|
||||
|
||||
import argparse
|
||||
|
||||
import pandas as pd
|
||||
import plotly.express as px
|
||||
import plotly.figure_factory as ff
|
||||
import plotly.graph_objects as go
|
||||
from plotly.subplots import make_subplots
|
||||
|
||||
|
||||
METRICS = {
|
||||
'time_exec': 'Execution task',
|
||||
'cpu': 'CPU load',
|
||||
'sys_mem': 'System memory (in MB)',
|
||||
'proc_mem': 'Process Memory (in MB)',
|
||||
'gpu': 'GPU load',
|
||||
'gpu_mem': 'GPU memory (in MB)'
|
||||
}
|
||||
|
||||
|
||||
def read(csv_file):
|
||||
"""
|
||||
Generate a DataFrame from the CSV file
|
||||
Metrics event can have up to 3 extra parameters in addition to its type and its timestamp
|
||||
:param csv_file:
|
||||
:return: Dataframe
|
||||
"""
|
||||
return pd.read_csv(csv_file, sep=';', names=['metric', 'timestamp', 'extra_1', 'extra_2', 'extra_3'])
|
||||
|
||||
|
||||
def filter_dataframe(df, metric):
|
||||
"""
|
||||
Filter the datafram by metric type
|
||||
:param df:
|
||||
:param metric:
|
||||
:return: Dataframe
|
||||
"""
|
||||
return df[df['metric'] == metric]
|
||||
|
||||
|
||||
def gen_time_exec_df(df):
|
||||
"""
|
||||
Format the dataframe to represent time exec data as gant tasks
|
||||
:param df:
|
||||
:return: dataframe with gant tasks
|
||||
"""
|
||||
if df.empty:
|
||||
return None
|
||||
# 'time_exec' format is 'time_exec:<end_timestamp>:<start_timestamp>:<task_name>')
|
||||
time_exec_df = df[['extra_2', 'extra_1', 'timestamp']].copy()
|
||||
time_exec_df.rename(columns={"extra_2": "Task", "extra_1": "Start", "timestamp": "Finish"}, inplace=True)
|
||||
time_exec_df['Description'] = time_exec_df.apply(lambda row: "Task: {} (duration = {} ms)"
|
||||
.format(row['Task'], int(row['Finish']) - int(row['Start'])),
|
||||
axis=1)
|
||||
time_exec_df['Start'] = pd.to_datetime(time_exec_df['Start'], unit='ms')
|
||||
time_exec_df['Finish'] = pd.to_datetime(time_exec_df['Finish'], unit='ms')
|
||||
return time_exec_df
|
||||
|
||||
|
||||
def create_gantt_graph(df):
|
||||
"""
|
||||
Create a gant graph to represent the tasks
|
||||
(see https://chart-studio.plotly.com/~empet/15242/gantt-chart-in-a-subplot-httpscommun/ and https://plotly.com/python/gantt/)
|
||||
:param df:
|
||||
:return: graph
|
||||
"""
|
||||
size = len(df['Start'])
|
||||
colors = px.colors.sample_colorscale("turbo", [n / (size - 1) for n in range(size)])
|
||||
return ff.create_gantt(df, colors=colors, show_colorbar=True, showgrid_x=True, showgrid_y=True,
|
||||
show_hover_fill=True)
|
||||
|
||||
|
||||
def create_cpu_graphs(df):
|
||||
if df.empty:
|
||||
return None
|
||||
# 'cpu' metrics (format is 'cpu:<timestamp>:<cpu_number>:<percentage_usage>')
|
||||
cpus = pd.unique(df['extra_1'])
|
||||
for cpu in cpus:
|
||||
cpu_df = df[df['extra_1'] == cpu]
|
||||
yield go.Scatter(x=pd.to_datetime(cpu_df['timestamp'], unit='ms'),
|
||||
y=pd.to_numeric(cpu_df['extra_2']),
|
||||
name="CPU {}".format(cpu),
|
||||
showlegend=True)
|
||||
|
||||
|
||||
def create_sys_mem_graphs(df):
|
||||
# 'sys_mem' metrics (format is 'mem:<timestamp>:<total>:<available>:<free>')
|
||||
if df.empty:
|
||||
return None
|
||||
|
||||
names = ["Total", "Available", "Free"]
|
||||
for index in range(3):
|
||||
yield go.Scatter(x=pd.to_datetime(df['timestamp'], unit='ms'),
|
||||
y=(pd.to_numeric(df["extra_{}".format(index + 1)], downcast="integer") / 1024),
|
||||
name=names[index],
|
||||
showlegend=True)
|
||||
|
||||
|
||||
def create_proc_mem_graphs(df):
|
||||
# 'proc_mem' metrics (format is 'mem:<timestamp>:<rss>:<shared>')
|
||||
if df.empty:
|
||||
return None
|
||||
|
||||
names = ["RSS", "Shared"]
|
||||
for index in range(2):
|
||||
yield go.Scatter(x=pd.to_datetime(df['timestamp'], unit='ms'),
|
||||
y=(pd.to_numeric(df["extra_{}".format(index + 1)], downcast="integer") / 1024),
|
||||
name=names[index],
|
||||
showlegend=True)
|
||||
|
||||
|
||||
def create_gpu_mem_graphs(df):
|
||||
# 'gpu_mem' metrics (format is 'gpu_mem:<timestamp>:<total>:<used>')
|
||||
if df.empty:
|
||||
return None
|
||||
|
||||
names = ["Used", "Total"]
|
||||
for index in range(2):
|
||||
yield go.Scatter(x=pd.to_datetime(df['timestamp'], unit='ms'),
|
||||
y=(pd.to_numeric(df["extra_{}".format(index + 1)], downcast="integer") / 1024),
|
||||
name=names[index],
|
||||
showlegend=True)
|
||||
|
||||
|
||||
def create_gpu_usage_graphs(df):
|
||||
# 'gpu' metrics (format is 'gpu:<timestamp>:<percentage_usage>')
|
||||
if df.empty:
|
||||
return None
|
||||
|
||||
yield go.Scatter(x=pd.to_datetime(df['timestamp'], unit='ms'),
|
||||
y=pd.to_numeric(df['extra_1']),
|
||||
name="GPU usage",
|
||||
showlegend=True)
|
||||
|
||||
|
||||
def build_graphs(input_file, metrics):
|
||||
|
||||
# Use a multiple subplots (https://plotly.com/python/subplots/) to display
|
||||
# - the execution task graph
|
||||
# - the CPU usage
|
||||
# - the memory usage (system and process)
|
||||
# - the GPU usage and memory
|
||||
graph_titles = []
|
||||
for metric in metrics:
|
||||
graph_titles.append(METRICS[metric])
|
||||
|
||||
figs = make_subplots(rows=len(metrics),
|
||||
cols=1,
|
||||
shared_xaxes=True,
|
||||
vertical_spacing=0.025,
|
||||
subplot_titles=graph_titles
|
||||
)
|
||||
|
||||
with open(input_file) as file:
|
||||
global_df = read(file)
|
||||
|
||||
row_index = 1
|
||||
for metric in metrics:
|
||||
if metric == 'time_exec':
|
||||
# Display a grant graph for representing task execution durations
|
||||
time_exec_df = gen_time_exec_df(filter_dataframe(global_df, metric))
|
||||
if time_exec_df is not None:
|
||||
for trace in create_gantt_graph(time_exec_df).data:
|
||||
figs.add_trace(trace, row=row_index, col=1)
|
||||
elif metric == 'cpu':
|
||||
# Display all CPU usages in the same graph
|
||||
for trace in create_cpu_graphs(filter_dataframe(global_df, metric)):
|
||||
figs.add_trace(trace, row=row_index, col=1)
|
||||
elif metric == 'sys_mem':
|
||||
# Display memory usage
|
||||
for trace in create_sys_mem_graphs(filter_dataframe(global_df, metric)):
|
||||
figs.add_trace(trace, row=row_index, col=1)
|
||||
figs.update_yaxes(row=row_index, col=1, rangemode="tozero")
|
||||
elif metric == 'proc_mem':
|
||||
# Display process memory usage
|
||||
for trace in create_proc_mem_graphs(filter_dataframe(global_df, metric)):
|
||||
figs.add_trace(trace, row=row_index, col=1)
|
||||
figs.update_yaxes(row=row_index, col=1, rangemode="tozero")
|
||||
elif metric == 'gpu':
|
||||
# Display gpu usage
|
||||
for trace in create_gpu_usage_graphs(filter_dataframe(global_df, metric)):
|
||||
figs.add_trace(trace, row=row_index, col=1)
|
||||
figs.update_yaxes(row=row_index, col=1, rangemode="tozero")
|
||||
elif metric == 'gpu_mem':
|
||||
# Display gpu memory
|
||||
for trace in create_gpu_mem_graphs(filter_dataframe(global_df, 'gpu_mem')):
|
||||
figs.add_trace(trace, row=row_index, col=1)
|
||||
figs.update_yaxes(row=row_index, col=1, rangemode="tozero")
|
||||
row_index += 1
|
||||
|
||||
figs.update_layout(
|
||||
height=1200,
|
||||
xaxis_tickformat='%M:%S',
|
||||
showlegend=False
|
||||
)
|
||||
|
||||
return figs
|
||||
|
||||
|
||||
def main():
|
||||
"""
|
||||
Show the duration task graph and the cpu usage graph on a single view
|
||||
The tools reads the metrics file generated by the uprofile library
|
||||
"""
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('INPUT_FILE', type=str, help='Input file that contains profiling data')
|
||||
parser.add_argument('--output', '-o', type=str,
|
||||
help='Save the graph to the given HTML file')
|
||||
parser.add_argument('--metric', type=str, dest='metrics', choices=METRICS.keys(), action='append', default=[],
|
||||
help='Select the metric to display')
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.INPUT_FILE is None:
|
||||
parser.error('no INPUT_FILE given')
|
||||
|
||||
if not args.metrics:
|
||||
args.metrics = METRICS.keys()
|
||||
|
||||
graphs = build_graphs(args.INPUT_FILE, args.metrics)
|
||||
graphs.show()
|
||||
|
||||
if args.output is not None:
|
||||
print("Saving graph to '{}'".format(args.output))
|
||||
graphs.write_html(args.output)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
1043
3party/gperftools/CMakeLists.txt
Normal file
1043
3party/gperftools/CMakeLists.txt
Normal file
File diff suppressed because it is too large
Load Diff
202
3party/gperftools/README
Normal file
202
3party/gperftools/README
Normal file
@ -0,0 +1,202 @@
|
||||
gperftools
|
||||
----------
|
||||
(originally Google Performance Tools)
|
||||
|
||||
The fastest malloc we’ve seen; works particularly well with threads
|
||||
and STL. Also: thread-friendly heap-checker, heap-profiler, and
|
||||
cpu-profiler.
|
||||
|
||||
|
||||
OVERVIEW
|
||||
---------
|
||||
|
||||
gperftools is a collection of a high-performance multi-threaded
|
||||
malloc() implementation, plus some pretty nifty performance analysis
|
||||
tools.
|
||||
|
||||
gperftools is distributed under the terms of the BSD License. Join our
|
||||
mailing list at gperftools@googlegroups.com for updates:
|
||||
https://groups.google.com/forum/#!forum/gperftools
|
||||
|
||||
gperftools was original home for pprof program. But do note that
|
||||
original pprof (which is still included with gperftools) is now
|
||||
deprecated in favor of Go version at https://github.com/google/pprof
|
||||
|
||||
|
||||
TCMALLOC
|
||||
--------
|
||||
Just link in -ltcmalloc or -ltcmalloc_minimal to get the advantages of
|
||||
tcmalloc -- a replacement for malloc and new. See below for some
|
||||
environment variables you can use with tcmalloc, as well.
|
||||
|
||||
tcmalloc functionality is available on all systems we've tested; see
|
||||
INSTALL for more details. See README_windows.txt for instructions on
|
||||
using tcmalloc on Windows.
|
||||
|
||||
when compiling. gcc makes some optimizations assuming it is using its
|
||||
own, built-in malloc; that assumption obviously isn't true with
|
||||
tcmalloc. In practice, we haven't seen any problems with this, but
|
||||
the expected risk is highest for users who register their own malloc
|
||||
hooks with tcmalloc (using gperftools/malloc_hook.h). The risk is
|
||||
lowest for folks who use tcmalloc_minimal (or, of course, who pass in
|
||||
the above flags :-) ).
|
||||
|
||||
|
||||
HEAP PROFILER
|
||||
-------------
|
||||
See docs/heapprofile.html for information about how to use tcmalloc's
|
||||
heap profiler and analyze its output.
|
||||
|
||||
As a quick-start, do the following after installing this package:
|
||||
|
||||
1) Link your executable with -ltcmalloc
|
||||
2) Run your executable with the HEAPPROFILE environment var set:
|
||||
$ HEAPPROFILE=/tmp/heapprof <path/to/binary> [binary args]
|
||||
3) Run pprof to analyze the heap usage
|
||||
$ pprof <path/to/binary> /tmp/heapprof.0045.heap # run 'ls' to see options
|
||||
$ pprof --gv <path/to/binary> /tmp/heapprof.0045.heap
|
||||
|
||||
You can also use LD_PRELOAD to heap-profile an executable that you
|
||||
didn't compile.
|
||||
|
||||
There are other environment variables, besides HEAPPROFILE, you can
|
||||
set to adjust the heap-profiler behavior; c.f. "ENVIRONMENT VARIABLES"
|
||||
below.
|
||||
|
||||
The heap profiler is available on all unix-based systems we've tested;
|
||||
see INSTALL for more details. It is not currently available on Windows.
|
||||
|
||||
|
||||
HEAP CHECKER
|
||||
------------
|
||||
|
||||
Please note that as of gperftools-2.11 this is deprecated. You should
|
||||
consider asan and other sanitizers instead.
|
||||
|
||||
See docs/heap_checker.html for information about how to use tcmalloc's
|
||||
heap checker.
|
||||
|
||||
In order to catch all heap leaks, tcmalloc must be linked *last* into
|
||||
your executable. The heap checker may mischaracterize some memory
|
||||
accesses in libraries listed after it on the link line. For instance,
|
||||
it may report these libraries as leaking memory when they're not.
|
||||
(See the source code for more details.)
|
||||
|
||||
Here's a quick-start for how to use:
|
||||
|
||||
As a quick-start, do the following after installing this package:
|
||||
|
||||
1) Link your executable with -ltcmalloc
|
||||
2) Run your executable with the HEAPCHECK environment var set:
|
||||
$ HEAPCHECK=1 <path/to/binary> [binary args]
|
||||
|
||||
Other values for HEAPCHECK: normal (equivalent to "1"), strict, draconian
|
||||
|
||||
You can also use LD_PRELOAD to heap-check an executable that you
|
||||
didn't compile.
|
||||
|
||||
The heap checker is only available on Linux at this time; see INSTALL
|
||||
for more details.
|
||||
|
||||
|
||||
CPU PROFILER
|
||||
------------
|
||||
See docs/cpuprofile.html for information about how to use the CPU
|
||||
profiler and analyze its output.
|
||||
|
||||
As a quick-start, do the following after installing this package:
|
||||
|
||||
1) Link your executable with -lprofiler
|
||||
2) Run your executable with the CPUPROFILE environment var set:
|
||||
$ CPUPROFILE=/tmp/prof.out <path/to/binary> [binary args]
|
||||
3) Run pprof to analyze the CPU usage
|
||||
$ pprof <path/to/binary> /tmp/prof.out # -pg-like text output
|
||||
$ pprof --gv <path/to/binary> /tmp/prof.out # really cool graphical output
|
||||
|
||||
There are other environment variables, besides CPUPROFILE, you can set
|
||||
to adjust the cpu-profiler behavior; cf "ENVIRONMENT VARIABLES" below.
|
||||
|
||||
The CPU profiler is available on all unix-based systems we've tested;
|
||||
see INSTALL for more details. It is not currently available on Windows.
|
||||
|
||||
NOTE: CPU profiling doesn't work after fork (unless you immediately
|
||||
do an exec()-like call afterwards). Furthermore, if you do
|
||||
fork, and the child calls exit(), it may corrupt the profile
|
||||
data. You can use _exit() to work around this. We hope to have
|
||||
a fix for both problems in the next release of perftools
|
||||
(hopefully perftools 1.2).
|
||||
|
||||
|
||||
EVERYTHING IN ONE
|
||||
-----------------
|
||||
If you want the CPU profiler, heap profiler, and heap leak-checker to
|
||||
all be available for your application, you can do:
|
||||
gcc -o myapp ... -lprofiler -ltcmalloc
|
||||
|
||||
However, if you have a reason to use the static versions of the
|
||||
library, this two-library linking won't work:
|
||||
gcc -o myapp ... /usr/lib/libprofiler.a /usr/lib/libtcmalloc.a # errors!
|
||||
|
||||
Instead, use the special libtcmalloc_and_profiler library, which we
|
||||
make for just this purpose:
|
||||
gcc -o myapp ... /usr/lib/libtcmalloc_and_profiler.a
|
||||
|
||||
|
||||
CONFIGURATION OPTIONS
|
||||
---------------------
|
||||
For advanced users, there are several flags you can pass to
|
||||
'./configure' that tweak tcmalloc performance. (These are in addition
|
||||
to the environment variables you can set at runtime to affect
|
||||
tcmalloc, described below.) See the INSTALL file for details.
|
||||
|
||||
|
||||
ENVIRONMENT VARIABLES
|
||||
---------------------
|
||||
The cpu profiler, heap checker, and heap profiler will lie dormant,
|
||||
using no memory or CPU, until you turn them on. (Thus, there's no
|
||||
harm in linking -lprofiler into every application, and also -ltcmalloc
|
||||
assuming you're ok using the non-libc malloc library.)
|
||||
|
||||
The easiest way to turn them on is by setting the appropriate
|
||||
environment variables. We have several variables that let you
|
||||
enable/disable features as well as tweak parameters.
|
||||
|
||||
Here are some of the most important variables:
|
||||
|
||||
HEAPPROFILE=<pre> -- turns on heap profiling and dumps data using this prefix
|
||||
HEAPCHECK=<type> -- turns on heap checking with strictness 'type'
|
||||
CPUPROFILE=<file> -- turns on cpu profiling and dumps data to this file.
|
||||
PROFILESELECTED=1 -- if set, cpu-profiler will only profile regions of code
|
||||
surrounded with ProfilerEnable()/ProfilerDisable().
|
||||
CPUPROFILE_FREQUENCY=x-- how many interrupts/second the cpu-profiler samples.
|
||||
|
||||
PERFTOOLS_VERBOSE=<level> -- the higher level, the more messages malloc emits
|
||||
MALLOCSTATS=<level> -- prints memory-use stats at program-exit
|
||||
|
||||
For a full list of variables, see the documentation pages:
|
||||
docs/cpuprofile.html
|
||||
docs/heapprofile.html
|
||||
docs/heap_checker.html
|
||||
|
||||
See also TCMALLOC_STACKTRACE_METHOD_VERBOSE and
|
||||
TCMALLOC_STACKTRACE_METHOD environment variables briefly documented in
|
||||
our INSTALL file and on our wiki page at:
|
||||
https://github.com/gperftools/gperftools/wiki/gperftools'-stacktrace-capturing-methods-and-their-issues
|
||||
|
||||
|
||||
COMPILING ON NON-LINUX SYSTEMS
|
||||
------------------------------
|
||||
|
||||
Perftools was developed and tested on x86, aarch64 and riscv Linux
|
||||
systems, and it works in its full generality only on those systems.
|
||||
|
||||
However, we've successfully ported much of the tcmalloc library to
|
||||
FreeBSD, Solaris x86 (not tested recently though), and Mac OS X
|
||||
(aarch64; x86 and ppc have not been tested recently); and we've ported
|
||||
the basic functionality in tcmalloc_minimal to Windows. See INSTALL
|
||||
for details. See README_windows.txt for details on the Windows port.
|
||||
|
||||
|
||||
---
|
||||
Originally written: 17 May 2011
|
||||
Last refreshed: 10 Aug 2023
|
22
3party/gperftools/cmake/DefineTargetVariables.cmake
Normal file
22
3party/gperftools/cmake/DefineTargetVariables.cmake
Normal file
@ -0,0 +1,22 @@
|
||||
if(NOT COMMAND check_cxx_source_compiles)
|
||||
include(CheckCXXSourceCompiles)
|
||||
endif()
|
||||
|
||||
macro(define_target_variables)
|
||||
check_cxx_source_compiles("int main() { return __i386__; }" i386)
|
||||
check_cxx_source_compiles("int main() { return __x86_64__; }" x86_64)
|
||||
check_cxx_source_compiles("int main() { return __s390__; }" s390)
|
||||
if(APPLE)
|
||||
check_cxx_source_compiles("int main() { return __arm64__; }" ARM)
|
||||
check_cxx_source_compiles("int main() { return __ppc64__; }" PPC64)
|
||||
check_cxx_source_compiles("int main() { return __ppc__; }" PPC)
|
||||
else()
|
||||
check_cxx_source_compiles("int main() { return __arm__; }" ARM)
|
||||
check_cxx_source_compiles("int main() { return __PPC64__; }" PPC64)
|
||||
check_cxx_source_compiles("int main() { return __PPC__; }" PPC)
|
||||
endif()
|
||||
check_cxx_source_compiles("int main() { return __FreeBSD__; }" FreeBSD)
|
||||
check_cxx_source_compiles("int main() { return __MINGW__; }" MINGW)
|
||||
check_cxx_source_compiles("int main() { return __linux; }" LINUX)
|
||||
check_cxx_source_compiles("int main() { return __APPLE__; }" OSX)
|
||||
endmacro()
|
275
3party/gperftools/cmake/config.h.in
Normal file
275
3party/gperftools/cmake/config.h.in
Normal file
@ -0,0 +1,275 @@
|
||||
/* Sometimes we accidentally #include this config.h instead of the one
|
||||
in .. -- this is particularly true for msys/mingw, which uses the
|
||||
unix config.h but also runs code in the windows directory.
|
||||
*/
|
||||
#ifdef __MINGW32__
|
||||
#include "../config.h"
|
||||
#define GOOGLE_PERFTOOLS_WINDOWS_CONFIG_H_
|
||||
#endif
|
||||
|
||||
#ifndef GOOGLE_PERFTOOLS_WINDOWS_CONFIG_H_
|
||||
#define GOOGLE_PERFTOOLS_WINDOWS_CONFIG_H_
|
||||
/* used by tcmalloc.h */
|
||||
#define GPERFTOOLS_CONFIG_H_
|
||||
|
||||
/* Enable aggressive decommit by default */
|
||||
#cmakedefine ENABLE_AGGRESSIVE_DECOMMIT_BY_DEFAULT
|
||||
|
||||
/* Build new/delete operators for overaligned types */
|
||||
#cmakedefine ENABLE_ALIGNED_NEW_DELETE
|
||||
|
||||
/* Build runtime detection for sized delete */
|
||||
#cmakedefine ENABLE_DYNAMIC_SIZED_DELETE
|
||||
|
||||
/* Report large allocation */
|
||||
#cmakedefine ENABLE_LARGE_ALLOC_REPORT
|
||||
|
||||
/* Build sized deletion operators */
|
||||
#cmakedefine ENABLE_SIZED_DELETE
|
||||
|
||||
/* Define to 1 if you have the <asm/ptrace.h> header file. */
|
||||
#cmakedefine HAVE_ASM_PTRACE_H
|
||||
|
||||
/* Define to 1 if you have the <cygwin/signal.h> header file. */
|
||||
#cmakedefine HAVE_CYGWIN_SIGNAL_H
|
||||
|
||||
/* Define to 1 if you have the declaration of `backtrace', and to 0 if you
|
||||
don't. */
|
||||
#cmakedefine01 HAVE_DECL_BACKTRACE
|
||||
|
||||
/* Define to 1 if you have the declaration of `cfree', and to 0 if you don't.
|
||||
*/
|
||||
#cmakedefine01 HAVE_DECL_CFREE
|
||||
|
||||
/* Define to 1 if you have the declaration of `memalign', and to 0 if you
|
||||
don't. */
|
||||
#cmakedefine01 HAVE_DECL_MEMALIGN
|
||||
|
||||
/* Define to 1 if you have the declaration of `nanosleep', and to 0 if you
|
||||
don't. */
|
||||
#cmakedefine01 HAVE_DECL_NANOSLEEP
|
||||
|
||||
/* Define to 1 if you have the declaration of `posix_memalign', and to 0 if
|
||||
you don't. */
|
||||
#cmakedefine01 HAVE_DECL_POSIX_MEMALIGN
|
||||
|
||||
/* Define to 1 if you have the declaration of `pvalloc', and to 0 if you
|
||||
don't. */
|
||||
#cmakedefine01 HAVE_DECL_PVALLOC
|
||||
|
||||
/* Define to 1 if you have the declaration of `sleep', and to 0 if you don't.
|
||||
*/
|
||||
#cmakedefine01 HAVE_DECL_SLEEP
|
||||
|
||||
/* Define to 1 if you have the declaration of `valloc', and to 0 if you don't.
|
||||
*/
|
||||
#cmakedefine01 HAVE_DECL_VALLOC
|
||||
|
||||
/* Define to 1 if you have the <execinfo.h> header file. */
|
||||
#cmakedefine HAVE_EXECINFO_H
|
||||
|
||||
/* Define to 1 if you have the <fcntl.h> header file. */
|
||||
#cmakedefine HAVE_FCNTL_H
|
||||
|
||||
/* Define to 1 if you have the <features.h> header file. */
|
||||
#cmakedefine HAVE_FEATURES_H
|
||||
|
||||
/* Define to 1 if you have the `fork' function. */
|
||||
#cmakedefine HAVE_FORK
|
||||
|
||||
/* Define to 1 if you have the `geteuid' function. */
|
||||
#cmakedefine HAVE_GETEUID
|
||||
|
||||
/* Define to 1 if you have the <glob.h> header file. */
|
||||
#cmakedefine HAVE_GLOB_H
|
||||
|
||||
/* Define to 1 if you have the <grp.h> header file. */
|
||||
#cmakedefine HAVE_GRP_H
|
||||
|
||||
/* Define to 1 if you have the <libunwind.h> header file. */
|
||||
#cmakedefine01 HAVE_LIBUNWIND_H
|
||||
|
||||
#cmakedefine USE_LIBUNWIND
|
||||
|
||||
/* Define if this is Linux that has SIGEV_THREAD_ID */
|
||||
#cmakedefine01 HAVE_LINUX_SIGEV_THREAD_ID
|
||||
|
||||
/* Define to 1 if you have the <malloc.h> header file. */
|
||||
#cmakedefine HAVE_MALLOC_H
|
||||
|
||||
/* Define to 1 if you have the <malloc/malloc.h> header file. */
|
||||
#cmakedefine HAVE_MALLOC_MALLOC_H
|
||||
|
||||
/* Define to 1 if you have a working `mmap' system call. */
|
||||
#cmakedefine HAVE_MMAP
|
||||
|
||||
/* Define to 1 if you have the <poll.h> header file. */
|
||||
#cmakedefine HAVE_POLL_H
|
||||
|
||||
/* define if libc has program_invocation_name */
|
||||
#cmakedefine HAVE_PROGRAM_INVOCATION_NAME
|
||||
|
||||
/* Define if you have POSIX threads libraries and header files. */
|
||||
#cmakedefine HAVE_PTHREAD
|
||||
|
||||
/* defined to 1 if pthread symbols are exposed even without include pthread.h
|
||||
*/
|
||||
#cmakedefine HAVE_PTHREAD_DESPITE_ASKING_FOR
|
||||
|
||||
/* Define to 1 if you have the <pwd.h> header file. */
|
||||
#cmakedefine HAVE_PWD_H
|
||||
|
||||
/* Define to 1 if you have the `sbrk' function. */
|
||||
#cmakedefine HAVE_SBRK
|
||||
|
||||
/* Define to 1 if you have the <sched.h> header file. */
|
||||
#cmakedefine HAVE_SCHED_H
|
||||
|
||||
/* Define to 1 if the system has the type `struct mallinfo'. */
|
||||
#cmakedefine HAVE_STRUCT_MALLINFO
|
||||
|
||||
/* Define to 1 if the system has the type `struct mallinfo2'. */
|
||||
#cmakedefine HAVE_STRUCT_MALLINFO2
|
||||
|
||||
/* Define to 1 if you have the <sys/cdefs.h> header file. */
|
||||
#cmakedefine HAVE_SYS_CDEFS_H
|
||||
|
||||
/* Define to 1 if you have the <sys/malloc.h> header file. */
|
||||
#cmakedefine HAVE_SYS_MALLOC_H
|
||||
|
||||
/* Define to 1 if you have the <sys/resource.h> header file. */
|
||||
#cmakedefine HAVE_SYS_RESOURCE_H
|
||||
|
||||
/* Define to 1 if you have the <sys/socket.h> header file. */
|
||||
#cmakedefine HAVE_SYS_SOCKET_H
|
||||
|
||||
/* Define to 1 if you have the <sys/syscall.h> header file. */
|
||||
#cmakedefine01 HAVE_SYS_SYSCALL_H
|
||||
|
||||
/* Define to 1 if you have the <sys/types.h> header file. */
|
||||
#cmakedefine HAVE_SYS_TYPES_H
|
||||
|
||||
/* Define to 1 if you have the <sys/ucontext.h> header file. */
|
||||
#cmakedefine01 HAVE_SYS_UCONTEXT_H
|
||||
|
||||
/* Define to 1 if you have the <sys/wait.h> header file. */
|
||||
#cmakedefine HAVE_SYS_WAIT_H
|
||||
|
||||
/* Define to 1 if compiler supports __thread */
|
||||
#cmakedefine HAVE_TLS
|
||||
|
||||
/* Define to 1 if you have the <ucontext.h> header file. */
|
||||
#cmakedefine01 HAVE_UCONTEXT_H
|
||||
|
||||
/* Define to 1 if you have the <unistd.h> header file. */
|
||||
#cmakedefine HAVE_UNISTD_H
|
||||
|
||||
/* Whether <unwind.h> contains _Unwind_Backtrace */
|
||||
#cmakedefine HAVE_UNWIND_BACKTRACE
|
||||
|
||||
/* Define to 1 if you have the <unwind.h> header file. */
|
||||
#cmakedefine HAVE_UNWIND_H
|
||||
|
||||
/* define if your compiler has __attribute__ */
|
||||
#cmakedefine HAVE___ATTRIBUTE__
|
||||
|
||||
/* define if your compiler supports alignment of functions */
|
||||
#cmakedefine HAVE___ATTRIBUTE__ALIGNED_FN
|
||||
|
||||
/* Define to 1 if compiler supports __environ */
|
||||
#cmakedefine HAVE___ENVIRON
|
||||
|
||||
/* Define to 1 if you have the `__sbrk' function. */
|
||||
#cmakedefine01 HAVE___SBRK
|
||||
|
||||
/* prefix where we look for installed files */
|
||||
#cmakedefine INSTALL_PREFIX
|
||||
|
||||
/* Define to the sub-directory where libtool stores uninstalled libraries. */
|
||||
#cmakedefine LT_OBJDIR
|
||||
|
||||
/* Name of package */
|
||||
#define PACKAGE "@PROJECT_NAME@"
|
||||
|
||||
/* Define to the address where bug reports for this package should be sent. */
|
||||
#define PACKAGE_BUGREPORT "gperftools@googlegroups.com"
|
||||
|
||||
/* Define to the full name of this package. */
|
||||
#define PACKAGE_NAME "@PROJECT_NAME@"
|
||||
|
||||
/* Define to the full name and version of this package. */
|
||||
#define PACKAGE_STRING "@PROJECT_NAME@ @PROJECT_VERSION@"
|
||||
|
||||
/* Define to the one symbol short name of this package. */
|
||||
#define PACKAGE_TARNAME "@PROJECT_NAME@"
|
||||
|
||||
/* Define to the home page for this package. */
|
||||
#cmakedefine PACKAGE_URL
|
||||
|
||||
/* Define to the version of this package. */
|
||||
#define PACKAGE_VERSION "@PROJECT_VERSION@"
|
||||
|
||||
/* 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. */
|
||||
#ifndef WIN32
|
||||
#cmakedefine WIN32
|
||||
#endif
|
||||
#if defined(WIN32)
|
||||
#ifndef PERFTOOLS_DLL_DECL
|
||||
# define PERFTOOLS_IS_A_DLL 1
|
||||
# define PERFTOOLS_DLL_DECL __declspec(dllexport)
|
||||
# define PERFTOOLS_DLL_DECL_FOR_UNITTESTS __declspec(dllimport)
|
||||
#endif
|
||||
#else
|
||||
#ifndef PERFTOOLS_DLL_DECL
|
||||
# define PERFTOOLS_DLL_DECL
|
||||
# define PERFTOOLS_DLL_DECL_FOR_UNITTESTS
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* if libgcc stacktrace method should be default */
|
||||
#cmakedefine PREFER_LIBGCC_UNWINDER
|
||||
|
||||
/* 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 8 bytes of allocation alignment for tcmalloc */
|
||||
#cmakedefine TCMALLOC_ALIGN_8BYTES
|
||||
|
||||
/* Define internal page size for tcmalloc as number of left bitshift */
|
||||
#cmakedefine TCMALLOC_PAGE_SIZE_SHIFT @TCMALLOC_PAGE_SIZE_SHIFT@
|
||||
|
||||
/* Version number of package */
|
||||
#define VERSION @PROJECT_VERSION@
|
||||
|
||||
/* C99 says: define this to get the PRI... macros from stdint.h */
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
# define __STDC_FORMAT_MACROS 1
|
||||
#endif
|
||||
|
||||
// ---------------------------------------------------------------------
|
||||
// Extra stuff not found in config.h.in
|
||||
#if defined(WIN32)
|
||||
|
||||
// This must be defined before the windows.h is included. We need at
|
||||
// least 0x0400 for mutex.h to have access to TryLock, and at least
|
||||
// 0x0501 for patch_functions.cc to have access to GetModuleHandleEx.
|
||||
// (This latter is an optimization we could take out if need be.)
|
||||
#ifndef _WIN32_WINNT
|
||||
# define _WIN32_WINNT 0x0501
|
||||
#endif
|
||||
|
||||
// We want to make sure not to ever try to #include heap-checker.h
|
||||
#define NO_HEAP_CHECK 1
|
||||
|
||||
// TODO(csilvers): include windows/port.h in every relevant source file instead?
|
||||
#include "windows/port.h"
|
||||
|
||||
#endif
|
||||
#endif /* GOOGLE_PERFTOOLS_WINDOWS_CONFIG_H_ */
|
14
3party/gperftools/cmake/pkgconfig.pc
Normal file
14
3party/gperftools/cmake/pkgconfig.pc
Normal file
@ -0,0 +1,14 @@
|
||||
prefix=@CMAKE_INSTALL_PREFIX@
|
||||
exec_prefix=${prefix}
|
||||
libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@
|
||||
includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@
|
||||
|
||||
Name: @CMAKE_PROJECT_NAME@
|
||||
Version: @CMAKE_PROJECT_VERSION@
|
||||
Description: @CMAKE_PROJECT_DESCRIPTION@
|
||||
URL: @CMAKE_PROJECT_HOMEPAGE_URL@
|
||||
Requires:
|
||||
Libs: -L${libdir} -l@NAME@
|
||||
Libs.private:@PTHREAD_FLAGS@
|
||||
Cflags: -I${includedir}
|
||||
|
166
3party/gperftools/cmake/tcmalloc.h.in
Normal file
166
3party/gperftools/cmake/tcmalloc.h.in
Normal file
@ -0,0 +1,166 @@
|
||||
// -*- 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 @PROJECT_VERSION_MAJOR@
|
||||
#define TC_VERSION_MINOR @PROJECT_VERSION_MINOR@
|
||||
#define TC_VERSION_PATCH ".@PROJECT_VERSION_PATCH@"
|
||||
#define TC_VERSION_STRING "gperftools @PROJECT_VERSION@"
|
||||
|
||||
/* For struct mallinfo, if it's defined. */
|
||||
#if @HAVE_STRUCT_MALLINFO@ || @HAVE_STRUCT_MALLINFO2@
|
||||
# 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 @HAVE_STRUCT_MALLINFO@
|
||||
PERFTOOLS_DLL_DECL struct mallinfo tc_mallinfo(void) PERFTOOLS_NOTHROW;
|
||||
#endif
|
||||
#if @HAVE_STRUCT_MALLINFO2@
|
||||
PERFTOOLS_DLL_DECL struct mallinfo2 tc_mallinfo2(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 @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_ */
|
417
3party/gperftools/src/addressmap-inl.h
Normal file
417
3party/gperftools/src/addressmap-inl.h
Normal file
@ -0,0 +1,417 @@
|
||||
// -*- 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>
|
||||
#include <stdint.h> // to get uint16_t (ISO naming madness)
|
||||
#include <inttypes.h> // another place uint16_t might be defined
|
||||
|
||||
// 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_
|
439
3party/gperftools/src/base/basictypes.h
Normal file
439
3party/gperftools/src/base/basictypes.h
Normal file
@ -0,0 +1,439 @@
|
||||
// -*- 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()
|
||||
#include <inttypes.h> // gets us PRId64, etc
|
||||
|
||||
// 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 */
|
||||
|
||||
#include <stdint.h> // to get uint16_t (ISO naming madness)
|
||||
#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)
|
||||
#elif defined(__QNXNTO__)
|
||||
#define PRINTABLE_PTHREAD(pthreadt) static_cast<intptr_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
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#undef ATTRIBUTE_NOINLINE
|
||||
#define ATTRIBUTE_NOINLINE __declspec(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))) __attribute__((noinline))
|
||||
|
||||
#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
|
||||
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__) || 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(__loongarch64)
|
||||
# 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_
|
175
3party/gperftools/src/base/commandlineflags.h
Normal file
175
3party/gperftools/src/base/commandlineflags.h
Normal 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_
|
60
3party/gperftools/src/base/dynamic_annotations.cc
Normal file
60
3party/gperftools/src/base/dynamic_annotations.cc
Normal file
@ -0,0 +1,60 @@
|
||||
// -*- 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
|
||||
*/
|
||||
|
||||
#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;
|
||||
}
|
86
3party/gperftools/src/base/dynamic_annotations.h
Normal file
86
3party/gperftools/src/base/dynamic_annotations.h
Normal 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_ */
|
434
3party/gperftools/src/base/elf_mem_image.cc
Normal file
434
3party/gperftools/src/base/elf_mem_image.cc
Normal 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
|
135
3party/gperftools/src/base/elf_mem_image.h
Normal file
135
3party/gperftools/src/base/elf_mem_image.h
Normal 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_
|
74
3party/gperftools/src/base/googleinit.h
Normal file
74
3party/gperftools/src/base/googleinit.h
Normal 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 */
|
727
3party/gperftools/src/base/linuxthreads.cc
Normal file
727
3party/gperftools/src/base/linuxthreads.cc
Normal file
@ -0,0 +1,727 @@
|
||||
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
|
||||
/* Copyright (c) 2005-2007, Google Inc.
|
||||
* Copyright (c) 2023, 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.
|
||||
*
|
||||
* ---
|
||||
* Author: Markus Gutschke
|
||||
*
|
||||
* Substantial upgrades by Aliaksey Kandratsenka. All bugs are mine.
|
||||
*/
|
||||
#ifndef _GNU_SOURCE
|
||||
#define _GNU_SOURCE
|
||||
#endif
|
||||
|
||||
#include "base/linuxthreads.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <sched.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "base/logging.h"
|
||||
|
||||
#ifndef CLONE_UNTRACED
|
||||
#define CLONE_UNTRACED 0x00800000
|
||||
#endif
|
||||
|
||||
#ifndef PR_SET_PTRACER
|
||||
#define PR_SET_PTRACER 0x59616d61
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
class SetPTracerSetup {
|
||||
public:
|
||||
~SetPTracerSetup() {
|
||||
if (need_cleanup_) {
|
||||
prctl(PR_SET_PTRACER, 0, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
void Prepare(int clone_pid) {
|
||||
if (prctl(PR_SET_PTRACER, clone_pid, 0, 0, 0) == 0) {
|
||||
need_cleanup_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool need_cleanup_ = false;
|
||||
};
|
||||
|
||||
class UniqueFD {
|
||||
public:
|
||||
explicit UniqueFD(int fd) : fd_(fd) {}
|
||||
|
||||
int ReleaseFD() {
|
||||
int retval = fd_;
|
||||
fd_ = -1;
|
||||
return retval;
|
||||
}
|
||||
|
||||
~UniqueFD() {
|
||||
if (fd_ < 0) {
|
||||
return;
|
||||
}
|
||||
(void)close(fd_);
|
||||
}
|
||||
private:
|
||||
int fd_;
|
||||
};
|
||||
|
||||
template <typename Body>
|
||||
struct SimpleCleanup {
|
||||
const Body body;
|
||||
|
||||
explicit SimpleCleanup(const Body& body) : body(body) {}
|
||||
|
||||
~SimpleCleanup() {
|
||||
body();
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Body>
|
||||
SimpleCleanup<Body> MakeSimpleCleanup(const Body& body) {
|
||||
return SimpleCleanup<Body>{body};
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
/* Synchronous signals that should not be blocked while in the lister thread.
|
||||
*/
|
||||
static const int sync_signals[] = {
|
||||
SIGABRT, SIGILL,
|
||||
SIGFPE, SIGSEGV, SIGBUS,
|
||||
#ifdef SIGEMT
|
||||
SIGEMT,
|
||||
#endif
|
||||
SIGSYS, SIGTRAP,
|
||||
SIGXCPU, SIGXFSZ };
|
||||
|
||||
ATTRIBUTE_NOINLINE
|
||||
static int local_clone (int (*fn)(void *), void *arg) {
|
||||
#ifdef __PPC64__
|
||||
/* To avoid the gap cross page boundaries, increase by the large parge
|
||||
* size mostly PowerPC system uses. */
|
||||
|
||||
// FIXME(alk): I don't really understand why ppc needs this and why
|
||||
// 64k pages matter. I.e. some other architectures have 64k pages,
|
||||
// so should we do the same there?
|
||||
uintptr_t clone_stack_size = 64 << 10;
|
||||
#else
|
||||
uintptr_t clone_stack_size = 4 << 10;
|
||||
#endif
|
||||
|
||||
bool grows_to_low = (&arg < arg);
|
||||
if (grows_to_low) {
|
||||
// Negate clone_stack_size if stack grows to lower addresses
|
||||
// (common for arch-es that matter).
|
||||
clone_stack_size = ~clone_stack_size + 1;
|
||||
}
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__) || defined(__riscv) || defined(__arm__) || defined(__aarch64__)
|
||||
// Sanity check code above. We know that those arch-es grow stack to
|
||||
// lower addresses.
|
||||
CHECK(grows_to_low);
|
||||
#endif
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
uintptr_t stack_addr = reinterpret_cast<uintptr_t>(&arg) + clone_stack_size;
|
||||
stack_addr &= ~63; // align stack address on 64 bytes (x86 needs 16, but lets be generous)
|
||||
return clone(fn, reinterpret_cast<void*>(stack_addr),
|
||||
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;
|
||||
}
|
||||
|
||||
static int ptrace_detach(pid_t pid) {
|
||||
return ptrace(PTRACE_DETACH, pid, nullptr, nullptr);
|
||||
}
|
||||
|
||||
/* Re-runs fn until it doesn't cause EINTR
|
||||
*/
|
||||
#define NO_INTR(fn) do {} while ((fn) < 0 && errno == EINTR)
|
||||
|
||||
/* 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;
|
||||
|
||||
|
||||
/* 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) {
|
||||
RAW_LOG(ERROR, "Got fatal signal %d inside ListerThread", signum);
|
||||
|
||||
if (sig_pids != NULL) {
|
||||
if (signum == SIGABRT) {
|
||||
prctl(PR_SET_PDEATHSIG, 0);
|
||||
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 ptrace_detach(). */
|
||||
sched_yield();
|
||||
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;
|
||||
|
||||
syscall(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);
|
||||
read(-1, buf, amount);
|
||||
}
|
||||
|
||||
|
||||
/* Data structure for passing arguments to the lister thread.
|
||||
*/
|
||||
#define ALT_STACKSIZE (MINSIGSTKSZ + 4096)
|
||||
|
||||
struct ListerParams {
|
||||
int result, err;
|
||||
pid_t ppid;
|
||||
int start_pipe_rd;
|
||||
int start_pipe_wr;
|
||||
char *altstack_mem;
|
||||
ListAllProcessThreadsCallBack callback;
|
||||
void *parameter;
|
||||
va_list ap;
|
||||
int proc_fd;
|
||||
};
|
||||
|
||||
struct kernel_dirent64 { // see man 2 getdents
|
||||
int64_t d_ino; /* 64-bit inode number */
|
||||
int64_t d_off; /* 64-bit offset to next structure */
|
||||
unsigned short d_reclen; /* Size of this dirent */
|
||||
unsigned char d_type; /* File type */
|
||||
char d_name[]; /* Filename (null-terminated) */
|
||||
};
|
||||
|
||||
static const kernel_dirent64 *BumpDirentPtr(const kernel_dirent64 *ptr, uintptr_t by_bytes) {
|
||||
return reinterpret_cast<kernel_dirent64*>(reinterpret_cast<uintptr_t>(ptr) + by_bytes);
|
||||
}
|
||||
|
||||
static int ListerThread(struct ListerParams *args) {
|
||||
int found_parent = 0;
|
||||
pid_t clone_pid = syscall(SYS_gettid);
|
||||
int proc = args->proc_fd, num_threads = 0;
|
||||
int max_threads = 0, sig;
|
||||
struct stat proc_sb;
|
||||
stack_t altstack;
|
||||
|
||||
/* Wait for parent thread to set appropriate permissions to allow
|
||||
* ptrace activity. Note we using pipe pair, so which ensures we
|
||||
* don't sleep past parent's death.
|
||||
*/
|
||||
(void)close(args->start_pipe_wr);
|
||||
{
|
||||
char tmp;
|
||||
read(args->start_pipe_rd, &tmp, sizeof(tmp));
|
||||
}
|
||||
|
||||
// No point in continuing if parent dies before/during ptracing.
|
||||
prctl(PR_SET_PDEATHSIG, SIGKILL);
|
||||
|
||||
/* 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;
|
||||
sigaltstack(&altstack, nullptr);
|
||||
|
||||
/* 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.
|
||||
*/
|
||||
for (sig = 0; sig < sizeof(sync_signals)/sizeof(*sync_signals); sig++) {
|
||||
struct sigaction sa;
|
||||
memset(&sa, 0, sizeof(sa));
|
||||
sa.sa_sigaction = SignalHandler;
|
||||
sigfillset(&sa.sa_mask);
|
||||
sa.sa_flags = SA_ONSTACK|SA_SIGINFO|SA_RESETHAND;
|
||||
sigaction(sync_signals[sig], &sa, nullptr);
|
||||
}
|
||||
|
||||
/* Read process directories in /proc/... */
|
||||
for (;;) {
|
||||
if (lseek(proc, 0, SEEK_SET) < 0) {
|
||||
goto failure;
|
||||
}
|
||||
if (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 (;;) {
|
||||
// lets make sure to align buf to store kernel_dirent64-s properly.
|
||||
int64_t buf[4096 / sizeof(int64_t)];
|
||||
|
||||
ssize_t nbytes = syscall(SYS_getdents64, proc, buf, sizeof(buf));
|
||||
// fprintf(stderr, "nbytes = %zd\n", nbytes);
|
||||
|
||||
if (nbytes < 0) {
|
||||
goto failure;
|
||||
}
|
||||
|
||||
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;
|
||||
lseek(proc, 0, SEEK_SET);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
const kernel_dirent64 *entry = reinterpret_cast<kernel_dirent64*>(buf);
|
||||
const kernel_dirent64 *end = BumpDirentPtr(entry, nbytes);
|
||||
|
||||
for (;entry < end; entry = BumpDirentPtr(entry, entry->d_reclen)) {
|
||||
if (entry->d_ino == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const char *ptr = entry->d_name;
|
||||
// fprintf(stderr, "name: %s\n", ptr);
|
||||
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);
|
||||
// fprintf(stderr, "pid = %d (%d)\n", pid, getpid());
|
||||
|
||||
if (!pid || pid == clone_pid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Attach (and suspend) all threads */
|
||||
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 */
|
||||
goto detach_threads;
|
||||
}
|
||||
|
||||
/* Attaching to thread suspends it */
|
||||
pids[num_threads++] = pid;
|
||||
sig_num_threads = num_threads;
|
||||
|
||||
if (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 (waitpid(pid, (int *)0, __WALL) < 0) {
|
||||
if (errno != EINTR) {
|
||||
ptrace_detach(pid);
|
||||
num_threads--;
|
||||
sig_num_threads = num_threads;
|
||||
goto next_entry;
|
||||
}
|
||||
}
|
||||
|
||||
if (syscall(SYS_ptrace, PTRACE_PEEKDATA, pid, &i, &j) || i++ != j ||
|
||||
syscall(SYS_ptrace, PTRACE_PEEKDATA, pid, &i, &j) || i != j) {
|
||||
/* Address spaces are distinct. This is probably
|
||||
* a forked child process rather than a thread.
|
||||
*/
|
||||
ptrace_detach(pid);
|
||||
num_threads--;
|
||||
sig_num_threads = num_threads;
|
||||
goto next_entry;
|
||||
}
|
||||
|
||||
found_parent |= pid == args->ppid;
|
||||
added_entries++;
|
||||
|
||||
next_entry:;
|
||||
} // entries iterations loop
|
||||
} // getdents loop
|
||||
|
||||
/* 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);
|
||||
return 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;
|
||||
}
|
||||
|
||||
return 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;
|
||||
} // pids[max_threads] scope
|
||||
} // for (;;)
|
||||
|
||||
failure:
|
||||
args->result = -1;
|
||||
args->err = errno;
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
int need_sigprocmask = 0;
|
||||
sigset_t sig_blocked, sig_old;
|
||||
int status, rc;
|
||||
|
||||
SetPTracerSetup ptracer_setup;
|
||||
|
||||
auto cleanup = MakeSimpleCleanup([&] () {
|
||||
int old_errno = errno;
|
||||
|
||||
if (need_sigprocmask) {
|
||||
sigprocmask(SIG_SETMASK, &sig_old, nullptr);
|
||||
}
|
||||
|
||||
if (!dumpable) {
|
||||
prctl(PR_SET_DUMPABLE, dumpable);
|
||||
}
|
||||
|
||||
errno = old_errno;
|
||||
});
|
||||
|
||||
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 = prctl(PR_GET_DUMPABLE, 0);
|
||||
if (!dumpable) {
|
||||
prctl(PR_SET_DUMPABLE, 1);
|
||||
}
|
||||
|
||||
/* Fill in argument block for dumper thread */
|
||||
args.result = -1;
|
||||
args.err = 0;
|
||||
args.ppid = getpid();
|
||||
args.altstack_mem = altstack_mem;
|
||||
args.parameter = parameter;
|
||||
args.callback = callback;
|
||||
|
||||
NO_INTR(args.proc_fd = open("/proc/self/task/", O_RDONLY|O_DIRECTORY|O_CLOEXEC));
|
||||
UniqueFD proc_closer{args.proc_fd};
|
||||
|
||||
if (args.proc_fd < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int pipefds[2];
|
||||
if (pipe2(pipefds, O_CLOEXEC)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
UniqueFD pipe_rd_closer{pipefds[0]};
|
||||
UniqueFD pipe_wr_closer{pipefds[1]};
|
||||
|
||||
args.start_pipe_rd = pipefds[0];
|
||||
args.start_pipe_wr = pipefds[1];
|
||||
|
||||
/* Before cloning the thread lister, block all asynchronous signals, as we */
|
||||
/* are not prepared to handle them. */
|
||||
sigfillset(&sig_blocked);
|
||||
for (int sig = 0; sig < sizeof(sync_signals)/sizeof(*sync_signals); sig++) {
|
||||
sigdelset(&sig_blocked, sync_signals[sig]);
|
||||
}
|
||||
if (sigprocmask(SIG_BLOCK, &sig_blocked, &sig_old)) {
|
||||
return -1;
|
||||
}
|
||||
need_sigprocmask = 1;
|
||||
|
||||
// make sure all functions used by parent from local_clone to after
|
||||
// waitpid have plt entries fully initialized. We cannot afford
|
||||
// dynamic linker running relocations and messing with errno (see
|
||||
// comment just below)
|
||||
(void)prctl(PR_GET_PDEATHSIG, 0);
|
||||
(void)close(-1);
|
||||
(void)waitpid(INT_MIN, nullptr, 0);
|
||||
|
||||
/* After cloning, both the parent and the child share the same
|
||||
* instance of errno. We deal with this by being very
|
||||
* careful. Specifically, child immediately calls into sem_wait
|
||||
* which never fails (cannot even EINTR), so doesn't touch errno.
|
||||
*
|
||||
* Parent sets up PR_SET_PTRACER prctl (if it fails, which usually
|
||||
* doesn't happen, we ignore that failure). Then parent does close
|
||||
* on write side of start pipe. After that child runs complex code,
|
||||
* including arbitrary callback. So parent avoids screwing with
|
||||
* errno by immediately calling waitpid with async signals disabled.
|
||||
*
|
||||
* I.e. errno is parent's up until close below. Then errno belongs
|
||||
* to child up until it exits.
|
||||
*/
|
||||
clone_pid = local_clone((int (*)(void *))ListerThread, &args);
|
||||
if (clone_pid < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Most Linux kernels in the wild have Yama LSM enabled, so
|
||||
* requires us to explicitly give permission for child to ptrace
|
||||
* us. See man 2 ptrace for details. This then requires us to
|
||||
* synchronize with the child (see close on start pipe
|
||||
* below). I.e. so that child doesn't start ptracing before we've
|
||||
* completed this prctl call.
|
||||
*/
|
||||
ptracer_setup.Prepare(clone_pid);
|
||||
|
||||
/* Closing write side of pipe works like releasing the lock. It
|
||||
* allows the ListerThread to run past read() call on read side of
|
||||
* pipe and ptrace us.
|
||||
*/
|
||||
close(pipe_wr_closer.ReleaseFD());
|
||||
|
||||
/* So here child runs (see ListerThread), it finds and ptraces all
|
||||
* threads, runs whatever callback is setup and then
|
||||
* detaches/resumes everything. In any case we wait for child's
|
||||
* completion to gather status and synchronize everything. */
|
||||
|
||||
rc = waitpid(clone_pid, &status, __WALL);
|
||||
|
||||
if (rc < 0) {
|
||||
if (errno == EINTR) {
|
||||
RAW_LOG(FATAL, "BUG: EINTR from waitpid shouldn't be possible!");
|
||||
}
|
||||
// Any error waiting for child is sign of some bug, so abort
|
||||
// asap. Continuing is unsafe anyways with child potentially writing to our
|
||||
// stack.
|
||||
RAW_LOG(FATAL, "BUG: waitpid inside TCMalloc_ListAllProcessThreads cannot fail, but it did. Raw errno: %d\n", errno);
|
||||
} else if (WIFEXITED(status)) {
|
||||
errno = args.err;
|
||||
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;
|
||||
}
|
||||
|
||||
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 |= (ptrace_detach(thread_pids[num_threads]) >= 0);
|
||||
}
|
||||
return detached_at_least_one;
|
||||
}
|
75
3party/gperftools/src/base/linuxthreads.h
Normal file
75
3party/gperftools/src/base/linuxthreads.h
Normal file
@ -0,0 +1,75 @@
|
||||
// -*- 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 <stdarg.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
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);
|
||||
|
||||
#endif /* _LINUXTHREADS_H */
|
108
3party/gperftools/src/base/logging.cc
Normal file
108
3party/gperftools/src/base/logging.cc
Normal 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) {
|
||||
close(fd);
|
||||
}
|
||||
|
||||
#endif // _WIN32 || __CYGWIN__ || __CYGWIN32__
|
259
3party/gperftools/src/base/logging.h
Normal file
259
3party/gperftools/src/base/logging.h
Normal 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 HAVE_SYS_SYSCALL_H && !defined(__APPLE__)
|
||||
#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, "%s:%d Check failed: %s %s %s\n", __FILE__, __LINE__, #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_
|
561
3party/gperftools/src/base/low_level_alloc.cc
Normal file
561
3party/gperftools/src/base/low_level_alloc.cc
Normal file
@ -0,0 +1,561 @@
|
||||
// -*- 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 "mmap_hook.h"
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <new> // for placement-new
|
||||
|
||||
// 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, ®ion->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 = tcmalloc::DirectMUnMap((arena->flags & LowLevelAlloc::kAsyncSignalSafe) == 0,
|
||||
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) {
|
||||
const bool invoke_hooks = ((flags & LowLevelAlloc::kAsyncSignalSafe) == 0);
|
||||
|
||||
auto result = tcmalloc::DirectAnonMMap(invoke_hooks, size);
|
||||
|
||||
RAW_CHECK(result.success, "mmap error");
|
||||
|
||||
return result.addr;
|
||||
}
|
||||
|
||||
void DefaultPagesAllocator::UnMapPages(int32 flags, void *region, size_t size) {
|
||||
const bool invoke_hooks = ((flags & LowLevelAlloc::kAsyncSignalSafe) == 0);
|
||||
|
||||
int munmap_result = tcmalloc::DirectMUnMap(invoke_hooks, region, size);
|
||||
RAW_CHECK(munmap_result == 0,
|
||||
"LowLevelAlloc::DeleteArena: munmap failed address");
|
||||
}
|
130
3party/gperftools/src/base/low_level_alloc.h
Normal file
130
3party/gperftools/src/base/low_level_alloc.h
Normal file
@ -0,0 +1,130 @@
|
||||
// -*- 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"
|
||||
|
||||
#ifndef __APPLE__
|
||||
// As of now, whatever clang version apple ships (clang-1205.0.22.11),
|
||||
// somehow miscompiles LowLevelAlloc when we try this section
|
||||
// thingy. Thankfully, we only need this section stuff heap leak
|
||||
// checker which is Linux-only anyways.
|
||||
#define ATTR_MALLOC_SECTION ATTRIBUTE_SECTION(malloc_hook)
|
||||
#else
|
||||
#define ATTR_MALLOC_SECTION
|
||||
#endif
|
||||
|
||||
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)
|
||||
ATTR_MALLOC_SECTION;
|
||||
static void *AllocWithArena(size_t request, Arena *arena)
|
||||
ATTR_MALLOC_SECTION;
|
||||
|
||||
// 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) ATTR_MALLOC_SECTION;
|
||||
|
||||
// ATTR_MALLOC_SECTION 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
|
332
3party/gperftools/src/base/simple_mutex.h
Normal file
332
3party/gperftools/src/base/simple_mutex.h
Normal 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_ */
|
144
3party/gperftools/src/base/spinlock.cc
Normal file
144
3party/gperftools/src/base/spinlock.cc
Normal file
@ -0,0 +1,144 @@
|
||||
// -*- 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" : : );
|
||||
#elif defined(__GNUC__) && defined(__aarch64__)
|
||||
__asm__ __volatile__("isb" : : );
|
||||
#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.
|
||||
int SpinLock::SpinLoop() {
|
||||
int c = adaptive_spin_count;
|
||||
while (lockword_.load(std::memory_order_relaxed) != kSpinLockFree && --c > 0) {
|
||||
SpinlockPause();
|
||||
}
|
||||
int old = kSpinLockFree;
|
||||
lockword_.compare_exchange_strong(old, kSpinLockSleeper, std::memory_order_acquire);
|
||||
// note, that we try to set lock word to 'have sleeper' state might
|
||||
// look unnecessary, but:
|
||||
//
|
||||
// *) pay attention to second call to SpinLoop at the bottom of SlowLock loop below
|
||||
//
|
||||
// *) note, that we get there after sleeping in SpinLockDelay and
|
||||
// getting woken by Unlock
|
||||
//
|
||||
// *) also note, that we don't "count" sleepers, so when unlock
|
||||
// awakes us, it also sets lock word to "free". So we risk
|
||||
// forgetting other sleepers. And to prevent this, we become
|
||||
// "designated waker", by setting lock word to "have sleeper". So
|
||||
// then when we unlock, we also wake up someone.
|
||||
return old;
|
||||
}
|
||||
|
||||
void SpinLock::SlowLock() {
|
||||
int 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. Note,
|
||||
// compare_exchange updates lock_value with previous value of
|
||||
// lock word.
|
||||
lockword_.compare_exchange_strong(lock_value, kSpinLockSleeper,
|
||||
std::memory_order_acquire);
|
||||
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.
|
||||
lockword_.compare_exchange_strong(lock_value, kSpinLockSleeper, std::memory_order_acquire);
|
||||
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);
|
||||
}
|
166
3party/gperftools/src/base/spinlock.h
Normal file
166
3party/gperftools/src/base/spinlock.h
Normal file
@ -0,0 +1,166 @@
|
||||
// -*- 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 <atomic>
|
||||
#include <type_traits>
|
||||
|
||||
#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.
|
||||
void Lock() EXCLUSIVE_LOCK_FUNCTION() {
|
||||
int old = kSpinLockFree;
|
||||
if (!lockword_.compare_exchange_weak(old, kSpinLockHeld, std::memory_order_acquire)) {
|
||||
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.
|
||||
bool TryLock() EXCLUSIVE_TRYLOCK_FUNCTION(true) {
|
||||
int old = kSpinLockFree;
|
||||
return lockword_.compare_exchange_weak(old, kSpinLockHeld);
|
||||
}
|
||||
|
||||
// Release this SpinLock, which must be held by the calling thread.
|
||||
void Unlock() UNLOCK_FUNCTION() {
|
||||
int prev_value = lockword_.exchange(kSpinLockFree, std::memory_order_release);
|
||||
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()).
|
||||
bool IsHeld() const {
|
||||
return lockword_.load(std::memory_order_relaxed) != kSpinLockFree;
|
||||
}
|
||||
|
||||
static const base::LinkerInitialized LINKER_INITIALIZED; // backwards compat
|
||||
private:
|
||||
enum { kSpinLockFree = 0 };
|
||||
enum { kSpinLockHeld = 1 };
|
||||
enum { kSpinLockSleeper = 2 };
|
||||
|
||||
std::atomic<int> lockword_;
|
||||
|
||||
void SlowLock();
|
||||
void SlowUnlock();
|
||||
int 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:
|
||||
explicit SpinLockHolder(SpinLock* l) EXCLUSIVE_LOCK_FUNCTION(l)
|
||||
: lock_(l) {
|
||||
l->Lock();
|
||||
}
|
||||
SpinLockHolder(const SpinLockHolder&) = delete;
|
||||
~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)
|
||||
|
||||
namespace tcmalloc {
|
||||
|
||||
class TrivialOnce {
|
||||
public:
|
||||
template <typename Body>
|
||||
bool RunOnce(Body body) {
|
||||
auto done_atomic = reinterpret_cast<std::atomic<int>*>(&done_flag_);
|
||||
if (done_atomic->load(std::memory_order_acquire) == 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
SpinLockHolder h(reinterpret_cast<SpinLock*>(&lock_storage_));
|
||||
|
||||
if (done_atomic->load(std::memory_order_relaxed) == 1) {
|
||||
// barrier provided by lock
|
||||
return false;
|
||||
}
|
||||
body();
|
||||
done_atomic->store(1, std::memory_order_release);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
int done_flag_;
|
||||
alignas(alignof(SpinLock)) char lock_storage_[sizeof(SpinLock)];
|
||||
};
|
||||
|
||||
static_assert(std::is_trivial<TrivialOnce>::value == true, "");
|
||||
|
||||
} // namespace tcmalloc
|
||||
|
||||
|
||||
#endif // BASE_SPINLOCK_H_
|
83
3party/gperftools/src/base/spinlock_internal.cc
Normal file
83
3party/gperftools/src/base/spinlock_internal.cc
Normal file
@ -0,0 +1,83 @@
|
||||
// -*- 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(std::atomic<int> *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.
|
||||
static volatile uint64_t rand;
|
||||
uint64 r = rand;
|
||||
r = 0x5deece66dLL * r + 0xb; // numbers from nrand48()
|
||||
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));
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace base
|
53
3party/gperftools/src/base/spinlock_internal.h
Normal file
53
3party/gperftools/src/base/spinlock_internal.h
Normal file
@ -0,0 +1,53 @@
|
||||
// -*- 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 <atomic>
|
||||
|
||||
#include "base/basictypes.h"
|
||||
|
||||
namespace base {
|
||||
namespace internal {
|
||||
|
||||
void SpinLockWake(std::atomic<int> *w, bool all);
|
||||
void SpinLockDelay(std::atomic<int> *w, int32 value, int loop);
|
||||
|
||||
} // namespace internal
|
||||
} // namespace base
|
||||
#endif
|
102
3party/gperftools/src/base/spinlock_linux-inl.h
Normal file
102
3party/gperftools/src/base/spinlock_linux-inl.h
Normal file
@ -0,0 +1,102 @@
|
||||
// -*- 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 = (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(std::atomic<int> *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*>(w),
|
||||
FUTEX_WAIT | futex_private_flag, value,
|
||||
reinterpret_cast<struct kernel_timespec*>(&tm), NULL, 0);
|
||||
} else {
|
||||
nanosleep(&tm, NULL);
|
||||
}
|
||||
errno = save_errno;
|
||||
}
|
||||
}
|
||||
|
||||
void SpinLockWake(std::atomic<int> *w, bool all) {
|
||||
if (have_futex) {
|
||||
syscall(__NR_futex, reinterpret_cast<int*>(w),
|
||||
FUTEX_WAKE | futex_private_flag, all ? INT_MAX : 1, NULL, NULL, 0);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace base
|
63
3party/gperftools/src/base/spinlock_posix-inl.h
Normal file
63
3party/gperftools/src/base/spinlock_posix-inl.h
Normal 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(std::atomic<int> *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(std::atomic<int> *w, bool all) {
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace base
|
63
3party/gperftools/src/base/spinlock_win32-inl.h
Normal file
63
3party/gperftools/src/base/spinlock_win32-inl.h
Normal 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 Win32-specific part of spinlock_internal.cc
|
||||
*/
|
||||
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# pragma comment(lib, "Synchronization.lib")
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
namespace internal {
|
||||
|
||||
void SpinLockDelay(std::atomic<int> *w, int32 value, int loop) {
|
||||
if (loop != 0) {
|
||||
auto wait_ns = static_cast<uint64_t>(base::internal::SuggestedDelayNS(loop)) * 16;
|
||||
auto wait_ms = wait_ns / 1000000;
|
||||
|
||||
WaitOnAddress(w, &value, 4, static_cast<DWORD>(wait_ms));
|
||||
}
|
||||
}
|
||||
|
||||
void SpinLockWake(std::atomic<int> *w, bool all) {
|
||||
if (all) {
|
||||
WakeByAddressAll((void*)w);
|
||||
} else {
|
||||
WakeByAddressSingle((void*)w);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
} // namespace base
|
98
3party/gperftools/src/base/stl_allocator.h
Normal file
98
3party/gperftools/src/base/stl_allocator.h
Normal 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_
|
1013
3party/gperftools/src/base/sysinfo.cc
Normal file
1013
3party/gperftools/src/base/sysinfo.cc
Normal file
File diff suppressed because it is too large
Load Diff
230
3party/gperftools/src/base/sysinfo.h
Normal file
230
3party/gperftools/src/base/sysinfo.h
Normal 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_ */
|
133
3party/gperftools/src/base/thread_annotations.h
Normal file
133
3party/gperftools/src/base/thread_annotations.h
Normal 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_
|
140
3party/gperftools/src/base/vdso_support.cc
Normal file
140
3party/gperftools/src/base/vdso_support.cc
Normal file
@ -0,0 +1,140 @@
|
||||
// -*- 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/logging.h"
|
||||
#include "base/dynamic_annotations.h"
|
||||
#include "base/basictypes.h" // for COMPILE_ASSERT
|
||||
|
||||
#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
|
137
3party/gperftools/src/base/vdso_support.h
Normal file
137
3party/gperftools/src/base/vdso_support.h
Normal 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_
|
396
3party/gperftools/src/central_freelist.cc
Normal file
396
3party/gperftools/src/central_freelist.cc
Normal file
@ -0,0 +1,396 @@
|
||||
// -*- 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
|
||||
|
||||
#if defined(__has_builtin)
|
||||
#if __has_builtin(__builtin_add_overflow)
|
||||
#define USE_ADD_OVERFLOW
|
||||
#endif
|
||||
#endif
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void CentralFreeList::ReleaseToSpans(void* object) {
|
||||
const PageID p = reinterpret_cast<uintptr_t>(object) >> kPageShift;
|
||||
Span* span = Static::pageheap()->GetDescriptor(p);
|
||||
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++;
|
||||
}
|
||||
(void)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();
|
||||
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 = Static::pageheap()->NewWithSizeClass(npages, size_class_);
|
||||
if (span == nullptr) {
|
||||
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;
|
||||
|
||||
// Note, when ptr is close to the top of address space, ptr + size
|
||||
// might overflow the top of address space before we're able to
|
||||
// detect that it exceeded limit. So we need to be careful. See
|
||||
// https://github.com/gperftools/gperftools/issues/1323.
|
||||
ASSERT(limit - size >= ptr);
|
||||
for (;;) {
|
||||
|
||||
#ifndef USE_ADD_OVERFLOW
|
||||
auto nextptr = reinterpret_cast<char *>(reinterpret_cast<uintptr_t>(ptr) + size);
|
||||
if (nextptr < ptr || nextptr > limit) {
|
||||
break;
|
||||
}
|
||||
#else
|
||||
// Same as above, just helping compiler a bit to produce better code
|
||||
uintptr_t nextaddr;
|
||||
if (__builtin_add_overflow(reinterpret_cast<uintptr_t>(ptr), size, &nextaddr)) {
|
||||
break;
|
||||
}
|
||||
char* nextptr = reinterpret_cast<char*>(nextaddr);
|
||||
if (nextptr > limit) {
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
// [ptr, ptr+size) bytes are all valid bytes, so append them
|
||||
*tail = ptr;
|
||||
tail = reinterpret_cast<void**>(ptr);
|
||||
num++;
|
||||
ptr = nextptr;
|
||||
}
|
||||
ASSERT(ptr <= limit);
|
||||
ASSERT(ptr > limit - size); // same as ptr + size > limit but avoiding overflow
|
||||
*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
|
209
3party/gperftools/src/central_freelist.h
Normal file
209
3party/gperftools/src/central_freelist.h
Normal file
@ -0,0 +1,209 @@
|
||||
// -*- 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
|
||||
#include <stdint.h> // for int32_t
|
||||
#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_
|
195
3party/gperftools/src/check_address-inl.h
Normal file
195
3party/gperftools/src/check_address-inl.h
Normal file
@ -0,0 +1,195 @@
|
||||
// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
|
||||
// Copyright (c) 2023, 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.
|
||||
|
||||
// This is internal implementation details of
|
||||
// stacktrace_generic_fp-inl.h module. We only split this into
|
||||
// separate header to enable unit test coverage.
|
||||
|
||||
// This is only used on OS-es with mmap support.
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <sys/mman.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#if HAVE_SYS_SYSCALL_H && !__APPLE__
|
||||
#include <sys/syscall.h>
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
#if defined(__linux__) && !defined(FORCE_PIPES)
|
||||
#define CHECK_ADDRESS_USES_SIGPROCMASK
|
||||
|
||||
// Linux kernel ABI for sigprocmask requires us to pass exact sizeof
|
||||
// for kernel's sigset_t. Which is 64-bit for most arches, with only
|
||||
// notable exception of mips.
|
||||
#if defined(__mips__)
|
||||
static constexpr int kKernelSigSetSize = 16;
|
||||
#else
|
||||
static constexpr int kKernelSigSetSize = 8;
|
||||
#endif
|
||||
|
||||
// For Linux we have two strategies. One is calling sigprocmask with
|
||||
// bogus HOW argument and 'new' sigset arg our address. Kernel ends up
|
||||
// reading new sigset before interpreting how. So then we either get
|
||||
// EFAULT when addr is unreadable, or we get EINVAL for readable addr,
|
||||
// but bogus HOW argument.
|
||||
//
|
||||
// We 'steal' this idea from abseil. But nothing guarantees this exact
|
||||
// behavior of Linux. So to be future-compatible (some our binaries
|
||||
// will run tens of years from the time they're compiled), we also
|
||||
// have second more robust method.
|
||||
bool CheckAccessSingleSyscall(uintptr_t addr, int pagesize) {
|
||||
addr &= ~uintptr_t{15};
|
||||
|
||||
if (addr == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int rv = syscall(SYS_rt_sigprocmask, ~0, addr, uintptr_t{0}, kKernelSigSetSize);
|
||||
RAW_CHECK(rv < 0, "sigprocmask(~0, addr, ...)");
|
||||
|
||||
return (errno != EFAULT);
|
||||
}
|
||||
|
||||
// This is second strategy. Idea is more or less same as before, but
|
||||
// we use SIG_BLOCK for HOW argument. Then if this succeeds (with side
|
||||
// effect of blocking random set of signals), we simply restore
|
||||
// previous signal mask.
|
||||
bool CheckAccessTwoSyscalls(uintptr_t addr, int pagesize) {
|
||||
addr &= ~uintptr_t{15};
|
||||
|
||||
if (addr == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uintptr_t old[(kKernelSigSetSize + sizeof(uintptr_t) - 1) / sizeof(uintptr_t)];
|
||||
int rv = syscall(SYS_rt_sigprocmask, SIG_BLOCK, addr, old, kKernelSigSetSize);
|
||||
if (rv == 0) {
|
||||
syscall(SYS_rt_sigprocmask, SIG_SETMASK, old, nullptr, kKernelSigSetSize);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CheckAddressFirstCall(uintptr_t addr, int pagesize);
|
||||
|
||||
bool (* volatile CheckAddress)(uintptr_t addr, int pagesize) = CheckAddressFirstCall;
|
||||
|
||||
// And we choose between strategies by checking at runtime if
|
||||
// single-syscall approach actually works and switch to a proper
|
||||
// version.
|
||||
bool CheckAddressFirstCall(uintptr_t addr, int pagesize) {
|
||||
void* unreadable = mmap(0, pagesize, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
|
||||
RAW_CHECK(unreadable != MAP_FAILED, "mmap of unreadable");
|
||||
|
||||
if (!CheckAccessSingleSyscall(reinterpret_cast<uintptr_t>(unreadable), pagesize)) {
|
||||
CheckAddress = CheckAccessSingleSyscall;
|
||||
} else {
|
||||
CheckAddress = CheckAccessTwoSyscalls;
|
||||
}
|
||||
|
||||
// Sanity check that our unreadable address is unreadable and that
|
||||
// our readable address (our own fn pointer variable) is readable.
|
||||
RAW_CHECK(CheckAddress(reinterpret_cast<uintptr_t>(CheckAddress),
|
||||
pagesize),
|
||||
"sanity check for readable addr");
|
||||
RAW_CHECK(!CheckAddress(reinterpret_cast<uintptr_t>(unreadable),
|
||||
pagesize),
|
||||
"sanity check for unreadable addr");
|
||||
|
||||
(void)munmap(unreadable, pagesize);
|
||||
|
||||
return CheckAddress(addr, pagesize);
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
#if HAVE_SYS_SYSCALL_H && !__APPLE__
|
||||
static int raw_read(int fd, void* buf, size_t count) {
|
||||
return syscall(SYS_read, fd, buf, count);
|
||||
}
|
||||
static int raw_write(int fd, void* buf, size_t count) {
|
||||
return syscall(SYS_write, fd, buf, count);
|
||||
}
|
||||
#else
|
||||
#define raw_read read
|
||||
#define raw_write write
|
||||
#endif
|
||||
|
||||
bool CheckAddress(uintptr_t addr, int pagesize) {
|
||||
static tcmalloc::TrivialOnce once;
|
||||
static int fds[2];
|
||||
|
||||
once.RunOnce([] () {
|
||||
RAW_CHECK(pipe(fds) == 0, "pipe(fds)");
|
||||
|
||||
auto add_flag = [] (int fd, int get, int set, int the_flag) {
|
||||
int flags = fcntl(fd, get, 0);
|
||||
RAW_CHECK(flags >= 0, "fcntl get");
|
||||
flags |= the_flag;
|
||||
RAW_CHECK(fcntl(fd, set, flags) == 0, "fcntl set");
|
||||
};
|
||||
|
||||
for (int i = 0; i < 2; i++) {
|
||||
add_flag(fds[i], F_GETFD, F_SETFD, FD_CLOEXEC);
|
||||
add_flag(fds[i], F_GETFL, F_SETFL, O_NONBLOCK);
|
||||
}
|
||||
});
|
||||
|
||||
do {
|
||||
int rv = raw_write(fds[1], reinterpret_cast<void*>(addr), 1);
|
||||
RAW_CHECK(rv != 0, "raw_write(...) == 0");
|
||||
if (rv > 0) {
|
||||
return true;
|
||||
}
|
||||
if (errno == EFAULT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RAW_CHECK(errno == EAGAIN, "write errno must be EAGAIN");
|
||||
|
||||
char drainbuf[256];
|
||||
do {
|
||||
rv = raw_read(fds[0], drainbuf, sizeof(drainbuf));
|
||||
if (rv < 0 && errno != EINTR) {
|
||||
RAW_CHECK(errno == EAGAIN, "read errno must be EAGAIN");
|
||||
break;
|
||||
}
|
||||
// read succeeded or we got EINTR
|
||||
} while (true);
|
||||
} while (true);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // namespace
|
324
3party/gperftools/src/common.cc
Normal file
324
3party/gperftools/src/common.cc
Normal file
@ -0,0 +1,324 @@
|
||||
// -*- 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 <stdlib.h> // for strtol
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "common.h"
|
||||
#include "system-alloc.h"
|
||||
#include "base/spinlock.h"
|
||||
#include "base/commandlineflags.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;
|
||||
}
|
||||
|
||||
static 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();
|
||||
|
||||
#if (!defined(_WIN32) || defined(TCMALLOC_BRAVE_EFFECTIVE_PAGE_SIZE)) && !defined(TCMALLOC_COWARD_EFFECTIVE_PAGE_SIZE)
|
||||
size_t native_page_size = tcmalloc::commandlineflags::StringToLongLong(
|
||||
TCMallocGetenvSafe("TCMALLOC_OVERRIDE_PAGESIZE"), getpagesize());
|
||||
#else
|
||||
// So windows getpagesize() returns 64k. Because that is
|
||||
// "granularity size" w.r.t. their virtual memory facility. So kinda
|
||||
// maybe not a bad idea to also have effective logical pages at 64k
|
||||
// too. But it breaks frag_unittest (for mostly harmless
|
||||
// reason). And I am not brave enough to have our behavior change so
|
||||
// much on windows (which isn't that much; people routinely run 256k
|
||||
// logical pages anyways).
|
||||
constexpr size_t native_page_size = kPageSize;
|
||||
#endif
|
||||
|
||||
size_t min_span_size = std::max<size_t>(native_page_size, kPageSize);
|
||||
if (min_span_size > kPageSize && (min_span_size % kPageSize) != 0) {
|
||||
Log(kLog, __FILE__, __LINE__, "This should never happen, but somehow "
|
||||
"we got systems page size not power of 2 and not multiple of "
|
||||
"malloc's logical page size. Releasing memory back will mostly not happen. "
|
||||
"system: ", native_page_size, ", malloc: ", kPageSize);
|
||||
min_span_size = kPageSize;
|
||||
}
|
||||
|
||||
min_span_size_in_pages_ = min_span_size / kPageSize;
|
||||
|
||||
// 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 += min_span_size;
|
||||
// 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 += min_span_size;
|
||||
}
|
||||
// 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
|
311
3party/gperftools/src/common.h
Normal file
311
3party/gperftools/src/common.h
Normal file
@ -0,0 +1,311 @@
|
||||
// -*- 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
|
||||
#include <stdint.h> // for uintptr_t, uint64_t
|
||||
#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);
|
||||
}
|
||||
|
||||
// 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];
|
||||
|
||||
size_t min_span_size_in_pages_;
|
||||
|
||||
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.
|
||||
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
|
||||
int32 ATTRIBUTE_ALWAYS_INLINE ByteSizeForClass(uint32 cl) {
|
||||
return class_to_size_[cl];
|
||||
}
|
||||
|
||||
// Mapping from size class to max size storable in that class
|
||||
int32 class_to_size(uint32 cl) {
|
||||
return class_to_size_[cl];
|
||||
}
|
||||
|
||||
// Mapping from size class to number of pages to allocate at a time
|
||||
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.
|
||||
int num_objects_to_move(uint32 cl) {
|
||||
return num_objects_to_move_[cl];
|
||||
}
|
||||
|
||||
// Smallest Span size in bytes (max of system's page size and
|
||||
// kPageSize).
|
||||
Length min_span_size_in_pages() {
|
||||
return min_span_size_in_pages_;
|
||||
}
|
||||
};
|
||||
|
||||
// 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_
|
278
3party/gperftools/src/config.h.in
Normal file
278
3party/gperftools/src/config.h.in
Normal file
@ -0,0 +1,278 @@
|
||||
/* 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 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 `backtrace_symbols', and to 0 if
|
||||
you don't. */
|
||||
#undef HAVE_DECL_BACKTRACE_SYMBOLS
|
||||
|
||||
/* 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 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 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
|
||||
|
||||
/* Have PTHREAD_PRIO_INHERIT. */
|
||||
#undef HAVE_PTHREAD_PRIO_INHERIT
|
||||
|
||||
/* 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 <stdio.h> header file. */
|
||||
#undef HAVE_STDIO_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 the system has the type `struct mallinfo2'. */
|
||||
#undef HAVE_STRUCT_MALLINFO2
|
||||
|
||||
/* 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/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 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
|
||||
|
||||
/* 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
|
||||
|
||||
/* if libgcc stacktrace method should be default */
|
||||
#undef PREFER_LIBGCC_UNWINDER
|
||||
|
||||
/* Mark the systems where we know it's bad if pthreads runs too
|
||||
early before main (before threads are initialized, presumably). */
|
||||
#if defined(__FreeBSD__) || defined(_AIX)
|
||||
#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 all of the C90 standard headers exist (not just the ones
|
||||
required in a freestanding environment). This macro is provided for
|
||||
backward compatibility; new code need not use it. */
|
||||
#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
|
||||
|
||||
/* libunwind.h was found and is working */
|
||||
#undef USE_LIBUNWIND
|
||||
|
||||
/* 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_ */
|
||||
|
87
3party/gperftools/src/config_for_unittests.h
Normal file
87
3party/gperftools/src/config_for_unittests.h
Normal file
@ -0,0 +1,87 @@
|
||||
// -*- 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
|
||||
|
||||
#if defined(__clang__)
|
||||
#if __has_warning("-Wuse-after-free")
|
||||
#pragma clang diagnostic ignored "-Wuse-after-free"
|
||||
#endif
|
||||
#if __has_warning("-Wunused-result")
|
||||
#pragma clang diagnostic ignored "-Wunused-result"
|
||||
#endif
|
||||
#if __has_warning("-Wunused-private-field")
|
||||
#pragma clang diagnostic ignored "-Wunused-private-field"
|
||||
#endif
|
||||
#if __has_warning("-Wimplicit-exception-spec-mismatch")
|
||||
#pragma clang diagnostic ignored "-Wimplicit-exception-spec-mismatch"
|
||||
#endif
|
||||
#if __has_warning("-Wmissing-exception-spec")
|
||||
#pragma clang diagnostic ignored "-Wmissing-exception-spec"
|
||||
#endif
|
||||
#elif defined(__GNUC__)
|
||||
#pragma GCC diagnostic ignored "-Wpragmas" // warning: unknown option after '#pragma GCC diagnostic' kind
|
||||
#pragma GCC diagnostic ignored "-Wuse-after-free"
|
||||
#pragma GCC diagnostic ignored "-Wunused-result"
|
||||
#endif
|
1594
3party/gperftools/src/debugallocation.cc
Normal file
1594
3party/gperftools/src/debugallocation.cc
Normal file
File diff suppressed because it is too large
Load Diff
169
3party/gperftools/src/emergency_malloc.cc
Normal file
169
3party/gperftools/src/emergency_malloc.cc
Normal 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;
|
||||
}
|
||||
};
|
60
3party/gperftools/src/emergency_malloc.h
Normal file
60
3party/gperftools/src/emergency_malloc.h
Normal 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
|
48
3party/gperftools/src/emergency_malloc_for_stacktrace.cc
Normal file
48
3party/gperftools/src/emergency_malloc_for_stacktrace.cc
Normal 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();
|
||||
}
|
39
3party/gperftools/src/fake_stacktrace_scope.cc
Normal file
39
3party/gperftools/src/fake_stacktrace_scope.cc
Normal 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) {
|
||||
}
|
||||
}
|
63
3party/gperftools/src/getenv_safe.h
Normal file
63
3party/gperftools/src/getenv_safe.h
Normal 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
|
396
3party/gperftools/src/getpc-inl.h
Normal file
396
3party/gperftools/src/getpc-inl.h
Normal file
@ -0,0 +1,396 @@
|
||||
// -*- eval: (read-only-mode) -*-
|
||||
// WARNING: this file is autogenerated.
|
||||
// Change and run gen_getpc.rb >getpc-inl.h if you want to
|
||||
// update. (And submit both files)
|
||||
|
||||
// What this file does? We have several possible ways of fetching PC
|
||||
// (program counter) of signal's ucontext. We explicitly choose to
|
||||
// avoid ifdef-ing specific OSes (or even specific versions), to
|
||||
// increase our chances that stuff simply works. Comments below refer
|
||||
// to OS/architecture combos for documentation purposes, but what
|
||||
// works is what is used.
|
||||
|
||||
// How it does it? It uses lightweight C++ template magic where
|
||||
// "wrong" ucontext_t{nullptr}-><field access> combos are
|
||||
// automagically filtered out (via SFINAE).
|
||||
|
||||
// Each known case is represented as a template class. For SFINAE
|
||||
// reasons we masquerade ucontext_t type behind U template
|
||||
// parameter. And we also parameterize by parent class. This allows us
|
||||
// to arrange all template instantiations in a single ordered chain of
|
||||
// inheritance. See RawUCToPC below.
|
||||
|
||||
// Note, we do anticipate that most times exactly one of those access
|
||||
// methods works. But we're prepared there could be several. In
|
||||
// particular, according to previous comments Solaris/x86 also has
|
||||
// REG_RIP defined, but it is somehow wrong. So we're careful about
|
||||
// preserving specific order. We couldn't handle this "multiplicity"
|
||||
// aspect in pure C++, so we use code generation.
|
||||
|
||||
namespace internal {
|
||||
|
||||
struct Empty {
|
||||
#ifdef DEFINE_TRIVIAL_GET
|
||||
#define HAVE_TRIVIAL_GET
|
||||
// special thing for stacktrace_generic_fp-inl which wants no-op case
|
||||
static void* Get(...) {
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
// NetBSD has really nice portable macros
|
||||
template <class U, class P, class = void>
|
||||
struct get_c47a30af : public P {
|
||||
};
|
||||
#ifdef _UC_MACHINE_PC
|
||||
template <class U, class P>
|
||||
struct get_c47a30af<U, P, void_t<decltype(_UC_MACHINE_PC(((U*){})))>> : public P {
|
||||
static void* Get(const U* uc) {
|
||||
// NetBSD has really nice portable macros
|
||||
return (void*)(_UC_MACHINE_PC(uc));
|
||||
}
|
||||
};
|
||||
#endif // _UC_MACHINE_PC
|
||||
|
||||
// Solaris/x86
|
||||
template <class U, class P, class = void>
|
||||
struct get_c4719e8d : public P {
|
||||
};
|
||||
#ifdef REG_PC
|
||||
template <class U, class P>
|
||||
struct get_c4719e8d<U, P, void_t<decltype(((U*){})->uc_mcontext.gregs[REG_PC])>> : public P {
|
||||
static void* Get(const U* uc) {
|
||||
// Solaris/x86
|
||||
return (void*)(uc->uc_mcontext.gregs[REG_PC]);
|
||||
}
|
||||
};
|
||||
#endif // REG_PC
|
||||
|
||||
// Linux/i386
|
||||
template <class U, class P, class = void>
|
||||
struct get_278cba85 : public P {
|
||||
};
|
||||
#ifdef REG_EIP
|
||||
template <class U, class P>
|
||||
struct get_278cba85<U, P, void_t<decltype(((U*){})->uc_mcontext.gregs[REG_EIP])>> : public P {
|
||||
static void* Get(const U* uc) {
|
||||
// Linux/i386
|
||||
return (void*)(uc->uc_mcontext.gregs[REG_EIP]);
|
||||
}
|
||||
};
|
||||
#endif // REG_EIP
|
||||
|
||||
// Linux/amd64
|
||||
template <class U, class P, class = void>
|
||||
struct get_b49f2593 : public P {
|
||||
};
|
||||
#ifdef REG_RIP
|
||||
template <class U, class P>
|
||||
struct get_b49f2593<U, P, void_t<decltype(((U*){})->uc_mcontext.gregs[REG_RIP])>> : public P {
|
||||
static void* Get(const U* uc) {
|
||||
// Linux/amd64
|
||||
return (void*)(uc->uc_mcontext.gregs[REG_RIP]);
|
||||
}
|
||||
};
|
||||
#endif // REG_RIP
|
||||
|
||||
// Linux/ia64
|
||||
template <class U, class P, class = void>
|
||||
struct get_8fda99d3 : public P {
|
||||
};
|
||||
template <class U, class P>
|
||||
struct get_8fda99d3<U, P, void_t<decltype(((U*){})->uc_mcontext.sc_ip)>> : public P {
|
||||
static void* Get(const U* uc) {
|
||||
// Linux/ia64
|
||||
return (void*)(uc->uc_mcontext.sc_ip);
|
||||
}
|
||||
};
|
||||
|
||||
// Linux/loongarch64
|
||||
template <class U, class P, class = void>
|
||||
struct get_4e9b682d : public P {
|
||||
};
|
||||
template <class U, class P>
|
||||
struct get_4e9b682d<U, P, void_t<decltype(((U*){})->uc_mcontext.__pc)>> : public P {
|
||||
static void* Get(const U* uc) {
|
||||
// Linux/loongarch64
|
||||
return (void*)(uc->uc_mcontext.__pc);
|
||||
}
|
||||
};
|
||||
|
||||
// Linux/{mips,aarch64}
|
||||
template <class U, class P, class = void>
|
||||
struct get_b94b7246 : public P {
|
||||
};
|
||||
template <class U, class P>
|
||||
struct get_b94b7246<U, P, void_t<decltype(((U*){})->uc_mcontext.pc)>> : public P {
|
||||
static void* Get(const U* uc) {
|
||||
// Linux/{mips,aarch64}
|
||||
return (void*)(uc->uc_mcontext.pc);
|
||||
}
|
||||
};
|
||||
|
||||
// Linux/ppc
|
||||
template <class U, class P, class = void>
|
||||
struct get_d0eeceae : public P {
|
||||
};
|
||||
#ifdef PT_NIP
|
||||
template <class U, class P>
|
||||
struct get_d0eeceae<U, P, void_t<decltype(((U*){})->uc_mcontext.uc_regs->gregs[PT_NIP])>> : public P {
|
||||
static void* Get(const U* uc) {
|
||||
// Linux/ppc
|
||||
return (void*)(uc->uc_mcontext.uc_regs->gregs[PT_NIP]);
|
||||
}
|
||||
};
|
||||
#endif // PT_NIP
|
||||
|
||||
// Linux/ppc
|
||||
template <class U, class P, class = void>
|
||||
struct get_a81f6801 : public P {
|
||||
};
|
||||
#ifdef PT_NIP
|
||||
template <class U, class P>
|
||||
struct get_a81f6801<U, P, void_t<decltype(((U*){})->uc_mcontext.gp_regs[PT_NIP])>> : public P {
|
||||
static void* Get(const U* uc) {
|
||||
// Linux/ppc
|
||||
return (void*)(uc->uc_mcontext.gp_regs[PT_NIP]);
|
||||
}
|
||||
};
|
||||
#endif // PT_NIP
|
||||
|
||||
// Linux/riscv
|
||||
template <class U, class P, class = void>
|
||||
struct get_24e794ef : public P {
|
||||
};
|
||||
#ifdef REG_PC
|
||||
template <class U, class P>
|
||||
struct get_24e794ef<U, P, void_t<decltype(((U*){})->uc_mcontext.__gregs[REG_PC])>> : public P {
|
||||
static void* Get(const U* uc) {
|
||||
// Linux/riscv
|
||||
return (void*)(uc->uc_mcontext.__gregs[REG_PC]);
|
||||
}
|
||||
};
|
||||
#endif // REG_PC
|
||||
|
||||
// Linux/s390
|
||||
template <class U, class P, class = void>
|
||||
struct get_d9a75ed3 : public P {
|
||||
};
|
||||
template <class U, class P>
|
||||
struct get_d9a75ed3<U, P, void_t<decltype(((U*){})->uc_mcontext.psw.addr)>> : public P {
|
||||
static void* Get(const U* uc) {
|
||||
// Linux/s390
|
||||
return (void*)(uc->uc_mcontext.psw.addr);
|
||||
}
|
||||
};
|
||||
|
||||
// Linux/arm (32-bit; legacy)
|
||||
template <class U, class P, class = void>
|
||||
struct get_07114491 : public P {
|
||||
};
|
||||
template <class U, class P>
|
||||
struct get_07114491<U, P, void_t<decltype(((U*){})->uc_mcontext.arm_pc)>> : public P {
|
||||
static void* Get(const U* uc) {
|
||||
// Linux/arm (32-bit; legacy)
|
||||
return (void*)(uc->uc_mcontext.arm_pc);
|
||||
}
|
||||
};
|
||||
|
||||
// FreeBSD/i386
|
||||
template <class U, class P, class = void>
|
||||
struct get_9be162e6 : public P {
|
||||
};
|
||||
template <class U, class P>
|
||||
struct get_9be162e6<U, P, void_t<decltype(((U*){})->uc_mcontext.mc_eip)>> : public P {
|
||||
static void* Get(const U* uc) {
|
||||
// FreeBSD/i386
|
||||
return (void*)(uc->uc_mcontext.mc_eip);
|
||||
}
|
||||
};
|
||||
|
||||
// FreeBSD/ppc
|
||||
template <class U, class P, class = void>
|
||||
struct get_2812b129 : public P {
|
||||
};
|
||||
template <class U, class P>
|
||||
struct get_2812b129<U, P, void_t<decltype(((U*){})->uc_mcontext.mc_srr0)>> : public P {
|
||||
static void* Get(const U* uc) {
|
||||
// FreeBSD/ppc
|
||||
return (void*)(uc->uc_mcontext.mc_srr0);
|
||||
}
|
||||
};
|
||||
|
||||
// FreeBSD/x86_64
|
||||
template <class U, class P, class = void>
|
||||
struct get_5bb1da03 : public P {
|
||||
};
|
||||
template <class U, class P>
|
||||
struct get_5bb1da03<U, P, void_t<decltype(((U*){})->uc_mcontext.mc_rip)>> : public P {
|
||||
static void* Get(const U* uc) {
|
||||
// FreeBSD/x86_64
|
||||
return (void*)(uc->uc_mcontext.mc_rip);
|
||||
}
|
||||
};
|
||||
|
||||
// OS X (i386, <=10.4)
|
||||
template <class U, class P, class = void>
|
||||
struct get_880f83fe : public P {
|
||||
};
|
||||
template <class U, class P>
|
||||
struct get_880f83fe<U, P, void_t<decltype(((U*){})->uc_mcontext->ss.eip)>> : public P {
|
||||
static void* Get(const U* uc) {
|
||||
// OS X (i386, <=10.4)
|
||||
return (void*)(uc->uc_mcontext->ss.eip);
|
||||
}
|
||||
};
|
||||
|
||||
// OS X (i386, >=10.5)
|
||||
template <class U, class P, class = void>
|
||||
struct get_92fcd89a : public P {
|
||||
};
|
||||
template <class U, class P>
|
||||
struct get_92fcd89a<U, P, void_t<decltype(((U*){})->uc_mcontext->__ss.__eip)>> : public P {
|
||||
static void* Get(const U* uc) {
|
||||
// OS X (i386, >=10.5)
|
||||
return (void*)(uc->uc_mcontext->__ss.__eip);
|
||||
}
|
||||
};
|
||||
|
||||
// OS X (x86_64)
|
||||
template <class U, class P, class = void>
|
||||
struct get_773e27c8 : public P {
|
||||
};
|
||||
template <class U, class P>
|
||||
struct get_773e27c8<U, P, void_t<decltype(((U*){})->uc_mcontext->ss.rip)>> : public P {
|
||||
static void* Get(const U* uc) {
|
||||
// OS X (x86_64)
|
||||
return (void*)(uc->uc_mcontext->ss.rip);
|
||||
}
|
||||
};
|
||||
|
||||
// OS X (>=10.5 [untested])
|
||||
template <class U, class P, class = void>
|
||||
struct get_6627078a : public P {
|
||||
};
|
||||
template <class U, class P>
|
||||
struct get_6627078a<U, P, void_t<decltype(((U*){})->uc_mcontext->__ss.__rip)>> : public P {
|
||||
static void* Get(const U* uc) {
|
||||
// OS X (>=10.5 [untested])
|
||||
return (void*)(uc->uc_mcontext->__ss.__rip);
|
||||
}
|
||||
};
|
||||
|
||||
// OS X (ppc, ppc64 [untested])
|
||||
template <class U, class P, class = void>
|
||||
struct get_da992aca : public P {
|
||||
};
|
||||
template <class U, class P>
|
||||
struct get_da992aca<U, P, void_t<decltype(((U*){})->uc_mcontext->ss.srr0)>> : public P {
|
||||
static void* Get(const U* uc) {
|
||||
// OS X (ppc, ppc64 [untested])
|
||||
return (void*)(uc->uc_mcontext->ss.srr0);
|
||||
}
|
||||
};
|
||||
|
||||
// OS X (>=10.5 [untested])
|
||||
template <class U, class P, class = void>
|
||||
struct get_cce47a40 : public P {
|
||||
};
|
||||
template <class U, class P>
|
||||
struct get_cce47a40<U, P, void_t<decltype(((U*){})->uc_mcontext->__ss.__srr0)>> : public P {
|
||||
static void* Get(const U* uc) {
|
||||
// OS X (>=10.5 [untested])
|
||||
return (void*)(uc->uc_mcontext->__ss.__srr0);
|
||||
}
|
||||
};
|
||||
|
||||
// OS X (arm64)
|
||||
template <class U, class P, class = void>
|
||||
struct get_0a082e42 : public P {
|
||||
};
|
||||
template <class U, class P>
|
||||
struct get_0a082e42<U, P, void_t<decltype(((U*){})->uc_mcontext->__ss.__pc)>> : public P {
|
||||
static void* Get(const U* uc) {
|
||||
// OS X (arm64)
|
||||
return (void*)(uc->uc_mcontext->__ss.__pc);
|
||||
}
|
||||
};
|
||||
|
||||
// OpenBSD/i386
|
||||
template <class U, class P, class = void>
|
||||
struct get_3baa113a : public P {
|
||||
};
|
||||
template <class U, class P>
|
||||
struct get_3baa113a<U, P, void_t<decltype(((U*){})->sc_eip)>> : public P {
|
||||
static void* Get(const U* uc) {
|
||||
// OpenBSD/i386
|
||||
return (void*)(uc->sc_eip);
|
||||
}
|
||||
};
|
||||
|
||||
// OpenBSD/x86_64
|
||||
template <class U, class P, class = void>
|
||||
struct get_79f33851 : public P {
|
||||
};
|
||||
template <class U, class P>
|
||||
struct get_79f33851<U, P, void_t<decltype(((U*){})->sc_rip)>> : public P {
|
||||
static void* Get(const U* uc) {
|
||||
// OpenBSD/x86_64
|
||||
return (void*)(uc->sc_rip);
|
||||
}
|
||||
};
|
||||
|
||||
inline void* RawUCToPC(const ucontext_t* uc) {
|
||||
// OpenBSD/x86_64
|
||||
using g_79f33851 = get_79f33851<ucontext_t, Empty>;
|
||||
// OpenBSD/i386
|
||||
using g_3baa113a = get_3baa113a<ucontext_t, g_79f33851>;
|
||||
// OS X (arm64)
|
||||
using g_0a082e42 = get_0a082e42<ucontext_t, g_3baa113a>;
|
||||
// OS X (>=10.5 [untested])
|
||||
using g_cce47a40 = get_cce47a40<ucontext_t, g_0a082e42>;
|
||||
// OS X (ppc, ppc64 [untested])
|
||||
using g_da992aca = get_da992aca<ucontext_t, g_cce47a40>;
|
||||
// OS X (>=10.5 [untested])
|
||||
using g_6627078a = get_6627078a<ucontext_t, g_da992aca>;
|
||||
// OS X (x86_64)
|
||||
using g_773e27c8 = get_773e27c8<ucontext_t, g_6627078a>;
|
||||
// OS X (i386, >=10.5)
|
||||
using g_92fcd89a = get_92fcd89a<ucontext_t, g_773e27c8>;
|
||||
// OS X (i386, <=10.4)
|
||||
using g_880f83fe = get_880f83fe<ucontext_t, g_92fcd89a>;
|
||||
// FreeBSD/x86_64
|
||||
using g_5bb1da03 = get_5bb1da03<ucontext_t, g_880f83fe>;
|
||||
// FreeBSD/ppc
|
||||
using g_2812b129 = get_2812b129<ucontext_t, g_5bb1da03>;
|
||||
// FreeBSD/i386
|
||||
using g_9be162e6 = get_9be162e6<ucontext_t, g_2812b129>;
|
||||
// Linux/arm (32-bit; legacy)
|
||||
using g_07114491 = get_07114491<ucontext_t, g_9be162e6>;
|
||||
// Linux/s390
|
||||
using g_d9a75ed3 = get_d9a75ed3<ucontext_t, g_07114491>;
|
||||
// Linux/riscv (with #ifdef REG_PC)
|
||||
using g_24e794ef = get_24e794ef<ucontext_t, g_d9a75ed3>;
|
||||
// Linux/ppc (with #ifdef PT_NIP)
|
||||
using g_a81f6801 = get_a81f6801<ucontext_t, g_24e794ef>;
|
||||
// Linux/ppc (with #ifdef PT_NIP)
|
||||
using g_d0eeceae = get_d0eeceae<ucontext_t, g_a81f6801>;
|
||||
// Linux/{mips,aarch64}
|
||||
using g_b94b7246 = get_b94b7246<ucontext_t, g_d0eeceae>;
|
||||
// Linux/loongarch64
|
||||
using g_4e9b682d = get_4e9b682d<ucontext_t, g_b94b7246>;
|
||||
// Linux/ia64
|
||||
using g_8fda99d3 = get_8fda99d3<ucontext_t, g_4e9b682d>;
|
||||
// Linux/amd64 (with #ifdef REG_RIP)
|
||||
using g_b49f2593 = get_b49f2593<ucontext_t, g_8fda99d3>;
|
||||
// Linux/i386 (with #ifdef REG_EIP)
|
||||
using g_278cba85 = get_278cba85<ucontext_t, g_b49f2593>;
|
||||
// Solaris/x86 (with #ifdef REG_PC)
|
||||
using g_c4719e8d = get_c4719e8d<ucontext_t, g_278cba85>;
|
||||
// NetBSD has really nice portable macros (with #ifdef _UC_MACHINE_PC)
|
||||
using g_c47a30af = get_c47a30af<ucontext_t, g_c4719e8d>;
|
||||
return g_c47a30af::Get(uc);
|
||||
}
|
||||
|
||||
} // namespace internal
|
99
3party/gperftools/src/getpc.h
Normal file
99
3party/gperftools/src/getpc.h
Normal file
@ -0,0 +1,99 @@
|
||||
// -*- 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_
|
||||
|
||||
// Note: we include this from one of configure script C++ tests as
|
||||
// part of verifying that we're able to build CPU profiler. I.e. we
|
||||
// cannot include config.h as we normally do, since it isn't produced
|
||||
// yet, but those HAVE_XYZ defines are available, so including
|
||||
// ucontext etc stuff works. It's usage from profiler.cc (and
|
||||
// stacktrace_generic_fp-inl.h) is after config.h is included.
|
||||
|
||||
// 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
|
||||
|
||||
#ifdef HAVE_ASM_PTRACE_H
|
||||
#include <asm/ptrace.h>
|
||||
#endif
|
||||
#if HAVE_SYS_UCONTEXT_H
|
||||
#include <sys/ucontext.h>
|
||||
#elif 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
|
||||
|
||||
namespace tcmalloc {
|
||||
namespace getpc {
|
||||
|
||||
// std::void_t is C++ 14. So we steal this from
|
||||
// https://en.cppreference.com/w/cpp/types/void_t
|
||||
template<typename... Ts>
|
||||
struct make_void { typedef void type; };
|
||||
template <typename... Ts>
|
||||
using void_t = typename make_void<Ts...>::type;
|
||||
|
||||
#include "getpc-inl.h"
|
||||
|
||||
} // namespace getpc
|
||||
} // namespace tcmalloc
|
||||
|
||||
// If this doesn't compile, you need to figure out the right value for
|
||||
// your system, and add it to the list above.
|
||||
inline void* GetPC(const ucontext_t& signal_ucontext) {
|
||||
void* retval = tcmalloc::getpc::internal::RawUCToPC(&signal_ucontext);
|
||||
|
||||
#if defined(__s390__) && !defined(__s390x__)
|
||||
// Mask out the AMODE31 bit from the PC recorded in the context.
|
||||
retval = (void*)((unsigned long)retval & 0x7fffffffUL);
|
||||
#endif
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
#endif // BASE_GETPC_H_
|
36
3party/gperftools/src/google/heap-checker.h
Normal file
36
3party/gperftools/src/google/heap-checker.h
Normal 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>
|
37
3party/gperftools/src/google/heap-profiler.h
Normal file
37
3party/gperftools/src/google/heap-profiler.h
Normal 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>
|
36
3party/gperftools/src/google/malloc_extension.h
Normal file
36
3party/gperftools/src/google/malloc_extension.h
Normal 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>
|
37
3party/gperftools/src/google/malloc_extension_c.h
Normal file
37
3party/gperftools/src/google/malloc_extension_c.h
Normal 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>
|
36
3party/gperftools/src/google/malloc_hook.h
Normal file
36
3party/gperftools/src/google/malloc_hook.h
Normal 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>
|
37
3party/gperftools/src/google/malloc_hook_c.h
Normal file
37
3party/gperftools/src/google/malloc_hook_c.h
Normal 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>
|
37
3party/gperftools/src/google/profiler.h
Normal file
37
3party/gperftools/src/google/profiler.h
Normal 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>
|
36
3party/gperftools/src/google/stacktrace.h
Normal file
36
3party/gperftools/src/google/stacktrace.h
Normal 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>
|
37
3party/gperftools/src/google/tcmalloc.h
Normal file
37
3party/gperftools/src/google/tcmalloc.h
Normal 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>
|
422
3party/gperftools/src/gperftools/heap-checker.h
Normal file
422
3party/gperftools/src/gperftools/heap-checker.h
Normal 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 linuxthreads.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_
|
105
3party/gperftools/src/gperftools/heap-profiler.h
Normal file
105
3party/gperftools/src/gperftools/heap-profiler.h
Normal 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_ */
|
444
3party/gperftools/src/gperftools/malloc_extension.h
Normal file
444
3party/gperftools/src/gperftools/malloc_extension.h
Normal file
@ -0,0 +1,444 @@
|
||||
// -*- 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();
|
||||
|
||||
// Note, as of gperftools 3.11 it is identical to
|
||||
// MarkThreadIdle. See github issue #880
|
||||
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_
|
103
3party/gperftools/src/gperftools/malloc_extension_c.h
Normal file
103
3party/gperftools/src/gperftools/malloc_extension_c.h
Normal file
@ -0,0 +1,103 @@
|
||||
/* 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 void MallocExtension_SetMemoryReleaseRate(double rate);
|
||||
PERFTOOLS_DLL_DECL double MallocExtension_GetMemoryReleaseRate(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_ */
|
359
3party/gperftools/src/gperftools/malloc_hook.h
Normal file
359
3party/gperftools/src/gperftools/malloc_hook.h
Normal 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_ */
|
173
3party/gperftools/src/gperftools/malloc_hook_c.h
Normal file
173
3party/gperftools/src/gperftools/malloc_hook_c.h
Normal 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_ */
|
37
3party/gperftools/src/gperftools/nallocx.h
Normal file
37
3party/gperftools/src/gperftools/nallocx.h
Normal 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_ */
|
173
3party/gperftools/src/gperftools/profiler.h
Normal file
173
3party/gperftools/src/gperftools/profiler.h
Normal 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_ */
|
117
3party/gperftools/src/gperftools/stacktrace.h
Normal file
117
3party/gperftools/src/gperftools/stacktrace.h
Normal 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_ */
|
166
3party/gperftools/src/gperftools/tcmalloc.h.in
Normal file
166
3party/gperftools/src/gperftools/tcmalloc.h.in
Normal file
@ -0,0 +1,166 @@
|
||||
/* -*- 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@ || @ac_cv_have_struct_mallinfo2@
|
||||
# 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
|
||||
#if @ac_cv_have_struct_mallinfo2@
|
||||
PERFTOOLS_DLL_DECL struct mallinfo2 tc_mallinfo2(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_ */
|
98
3party/gperftools/src/heap-checker-bcad.cc
Normal file
98
3party/gperftools/src/heap-checker-bcad.cc
Normal file
@ -0,0 +1,98 @@
|
||||
// -*- 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.
|
||||
//
|
||||
// We use explicit global operator new/delete functions since
|
||||
// plain 'naked' delete new int modern compilers optimize out to
|
||||
// nothing. And apparently calling those global new/delete
|
||||
// functions is assumed by compilers to be 'for effect' as well.
|
||||
(operator delete)((operator new)(4));
|
||||
// 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;
|
2442
3party/gperftools/src/heap-checker.cc
Normal file
2442
3party/gperftools/src/heap-checker.cc
Normal file
File diff suppressed because it is too large
Load Diff
78
3party/gperftools/src/heap-profile-stats.h
Normal file
78
3party/gperftools/src/heap-profile-stats.h
Normal 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;
|
||||
}
|
||||
|
||||
int64_t allocs; // Number of allocation calls.
|
||||
int64_t frees; // Number of free calls.
|
||||
int64_t alloc_size; // Total size of all allocated objects so far.
|
||||
int64_t 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_
|
628
3party/gperftools/src/heap-profile-table.cc
Normal file
628
3party/gperftools/src/heap-profile-table.cc
Normal file
@ -0,0 +1,628 @@
|
||||
// -*- 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
|
||||
#include <inttypes.h> // for PRIxPTR
|
||||
#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, "%6" PRId64 ": %8" PRId64 " [%6" PRId64 ": %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::LockHolder holder{};
|
||||
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: %" PRId64 " %" PRId64 "\n",
|
||||
total_.allocs - total_.frees,
|
||||
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: %" PRId64 " %" PRId64 "\n",
|
||||
s->total_.allocs - s->total_.frees,
|
||||
s->total_.alloc_size - s->total_.free_size);
|
||||
return s;
|
||||
}
|
||||
|
||||
// Information kept per unique bucket seen
|
||||
struct HeapProfileTable::Snapshot::Entry {
|
||||
int count;
|
||||
size_t 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 %zu 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);
|
||||
}
|
399
3party/gperftools/src/heap-profile-table.h
Normal file
399
3party/gperftools/src/heap-profile-table.h
Normal 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_
|
595
3party/gperftools/src/heap-profiler.cc
Normal file
595
3party/gperftools/src/heap-profiler.cc
Normal file
@ -0,0 +1,595 @@
|
||||
// -*- 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
|
||||
#include <inttypes.h>
|
||||
#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"
|
||||
#include "mmap_hook.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;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// 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. Numeric errno is %d", file_name, errno);
|
||||
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_t 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);
|
||||
}
|
||||
|
||||
static tcmalloc::MappingHookSpace mmap_logging_hook_space;
|
||||
|
||||
static void LogMappingEvent(const tcmalloc::MappingEvent& evt) {
|
||||
if (!FLAGS_mmap_log) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (evt.file_valid) {
|
||||
// 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%llx) = 0x%" PRIxPTR "",
|
||||
(uintptr_t) evt.before_address, evt.after_length, evt.prot,
|
||||
evt.flags, evt.file_fd, (unsigned long long) evt.file_off,
|
||||
(uintptr_t) evt.after_address);
|
||||
} else if (evt.after_valid && evt.before_valid) {
|
||||
// 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) evt.before_address, evt.before_length, evt.after_length, evt.flags,
|
||||
(uintptr_t) evt.after_address, (uintptr_t) evt.after_address);
|
||||
} else if (evt.is_sbrk) {
|
||||
intptr_t increment;
|
||||
uintptr_t result;
|
||||
if (evt.after_valid) {
|
||||
increment = evt.after_length;
|
||||
result = reinterpret_cast<uintptr_t>(evt.after_address) + evt.after_length;
|
||||
} else {
|
||||
increment = -static_cast<intptr_t>(evt.before_length);
|
||||
result = reinterpret_cast<uintptr_t>(evt.before_address);
|
||||
}
|
||||
|
||||
RAW_LOG(INFO, "sbrk(inc=%zd) = 0x%" PRIxPTR "",
|
||||
increment, (uintptr_t) result);
|
||||
} else if (evt.before_valid) {
|
||||
// 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) evt.before_address, evt.before_length);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
// 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:
|
||||
tcmalloc::HookMMapEvents(&mmap_logging_hook_space, LogMappingEvent);
|
||||
}
|
||||
|
||||
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:
|
||||
tcmalloc::UnHookMMapEvents(&mmap_logging_hook_space);
|
||||
}
|
||||
|
||||
// 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_t 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;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user