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
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|
[](https://orange-opensource.github.io/cppuprofile/)
|
||||||
|
|
||||||
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
|
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:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
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