feat update
Some checks failed
linux-mips64-gcc / linux-gcc-mips64el (Debug) (push) Successful in 1m58s
linux-aarch64-cpu-gcc / linux-gcc-aarch64 (push) Successful in 2m8s
linux-x64-gcc / linux-gcc (Release) (push) Successful in 2m13s
linux-arm-gcc / linux-gcc-armhf (push) Failing after 2m22s
linux-mips64-gcc / linux-gcc-mips64el (Release) (push) Successful in 3m6s
linux-x64-gcc / linux-gcc (Debug) (push) Successful in 3m49s
Some checks failed
linux-mips64-gcc / linux-gcc-mips64el (Debug) (push) Successful in 1m58s
linux-aarch64-cpu-gcc / linux-gcc-aarch64 (push) Successful in 2m8s
linux-x64-gcc / linux-gcc (Release) (push) Successful in 2m13s
linux-arm-gcc / linux-gcc-armhf (push) Failing after 2m22s
linux-mips64-gcc / linux-gcc-mips64el (Release) (push) Successful in 3m6s
linux-x64-gcc / linux-gcc (Debug) (push) Successful in 3m49s
This commit is contained in:
parent
4a1d1c9aa1
commit
a137ee1f1c
@ -55,11 +55,11 @@
|
|||||||
|
|
||||||
/* Annoying stuff for windows; makes sure clients can import these functions */
|
/* Annoying stuff for windows; makes sure clients can import these functions */
|
||||||
#ifndef PERFTOOLS_DLL_DECL
|
#ifndef PERFTOOLS_DLL_DECL
|
||||||
# ifdef _WIN32
|
#ifdef _WIN32
|
||||||
# define PERFTOOLS_DLL_DECL __declspec(dllimport)
|
#define PERFTOOLS_DLL_DECL __declspec(dllimport)
|
||||||
# else
|
#else
|
||||||
# define PERFTOOLS_DLL_DECL
|
#define PERFTOOLS_DLL_DECL
|
||||||
# endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* All this code should be usable from within C apps. */
|
/* All this code should be usable from within C apps. */
|
||||||
@ -70,7 +70,7 @@ extern "C" {
|
|||||||
/* Start profiling and arrange to write profile data to file names
|
/* Start profiling and arrange to write profile data to file names
|
||||||
* of the form: "prefix.0000", "prefix.0001", ...
|
* of the form: "prefix.0000", "prefix.0001", ...
|
||||||
*/
|
*/
|
||||||
PERFTOOLS_DLL_DECL void HeapProfilerStart(const char* prefix);
|
PERFTOOLS_DLL_DECL void HeapProfilerStart(const char *prefix);
|
||||||
|
|
||||||
/* Returns non-zero if we are currently profiling the heap. (Returns
|
/* 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
|
* an int rather than a bool so it's usable from C.) This is true
|
||||||
@ -96,10 +96,10 @@ PERFTOOLS_DLL_DECL void HeapProfilerDump(const char *reason);
|
|||||||
* The returned pointer is a '\0'-terminated string allocated using malloc()
|
* 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.
|
* and should be free()-ed as soon as the caller does not need it anymore.
|
||||||
*/
|
*/
|
||||||
PERFTOOLS_DLL_DECL char* GetHeapProfile();
|
PERFTOOLS_DLL_DECL char *GetHeapProfile();
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} // extern "C"
|
}// extern "C"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* BASE_HEAP_PROFILER_H_ */
|
#endif /* BASE_HEAP_PROFILER_H_ */
|
||||||
|
@ -42,41 +42,41 @@
|
|||||||
#endif
|
#endif
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#ifdef HAVE_FCNTL_H
|
#ifdef HAVE_FCNTL_H
|
||||||
#include <fcntl.h> // for open()
|
#include <fcntl.h>// for open()
|
||||||
#endif
|
#endif
|
||||||
#ifdef HAVE_MMAP
|
#ifdef HAVE_MMAP
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#endif
|
#endif
|
||||||
#include <errno.h>
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <sys/types.h>
|
#include <errno.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <gperftools/heap-profiler.h>
|
#include <gperftools/heap-profiler.h>
|
||||||
|
|
||||||
#include "base/logging.h"
|
#include "base/basictypes.h"// for PRId64, among other things
|
||||||
#include "base/basictypes.h" // for PRId64, among other things
|
|
||||||
#include "base/googleinit.h"
|
|
||||||
#include "base/commandlineflags.h"
|
#include "base/commandlineflags.h"
|
||||||
#include "malloc_hook-inl.h"
|
#include "base/googleinit.h"
|
||||||
#include "tcmalloc_guard.h"
|
#include "base/logging.h"
|
||||||
#include <gperftools/malloc_hook.h>
|
|
||||||
#include <gperftools/malloc_extension.h>
|
|
||||||
#include "base/spinlock.h"
|
|
||||||
#include "base/low_level_alloc.h"
|
#include "base/low_level_alloc.h"
|
||||||
#include "base/sysinfo.h" // for GetUniquePathFromEnv()
|
#include "base/spinlock.h"
|
||||||
|
#include "base/sysinfo.h"// for GetUniquePathFromEnv()
|
||||||
#include "heap-profile-table.h"
|
#include "heap-profile-table.h"
|
||||||
|
#include "malloc_hook-inl.h"
|
||||||
#include "memory_region_map.h"
|
#include "memory_region_map.h"
|
||||||
#include "mmap_hook.h"
|
#include "mmap_hook.h"
|
||||||
|
#include "tcmalloc_guard.h"
|
||||||
|
#include <gperftools/malloc_extension.h>
|
||||||
|
#include <gperftools/malloc_hook.h>
|
||||||
|
|
||||||
#ifndef PATH_MAX
|
#ifndef PATH_MAX
|
||||||
#ifdef MAXPATHLEN
|
#ifdef MAXPATHLEN
|
||||||
#define PATH_MAX MAXPATHLEN
|
#define PATH_MAX MAXPATHLEN
|
||||||
#else
|
#else
|
||||||
#define PATH_MAX 4096 // seems conservative for max filename len!
|
#define PATH_MAX 4096// seems conservative for max filename len!
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -110,9 +110,7 @@ DEFINE_int64(heap_profile_time_interval,
|
|||||||
EnvToInt64("HEAP_PROFILE_TIME_INTERVAL", 0),
|
EnvToInt64("HEAP_PROFILE_TIME_INTERVAL", 0),
|
||||||
"If non-zero, dump heap profiling information once every "
|
"If non-zero, dump heap profiling information once every "
|
||||||
"specified number of seconds since the last dump.");
|
"specified number of seconds since the last dump.");
|
||||||
DEFINE_bool(mmap_log,
|
DEFINE_bool(mmap_log, EnvToBool("HEAP_PROFILE_MMAP_LOG", false), "Should mmap/munmap calls be logged?");
|
||||||
EnvToBool("HEAP_PROFILE_MMAP_LOG", false),
|
|
||||||
"Should mmap/munmap calls be logged?");
|
|
||||||
DEFINE_bool(mmap_profile,
|
DEFINE_bool(mmap_profile,
|
||||||
EnvToBool("HEAP_PROFILE_MMAP", false),
|
EnvToBool("HEAP_PROFILE_MMAP", false),
|
||||||
"If heap-profiling is on, also profile mmap, mremap, and sbrk)");
|
"If heap-profiling is on, also profile mmap, mremap, and sbrk)");
|
||||||
@ -121,7 +119,6 @@ DEFINE_bool(only_mmap_profile,
|
|||||||
"If heap-profiling is on, only profile mmap, mremap, and sbrk; "
|
"If heap-profiling is on, only profile mmap, mremap, and sbrk; "
|
||||||
"do not profile malloc/new/etc");
|
"do not profile malloc/new/etc");
|
||||||
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
// Locking
|
// Locking
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
@ -140,11 +137,16 @@ static SpinLock heap_lock(SpinLock::LINKER_INITIALIZED);
|
|||||||
|
|
||||||
static LowLevelAlloc::Arena *heap_profiler_memory;
|
static LowLevelAlloc::Arena *heap_profiler_memory;
|
||||||
|
|
||||||
static void* ProfilerMalloc(size_t bytes) {
|
static void *
|
||||||
return LowLevelAlloc::AllocWithArena(bytes, heap_profiler_memory);
|
ProfilerMalloc(size_t bytes)
|
||||||
|
{
|
||||||
|
return LowLevelAlloc::AllocWithArena(bytes, heap_profiler_memory);
|
||||||
}
|
}
|
||||||
static void ProfilerFree(void* p) {
|
|
||||||
LowLevelAlloc::Free(p);
|
static void
|
||||||
|
ProfilerFree(void *p)
|
||||||
|
{
|
||||||
|
LowLevelAlloc::Free(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We use buffers of this size in DoGetHeapProfile.
|
// We use buffers of this size in DoGetHeapProfile.
|
||||||
@ -155,106 +157,107 @@ static const int kProfileBufferSize = 1 << 20;
|
|||||||
// will be used by HeapProfileEndWriter when the application has to
|
// will be used by HeapProfileEndWriter when the application has to
|
||||||
// exit due to out-of-memory. This buffer is allocated in
|
// exit due to out-of-memory. This buffer is allocated in
|
||||||
// HeapProfilerStart. Access to this must be protected by heap_lock.
|
// HeapProfilerStart. Access to this must be protected by heap_lock.
|
||||||
static char* global_profiler_buffer = NULL;
|
static char *global_profiler_buffer = NULL;
|
||||||
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
// Profiling control/state data
|
// Profiling control/state data
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
// Access to all of these is protected by heap_lock.
|
// Access to all of these is protected by heap_lock.
|
||||||
static bool is_on = false; // If are on as a subsytem.
|
static bool is_on = false;// If are on as a subsytem.
|
||||||
static bool dumping = false; // Dumping status to prevent recursion
|
static bool dumping = false;// Dumping status to prevent recursion
|
||||||
static char* filename_prefix = NULL; // Prefix used for profile file names
|
static char *filename_prefix = NULL; // Prefix used for profile file names
|
||||||
// (NULL if no need for dumping yet)
|
// (NULL if no need for dumping yet)
|
||||||
static int dump_count = 0; // How many dumps so far
|
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_alloc = 0; // alloc_size when did we last dump
|
||||||
static int64 last_dump_free = 0; // free_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 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 int64 last_dump_time = 0; // The time of the last dump
|
||||||
|
|
||||||
static HeapProfileTable* heap_profile = NULL; // the heap profile table
|
static HeapProfileTable *heap_profile = NULL;// the heap profile table
|
||||||
|
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
// Profile generation
|
// Profile generation
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
// Input must be a buffer of size at least 1MB.
|
// Input must be a buffer of size at least 1MB.
|
||||||
static char* DoGetHeapProfileLocked(char* buf, int buflen) {
|
static char *
|
||||||
// We used to be smarter about estimating the required memory and
|
DoGetHeapProfileLocked(char *buf, int buflen)
|
||||||
// then capping it to 1MB and generating the profile into that.
|
{
|
||||||
if (buf == NULL || buflen < 1)
|
// We used to be smarter about estimating the required memory and
|
||||||
return NULL;
|
// then capping it to 1MB and generating the profile into that.
|
||||||
|
if (buf == NULL || buflen < 1) return NULL;
|
||||||
|
|
||||||
RAW_DCHECK(heap_lock.IsHeld(), "");
|
RAW_DCHECK(heap_lock.IsHeld(), "");
|
||||||
int bytes_written = 0;
|
int bytes_written = 0;
|
||||||
if (is_on) {
|
if (is_on) {
|
||||||
HeapProfileTable::Stats const stats = heap_profile->total();
|
HeapProfileTable::Stats const stats = heap_profile->total();
|
||||||
(void)stats; // avoid an unused-variable warning in non-debug mode.
|
(void) stats;// avoid an unused-variable warning in non-debug mode.
|
||||||
bytes_written = heap_profile->FillOrderedProfile(buf, buflen - 1);
|
bytes_written = heap_profile->FillOrderedProfile(buf, buflen - 1);
|
||||||
// FillOrderedProfile should not reduce the set of active mmap-ed regions,
|
// FillOrderedProfile should not reduce the set of active mmap-ed regions,
|
||||||
// hence MemoryRegionMap will let us remove everything we've added above:
|
// hence MemoryRegionMap will let us remove everything we've added above:
|
||||||
RAW_DCHECK(stats.Equivalent(heap_profile->total()), "");
|
RAW_DCHECK(stats.Equivalent(heap_profile->total()), "");
|
||||||
// if this fails, we somehow removed by FillOrderedProfile
|
// if this fails, we somehow removed by FillOrderedProfile
|
||||||
// more than we have added.
|
// more than we have added.
|
||||||
}
|
}
|
||||||
buf[bytes_written] = '\0';
|
buf[bytes_written] = '\0';
|
||||||
RAW_DCHECK(bytes_written == strlen(buf), "");
|
RAW_DCHECK(bytes_written == strlen(buf), "");
|
||||||
|
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" char* GetHeapProfile() {
|
extern "C" char *
|
||||||
// Use normal malloc: we return the profile to the user to free it:
|
GetHeapProfile()
|
||||||
char* buffer = reinterpret_cast<char*>(malloc(kProfileBufferSize));
|
{
|
||||||
SpinLockHolder l(&heap_lock);
|
// Use normal malloc: we return the profile to the user to free it:
|
||||||
return DoGetHeapProfileLocked(buffer, kProfileBufferSize);
|
char *buffer = reinterpret_cast<char *>(malloc(kProfileBufferSize));
|
||||||
|
SpinLockHolder l(&heap_lock);
|
||||||
|
return DoGetHeapProfileLocked(buffer, kProfileBufferSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// defined below
|
// defined below
|
||||||
static void NewHook(const void* ptr, size_t size);
|
static void NewHook(const void *ptr, size_t size);
|
||||||
static void DeleteHook(const void* ptr);
|
static void DeleteHook(const void *ptr);
|
||||||
|
|
||||||
// Helper for HeapProfilerDump.
|
// Helper for HeapProfilerDump.
|
||||||
static void DumpProfileLocked(const char* reason) {
|
static void
|
||||||
RAW_DCHECK(heap_lock.IsHeld(), "");
|
DumpProfileLocked(const char *reason)
|
||||||
RAW_DCHECK(is_on, "");
|
{
|
||||||
RAW_DCHECK(!dumping, "");
|
RAW_DCHECK(heap_lock.IsHeld(), "");
|
||||||
|
RAW_DCHECK(is_on, "");
|
||||||
|
RAW_DCHECK(!dumping, "");
|
||||||
|
|
||||||
if (filename_prefix == NULL) return; // we do not yet need dumping
|
if (filename_prefix == NULL) return;// we do not yet need dumping
|
||||||
|
|
||||||
dumping = true;
|
dumping = true;
|
||||||
|
|
||||||
// Make file name
|
// Make file name
|
||||||
char file_name[1000];
|
char file_name[1000];
|
||||||
dump_count++;
|
dump_count++;
|
||||||
snprintf(file_name, sizeof(file_name), "%s.%04d%s",
|
snprintf(file_name, sizeof(file_name), "%s.%04d%s", filename_prefix, dump_count, HeapProfileTable::kFileExt);
|
||||||
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);
|
||||||
|
|
||||||
// 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;
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
@ -263,73 +266,78 @@ static void DumpProfileLocked(const char* reason) {
|
|||||||
|
|
||||||
// Dump a profile after either an allocation or deallocation, if
|
// Dump a profile after either an allocation or deallocation, if
|
||||||
// the memory use has changed enough since the last dump.
|
// the memory use has changed enough since the last dump.
|
||||||
static void MaybeDumpProfileLocked() {
|
static void
|
||||||
if (!dumping) {
|
MaybeDumpProfileLocked()
|
||||||
const HeapProfileTable::Stats& total = heap_profile->total();
|
{
|
||||||
const int64_t inuse_bytes = total.alloc_size - total.free_size;
|
if (!dumping) {
|
||||||
bool need_to_dump = false;
|
const HeapProfileTable::Stats &total = heap_profile->total();
|
||||||
char buf[128];
|
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 &&
|
if (FLAGS_heap_profile_allocation_interval > 0
|
||||||
total.alloc_size >=
|
&& total.alloc_size >= last_dump_alloc + FLAGS_heap_profile_allocation_interval) {
|
||||||
last_dump_alloc + FLAGS_heap_profile_allocation_interval) {
|
snprintf(buf,
|
||||||
snprintf(buf, sizeof(buf), ("%" PRId64 " MB allocated cumulatively, "
|
sizeof(buf),
|
||||||
"%" PRId64 " MB currently in use"),
|
("%" PRId64 " MB allocated cumulatively, "
|
||||||
total.alloc_size >> 20, inuse_bytes >> 20);
|
"%" PRId64 " MB currently in use"),
|
||||||
need_to_dump = true;
|
total.alloc_size >> 20,
|
||||||
} else if (FLAGS_heap_profile_deallocation_interval > 0 &&
|
inuse_bytes >> 20);
|
||||||
total.free_size >=
|
need_to_dump = true;
|
||||||
last_dump_free + FLAGS_heap_profile_deallocation_interval) {
|
} else if (FLAGS_heap_profile_deallocation_interval > 0
|
||||||
snprintf(buf, sizeof(buf), ("%" PRId64 " MB freed cumulatively, "
|
&& total.free_size >= last_dump_free + FLAGS_heap_profile_deallocation_interval) {
|
||||||
"%" PRId64 " MB currently in use"),
|
snprintf(buf,
|
||||||
total.free_size >> 20, inuse_bytes >> 20);
|
sizeof(buf),
|
||||||
need_to_dump = true;
|
("%" PRId64 " MB freed cumulatively, "
|
||||||
} else if (FLAGS_heap_profile_inuse_interval > 0 &&
|
"%" PRId64 " MB currently in use"),
|
||||||
inuse_bytes >
|
total.free_size >> 20,
|
||||||
high_water_mark + FLAGS_heap_profile_inuse_interval) {
|
inuse_bytes >> 20);
|
||||||
snprintf(buf, sizeof(buf), "%" PRId64 " MB currently in use",
|
need_to_dump = true;
|
||||||
inuse_bytes >> 20);
|
} else if (FLAGS_heap_profile_inuse_interval > 0
|
||||||
need_to_dump = true;
|
&& inuse_bytes > high_water_mark + FLAGS_heap_profile_inuse_interval) {
|
||||||
} else if (FLAGS_heap_profile_time_interval > 0 ) {
|
snprintf(buf, sizeof(buf), "%" PRId64 " MB currently in use", inuse_bytes >> 20);
|
||||||
int64 current_time = time(NULL);
|
need_to_dump = true;
|
||||||
if (current_time - last_dump_time >=
|
} else if (FLAGS_heap_profile_time_interval > 0) {
|
||||||
FLAGS_heap_profile_time_interval) {
|
int64 current_time = time(NULL);
|
||||||
snprintf(buf, sizeof(buf), "%" PRId64 " sec since the last dump",
|
if (current_time - last_dump_time >= FLAGS_heap_profile_time_interval) {
|
||||||
current_time - last_dump_time);
|
snprintf(buf, sizeof(buf), "%" PRId64 " sec since the last dump", current_time - last_dump_time);
|
||||||
need_to_dump = true;
|
need_to_dump = true;
|
||||||
last_dump_time = current_time;
|
last_dump_time = current_time;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (need_to_dump) {
|
if (need_to_dump) {
|
||||||
DumpProfileLocked(buf);
|
DumpProfileLocked(buf);
|
||||||
|
|
||||||
last_dump_alloc = total.alloc_size;
|
last_dump_alloc = total.alloc_size;
|
||||||
last_dump_free = total.free_size;
|
last_dump_free = total.free_size;
|
||||||
if (inuse_bytes > high_water_mark)
|
if (inuse_bytes > high_water_mark) high_water_mark = inuse_bytes;
|
||||||
high_water_mark = inuse_bytes;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record an allocation in the profile.
|
// Record an allocation in the profile.
|
||||||
static void RecordAlloc(const void* ptr, size_t bytes, int skip_count) {
|
static void
|
||||||
// Take the stack trace outside the critical section.
|
RecordAlloc(const void *ptr, size_t bytes, int skip_count)
|
||||||
void* stack[HeapProfileTable::kMaxStackDepth];
|
{
|
||||||
int depth = HeapProfileTable::GetCallerStackTrace(skip_count + 1, stack);
|
// Take the stack trace outside the critical section.
|
||||||
SpinLockHolder l(&heap_lock);
|
void *stack[HeapProfileTable::kMaxStackDepth];
|
||||||
if (is_on) {
|
int depth = HeapProfileTable::GetCallerStackTrace(skip_count + 1, stack);
|
||||||
heap_profile->RecordAlloc(ptr, bytes, depth, stack);
|
SpinLockHolder l(&heap_lock);
|
||||||
MaybeDumpProfileLocked();
|
if (is_on) {
|
||||||
}
|
heap_profile->RecordAlloc(ptr, bytes, depth, stack);
|
||||||
|
MaybeDumpProfileLocked();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record a deallocation in the profile.
|
// Record a deallocation in the profile.
|
||||||
static void RecordFree(const void* ptr) {
|
static void
|
||||||
SpinLockHolder l(&heap_lock);
|
RecordFree(const void *ptr)
|
||||||
if (is_on) {
|
{
|
||||||
heap_profile->RecordFree(ptr);
|
SpinLockHolder l(&heap_lock);
|
||||||
MaybeDumpProfileLocked();
|
if (is_on) {
|
||||||
}
|
heap_profile->RecordFree(ptr);
|
||||||
|
MaybeDumpProfileLocked();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
@ -337,256 +345,263 @@ static void RecordFree(const void* ptr) {
|
|||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void NewHook(const void* ptr, size_t size) {
|
void
|
||||||
if (ptr != NULL) RecordAlloc(ptr, size, 0);
|
NewHook(const void *ptr, size_t size)
|
||||||
|
{
|
||||||
|
if (ptr != NULL) RecordAlloc(ptr, size, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void DeleteHook(const void* ptr) {
|
void
|
||||||
if (ptr != NULL) RecordFree(ptr);
|
DeleteHook(const void *ptr)
|
||||||
|
{
|
||||||
|
if (ptr != NULL) RecordFree(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static tcmalloc::MappingHookSpace mmap_logging_hook_space;
|
static tcmalloc::MappingHookSpace mmap_logging_hook_space;
|
||||||
|
|
||||||
static void LogMappingEvent(const tcmalloc::MappingEvent& evt) {
|
static void
|
||||||
if (!FLAGS_mmap_log) {
|
LogMappingEvent(const tcmalloc::MappingEvent &evt)
|
||||||
return;
|
{
|
||||||
}
|
if (!FLAGS_mmap_log) { return; }
|
||||||
|
|
||||||
if (evt.file_valid) {
|
if (evt.file_valid) {
|
||||||
// We use PRIxPTR not just '%p' to avoid deadlocks
|
// We use PRIxPTR not just '%p' to avoid deadlocks
|
||||||
// in pretty-printing of NULL as "nil".
|
// in pretty-printing of NULL as "nil".
|
||||||
// TODO(maxim): instead should use a safe snprintf reimplementation
|
// TODO(maxim): instead should use a safe snprintf reimplementation
|
||||||
RAW_LOG(INFO,
|
RAW_LOG(INFO,
|
||||||
"mmap(start=0x%" PRIxPTR ", len=%zu, prot=0x%x, flags=0x%x, "
|
"mmap(start=0x%" PRIxPTR
|
||||||
"fd=%d, offset=0x%llx) = 0x%" PRIxPTR "",
|
", len=%zu, prot=0x%x, flags=0x%x, "
|
||||||
(uintptr_t) evt.before_address, evt.after_length, evt.prot,
|
"fd=%d, offset=0x%llx) = 0x%" PRIxPTR "",
|
||||||
evt.flags, evt.file_fd, (unsigned long long) evt.file_off,
|
(uintptr_t) evt.before_address,
|
||||||
(uintptr_t) evt.after_address);
|
evt.after_length,
|
||||||
} else if (evt.after_valid && evt.before_valid) {
|
evt.prot,
|
||||||
// We use PRIxPTR not just '%p' to avoid deadlocks
|
evt.flags,
|
||||||
// in pretty-printing of NULL as "nil".
|
evt.file_fd,
|
||||||
// TODO(maxim): instead should use a safe snprintf reimplementation
|
(unsigned long long) evt.file_off,
|
||||||
RAW_LOG(INFO,
|
(uintptr_t) evt.after_address);
|
||||||
"mremap(old_addr=0x%" PRIxPTR ", old_size=%zu, "
|
} else if (evt.after_valid && evt.before_valid) {
|
||||||
"new_size=%zu, flags=0x%x, new_addr=0x%" PRIxPTR ") = "
|
// We use PRIxPTR not just '%p' to avoid deadlocks
|
||||||
"0x%" PRIxPTR "",
|
// in pretty-printing of NULL as "nil".
|
||||||
(uintptr_t) evt.before_address, evt.before_length, evt.after_length, evt.flags,
|
// TODO(maxim): instead should use a safe snprintf reimplementation
|
||||||
(uintptr_t) evt.after_address, (uintptr_t) evt.after_address);
|
RAW_LOG(INFO,
|
||||||
} else if (evt.is_sbrk) {
|
"mremap(old_addr=0x%" PRIxPTR
|
||||||
intptr_t increment;
|
", old_size=%zu, "
|
||||||
uintptr_t result;
|
"new_size=%zu, flags=0x%x, new_addr=0x%" PRIxPTR
|
||||||
if (evt.after_valid) {
|
") = "
|
||||||
increment = evt.after_length;
|
"0x%" PRIxPTR "",
|
||||||
result = reinterpret_cast<uintptr_t>(evt.after_address) + evt.after_length;
|
(uintptr_t) evt.before_address,
|
||||||
} else {
|
evt.before_length,
|
||||||
increment = -static_cast<intptr_t>(evt.before_length);
|
evt.after_length,
|
||||||
result = reinterpret_cast<uintptr_t>(evt.before_address);
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
// Starting/stopping/dumping
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
extern "C" void HeapProfilerStart(const char* prefix) {
|
extern "C" void
|
||||||
SpinLockHolder l(&heap_lock);
|
HeapProfilerStart(const char *prefix)
|
||||||
|
{
|
||||||
|
SpinLockHolder l(&heap_lock);
|
||||||
|
|
||||||
if (is_on) return;
|
if (is_on) return;
|
||||||
|
|
||||||
is_on = true;
|
is_on = true;
|
||||||
|
|
||||||
RAW_VLOG(0, "Starting tracking the heap");
|
RAW_VLOG(0, "Starting tracking the heap");
|
||||||
|
|
||||||
// This should be done before the hooks are set up, since it should
|
// This should be done before the hooks are set up, since it should
|
||||||
// call new, and we want that to be accounted for correctly.
|
// call new, and we want that to be accounted for correctly.
|
||||||
MallocExtension::Initialize();
|
MallocExtension::Initialize();
|
||||||
|
|
||||||
if (FLAGS_only_mmap_profile) {
|
if (FLAGS_only_mmap_profile) { FLAGS_mmap_profile = true; }
|
||||||
FLAGS_mmap_profile = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FLAGS_mmap_profile) {
|
if (FLAGS_mmap_profile) {
|
||||||
// Ask MemoryRegionMap to record all mmap, mremap, and sbrk
|
// Ask MemoryRegionMap to record all mmap, mremap, and sbrk
|
||||||
// call stack traces of at least size kMaxStackDepth:
|
// call stack traces of at least size kMaxStackDepth:
|
||||||
MemoryRegionMap::Init(HeapProfileTable::kMaxStackDepth,
|
MemoryRegionMap::Init(HeapProfileTable::kMaxStackDepth,
|
||||||
/* use_buckets */ true);
|
/* use_buckets */ true);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (FLAGS_mmap_log) {
|
if (FLAGS_mmap_log) {
|
||||||
// Install our hooks to do the logging:
|
// Install our hooks to do the logging:
|
||||||
tcmalloc::HookMMapEvents(&mmap_logging_hook_space, LogMappingEvent);
|
tcmalloc::HookMMapEvents(&mmap_logging_hook_space, LogMappingEvent);
|
||||||
}
|
}
|
||||||
|
|
||||||
heap_profiler_memory =
|
heap_profiler_memory = LowLevelAlloc::NewArena(0, LowLevelAlloc::DefaultArena());
|
||||||
LowLevelAlloc::NewArena(0, LowLevelAlloc::DefaultArena());
|
|
||||||
|
|
||||||
// Reserve space now for the heap profiler, so we can still write a
|
// Reserve space now for the heap profiler, so we can still write a
|
||||||
// heap profile even if the application runs out of memory.
|
// heap profile even if the application runs out of memory.
|
||||||
global_profiler_buffer =
|
global_profiler_buffer = reinterpret_cast<char *>(ProfilerMalloc(kProfileBufferSize));
|
||||||
reinterpret_cast<char*>(ProfilerMalloc(kProfileBufferSize));
|
|
||||||
|
|
||||||
heap_profile = new(ProfilerMalloc(sizeof(HeapProfileTable)))
|
heap_profile = new (ProfilerMalloc(sizeof(HeapProfileTable)))
|
||||||
HeapProfileTable(ProfilerMalloc, ProfilerFree, FLAGS_mmap_profile);
|
HeapProfileTable(ProfilerMalloc, ProfilerFree, FLAGS_mmap_profile);
|
||||||
|
|
||||||
last_dump_alloc = 0;
|
last_dump_alloc = 0;
|
||||||
last_dump_free = 0;
|
last_dump_free = 0;
|
||||||
high_water_mark = 0;
|
high_water_mark = 0;
|
||||||
last_dump_time = 0;
|
last_dump_time = 0;
|
||||||
|
|
||||||
// We do not reset dump_count so if the user does a sequence of
|
// We do not reset dump_count so if the user does a sequence of
|
||||||
// HeapProfilerStart/HeapProfileStop, we will get a continuous
|
// HeapProfilerStart/HeapProfileStop, we will get a continuous
|
||||||
// sequence of profiles.
|
// sequence of profiles.
|
||||||
|
|
||||||
if (FLAGS_only_mmap_profile == false) {
|
if (FLAGS_only_mmap_profile == false) {
|
||||||
// Now set the hooks that capture new/delete and malloc/free.
|
// Now set the hooks that capture new/delete and malloc/free.
|
||||||
RAW_CHECK(MallocHook::AddNewHook(&NewHook), "");
|
RAW_CHECK(MallocHook::AddNewHook(&NewHook), "");
|
||||||
RAW_CHECK(MallocHook::AddDeleteHook(&DeleteHook), "");
|
RAW_CHECK(MallocHook::AddDeleteHook(&DeleteHook), "");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy filename prefix
|
// Copy filename prefix
|
||||||
RAW_DCHECK(filename_prefix == NULL, "");
|
RAW_DCHECK(filename_prefix == NULL, "");
|
||||||
const int prefix_length = strlen(prefix);
|
const int prefix_length = strlen(prefix);
|
||||||
filename_prefix = reinterpret_cast<char*>(ProfilerMalloc(prefix_length + 1));
|
filename_prefix = reinterpret_cast<char *>(ProfilerMalloc(prefix_length + 1));
|
||||||
memcpy(filename_prefix, prefix, prefix_length);
|
memcpy(filename_prefix, prefix, prefix_length);
|
||||||
filename_prefix[prefix_length] = '\0';
|
filename_prefix[prefix_length] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" int IsHeapProfilerRunning() {
|
extern "C" int
|
||||||
SpinLockHolder l(&heap_lock);
|
IsHeapProfilerRunning()
|
||||||
return is_on ? 1 : 0; // return an int, because C code doesn't have bool
|
{
|
||||||
|
SpinLockHolder l(&heap_lock);
|
||||||
|
return is_on ? 1 : 0;// return an int, because C code doesn't have bool
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void HeapProfilerStop() {
|
extern "C" void
|
||||||
SpinLockHolder l(&heap_lock);
|
HeapProfilerStop()
|
||||||
|
{
|
||||||
|
SpinLockHolder l(&heap_lock);
|
||||||
|
|
||||||
if (!is_on) return;
|
if (!is_on) return;
|
||||||
|
|
||||||
if (FLAGS_only_mmap_profile == false) {
|
if (FLAGS_only_mmap_profile == false) {
|
||||||
// Unset our new/delete hooks, checking they were set:
|
// Unset our new/delete hooks, checking they were set:
|
||||||
RAW_CHECK(MallocHook::RemoveNewHook(&NewHook), "");
|
RAW_CHECK(MallocHook::RemoveNewHook(&NewHook), "");
|
||||||
RAW_CHECK(MallocHook::RemoveDeleteHook(&DeleteHook), "");
|
RAW_CHECK(MallocHook::RemoveDeleteHook(&DeleteHook), "");
|
||||||
}
|
}
|
||||||
if (FLAGS_mmap_log) {
|
if (FLAGS_mmap_log) {
|
||||||
// Restore mmap/sbrk hooks, checking that our hooks were set:
|
// Restore mmap/sbrk hooks, checking that our hooks were set:
|
||||||
tcmalloc::UnHookMMapEvents(&mmap_logging_hook_space);
|
tcmalloc::UnHookMMapEvents(&mmap_logging_hook_space);
|
||||||
}
|
}
|
||||||
|
|
||||||
// free profile
|
// free profile
|
||||||
heap_profile->~HeapProfileTable();
|
heap_profile->~HeapProfileTable();
|
||||||
ProfilerFree(heap_profile);
|
ProfilerFree(heap_profile);
|
||||||
heap_profile = NULL;
|
heap_profile = NULL;
|
||||||
|
|
||||||
// free output-buffer memory
|
// free output-buffer memory
|
||||||
ProfilerFree(global_profiler_buffer);
|
ProfilerFree(global_profiler_buffer);
|
||||||
|
|
||||||
// free prefix
|
// free prefix
|
||||||
ProfilerFree(filename_prefix);
|
ProfilerFree(filename_prefix);
|
||||||
filename_prefix = NULL;
|
filename_prefix = NULL;
|
||||||
|
|
||||||
if (!LowLevelAlloc::DeleteArena(heap_profiler_memory)) {
|
if (!LowLevelAlloc::DeleteArena(heap_profiler_memory)) { RAW_LOG(FATAL, "Memory leak in HeapProfiler:"); }
|
||||||
RAW_LOG(FATAL, "Memory leak in HeapProfiler:");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FLAGS_mmap_profile) {
|
if (FLAGS_mmap_profile) { MemoryRegionMap::Shutdown(); }
|
||||||
MemoryRegionMap::Shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
is_on = false;
|
is_on = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" void HeapProfilerDump(const char *reason) {
|
extern "C" void
|
||||||
SpinLockHolder l(&heap_lock);
|
HeapProfilerDump(const char *reason)
|
||||||
if (is_on && !dumping) {
|
{
|
||||||
DumpProfileLocked(reason);
|
SpinLockHolder l(&heap_lock);
|
||||||
}
|
if (is_on && !dumping) { DumpProfileLocked(reason); }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signal handler that is registered when a user selectable signal
|
// Signal handler that is registered when a user selectable signal
|
||||||
// number is defined in the environment variable HEAPPROFILESIGNAL.
|
// number is defined in the environment variable HEAPPROFILESIGNAL.
|
||||||
static void HeapProfilerDumpSignal(int signal_number) {
|
static void
|
||||||
(void)signal_number;
|
HeapProfilerDumpSignal(int signal_number)
|
||||||
if (!heap_lock.TryLock()) {
|
{
|
||||||
return;
|
(void) signal_number;
|
||||||
}
|
if (!heap_lock.TryLock()) { return; }
|
||||||
if (is_on && !dumping) {
|
if (is_on && !dumping) { DumpProfileLocked("signal"); }
|
||||||
DumpProfileLocked("signal");
|
heap_lock.Unlock();
|
||||||
}
|
|
||||||
heap_lock.Unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
// Initialization/finalization code
|
// Initialization/finalization code
|
||||||
//----------------------------------------------------------------------
|
//----------------------------------------------------------------------
|
||||||
|
|
||||||
// Initialization code
|
// Initialization code
|
||||||
static void HeapProfilerInit() {
|
static void
|
||||||
// Everything after this point is for setting up the profiler based on envvar
|
HeapProfilerInit()
|
||||||
char fname[PATH_MAX];
|
{
|
||||||
if (!GetUniquePathFromEnv("HEAPPROFILE", fname)) {
|
// Everything after this point is for setting up the profiler based on envvar
|
||||||
return;
|
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.
|
// We do a uid check so we don't write out files in a setuid executable.
|
||||||
#ifdef HAVE_GETEUID
|
#ifdef HAVE_GETEUID
|
||||||
if (getuid() != geteuid()) {
|
if (getuid() != geteuid()) {
|
||||||
RAW_LOG(WARNING, ("HeapProfiler: ignoring HEAPPROFILE because "
|
RAW_LOG(WARNING,
|
||||||
"program seems to be setuid\n"));
|
("HeapProfiler: ignoring HEAPPROFILE because "
|
||||||
return;
|
"program seems to be setuid\n"));
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
char *signal_number_str = getenv("HEAPPROFILESIGNAL");
|
char *signal_number_str = getenv("HEAPPROFILESIGNAL");
|
||||||
if (signal_number_str != NULL) {
|
if (signal_number_str != NULL) {
|
||||||
long int signal_number = strtol(signal_number_str, NULL, 10);
|
long int signal_number = strtol(signal_number_str, NULL, 10);
|
||||||
intptr_t old_signal_handler = reinterpret_cast<intptr_t>(signal(signal_number, HeapProfilerDumpSignal));
|
intptr_t old_signal_handler = reinterpret_cast<intptr_t>(signal(signal_number, HeapProfilerDumpSignal));
|
||||||
if (old_signal_handler == reinterpret_cast<intptr_t>(SIG_ERR)) {
|
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);
|
RAW_LOG(FATAL, "Failed to set signal. Perhaps signal number %s is invalid\n", signal_number_str);
|
||||||
} else if (old_signal_handler == 0) {
|
} else if (old_signal_handler == 0) {
|
||||||
RAW_LOG(INFO,"Using signal %d as heap profiling switch", signal_number);
|
RAW_LOG(INFO, "Using signal %d as heap profiling switch", signal_number);
|
||||||
} else {
|
} else {
|
||||||
RAW_LOG(FATAL, "Signal %d already in use\n", signal_number);
|
RAW_LOG(FATAL, "Signal %d already in use\n", signal_number);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
HeapProfileTable::CleanupOldProfiles(fname);
|
HeapProfileTable::CleanupOldProfiles(fname);
|
||||||
|
|
||||||
HeapProfilerStart(fname);
|
HeapProfilerStart(fname);
|
||||||
}
|
}
|
||||||
|
|
||||||
// class used for finalization -- dumps the heap-profile at program exit
|
// class used for finalization -- dumps the heap-profile at program exit
|
||||||
struct HeapProfileEndWriter {
|
struct HeapProfileEndWriter {
|
||||||
~HeapProfileEndWriter() {
|
~HeapProfileEndWriter()
|
||||||
char buf[128];
|
{
|
||||||
if (heap_profile) {
|
char buf[128];
|
||||||
const HeapProfileTable::Stats& total = heap_profile->total();
|
if (heap_profile) {
|
||||||
const int64_t inuse_bytes = total.alloc_size - total.free_size;
|
const HeapProfileTable::Stats &total = heap_profile->total();
|
||||||
|
const int64_t inuse_bytes = total.alloc_size - total.free_size;
|
||||||
|
|
||||||
if ((inuse_bytes >> 20) > 0) {
|
if ((inuse_bytes >> 20) > 0) {
|
||||||
snprintf(buf, sizeof(buf), ("Exiting, %" PRId64 " MB in use"),
|
snprintf(buf, sizeof(buf), ("Exiting, %" PRId64 " MB in use"), inuse_bytes >> 20);
|
||||||
inuse_bytes >> 20);
|
} else if ((inuse_bytes >> 10) > 0) {
|
||||||
} else if ((inuse_bytes >> 10) > 0) {
|
snprintf(buf, sizeof(buf), ("Exiting, %" PRId64 " kB in use"), inuse_bytes >> 10);
|
||||||
snprintf(buf, sizeof(buf), ("Exiting, %" PRId64 " kB in use"),
|
} else {
|
||||||
inuse_bytes >> 10);
|
snprintf(buf, sizeof(buf), ("Exiting, %" PRId64 " bytes in use"), inuse_bytes);
|
||||||
} else {
|
}
|
||||||
snprintf(buf, sizeof(buf), ("Exiting, %" PRId64 " bytes in use"),
|
} else {
|
||||||
inuse_bytes);
|
snprintf(buf, sizeof(buf), ("Exiting"));
|
||||||
}
|
}
|
||||||
} else {
|
HeapProfilerDump(buf);
|
||||||
snprintf(buf, sizeof(buf), ("Exiting"));
|
|
||||||
}
|
}
|
||||||
HeapProfilerDump(buf);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// We want to make sure tcmalloc is up and running before starting the profiler
|
// We want to make sure tcmalloc is up and running before starting the profiler
|
||||||
|
@ -104,7 +104,7 @@ target_sources(
|
|||||||
|
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
sled
|
sled
|
||||||
PUBLIC rpc_core fmt marl Async++ minilua tcmalloc_and_profiler_static
|
PUBLIC rpc_core fmt marl Async++ minilua
|
||||||
# protobuf::libprotoc
|
# protobuf::libprotoc
|
||||||
PRIVATE dl)
|
PRIVATE dl)
|
||||||
if(SLED_WITH_PROTOBUF)
|
if(SLED_WITH_PROTOBUF)
|
||||||
|
@ -108,5 +108,7 @@
|
|||||||
|
|
||||||
// testing
|
// testing
|
||||||
#include "sled/testing/test.h"
|
#include "sled/testing/test.h"
|
||||||
|
|
||||||
// debugging
|
// debugging
|
||||||
|
|
||||||
#endif// SLED_SLED_H
|
#endif// SLED_SLED_H
|
||||||
|
Loading…
Reference in New Issue
Block a user