diff options
author | Dmitriy Evgenevich Gonzha <d.gonzha@samsung.com> | 2018-03-14 13:47:05 +0300 |
---|---|---|
committer | Dmitriy Evgenevich Gonzha <d.gonzha@samsung.com> | 2018-09-07 20:32:24 +0300 |
commit | 46637f11ebdbd5b1253e8e6a36fe62487efcda0f (patch) | |
tree | 3cc5bc2539071f2a23d5230c0779304bc581b191 | |
parent | 5493fd8ece76a701e4a48b3e2a13575cc15255a5 (diff) | |
download | linaro-gcc-46637f11ebdbd5b1253e8e6a36fe62487efcda0f.tar.gz linaro-gcc-46637f11ebdbd5b1253e8e6a36fe62487efcda0f.tar.bz2 linaro-gcc-46637f11ebdbd5b1253e8e6a36fe62487efcda0f.zip |
ASan heap profiler additional options
Added additional options:
- timestamp support
- heap_profile_top_percent (default: top 90%)
- heap_profile_rss_mb (=0 - output rss in bytes)
- short profile snapshot
- option to print heap profile logs to separate file, removed
heap profile syslog printing
Also added heap profile tests
libsanitizer/
* asan/asan_internal.h: heap profile initialization.
* asan/asan_memory_profile.cc: heap profile initialization,
separate heap profile logging.
* asan/asan_rtl.cc: call heap profile initialization.
* sanitizer_common/sanitizer_allocator_interface.h: separate
heap profile logging function.
* sanitizer_common/sanitizer_common.cc: separate heap profile
logging function.
* sanitizer_common/sanitizer_common_libcdep.cc: additional heap
profile options.
* sanitizer_common/sanitizer_flags.inc: additional heap profile
options.
* sanitizer_common/sanitizer_libc.h: stdarg.h included,
internal_vsnprintf() added.
* sanitizer_common/sanitizer_printf.cc: new internal_vsnprintf()
function implementation.
gcc/testcuite/
* c-c++-common/asan/auto_memory_profile.c: new file, test for
standard features.
* c-c++-common/asan/auto_memory_profile_decrease.c: new file,
test for memory decrease detection.
* c-c++-common/asan/auto_memory_profile_timestamp.c: new file,
test for memory profile timestamp printing.
Change-Id: I5746a0cb07ba9ec8054560b70608f10b996eba34
-rw-r--r-- | gcc/testsuite/c-c++-common/asan/auto_memory_profile.c | 35 | ||||
-rw-r--r-- | gcc/testsuite/c-c++-common/asan/auto_memory_profile_decrease.c | 37 | ||||
-rw-r--r-- | gcc/testsuite/c-c++-common/asan/auto_memory_profile_timestamp.c | 29 | ||||
-rw-r--r-- | libsanitizer/asan/asan_internal.h | 4 | ||||
-rw-r--r-- | libsanitizer/asan/asan_memory_profile.cc | 129 | ||||
-rw-r--r-- | libsanitizer/asan/asan_rtl.cc | 2 | ||||
-rw-r--r-- | libsanitizer/sanitizer_common/sanitizer_allocator_interface.h | 2 | ||||
-rw-r--r-- | libsanitizer/sanitizer_common/sanitizer_common.cc | 4 | ||||
-rw-r--r-- | libsanitizer/sanitizer_common/sanitizer_common_libcdep.cc | 117 | ||||
-rw-r--r-- | libsanitizer/sanitizer_common/sanitizer_flags.inc | 12 | ||||
-rw-r--r-- | libsanitizer/sanitizer_common/sanitizer_libc.h | 4 | ||||
-rw-r--r-- | libsanitizer/sanitizer_common/sanitizer_printf.cc | 5 |
12 files changed, 370 insertions, 10 deletions
diff --git a/gcc/testsuite/c-c++-common/asan/auto_memory_profile.c b/gcc/testsuite/c-c++-common/asan/auto_memory_profile.c new file mode 100644 index 00000000000..e71a98b84ec --- /dev/null +++ b/gcc/testsuite/c-c++-common/asan/auto_memory_profile.c @@ -0,0 +1,35 @@ +// Based on LLVM Heap Profile test. Ported to C, adopted for GCC +// Tests heap_profile=1. +// Printing memory profiling only works in the configuration where we can +// detect leaks. + +/* { dg-do run } */ +/* { dg-set-target-env-var ASAN_OPTIONS "heap_profile=1" } */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +char *sink[1000]; + +int main() { + int i; + for (i = 0; i < 3; i++) { + const size_t kSize = 13000000; + char *x = (char*)malloc(sizeof(char) * kSize); + memset(x, i, kSize); + sink[i] = x; + sleep(1); + } + sleep(1); + for (i = 0; i < 3; i++) { + free(sink[i]); + } + return 0; +} + +/* { dg-output "HEAP PROFILE at RSS \[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output ".*13000000 byte\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output ".*HEAP PROFILE at RSS \[^\n\r]*(\n|\r\n|\r)Live\[^\n\r]*(\n|\r\n|\r)26000000 byte\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output ".*HEAP PROFILE at RSS \[^\n\r]*(\n|\r\n|\r)Live\[^\n\r]*(\n|\r\n|\r)39000000 byte\[^\n\r]*(\n|\r\n|\r)" } */ diff --git a/gcc/testsuite/c-c++-common/asan/auto_memory_profile_decrease.c b/gcc/testsuite/c-c++-common/asan/auto_memory_profile_decrease.c new file mode 100644 index 00000000000..cb59c2a9f8d --- /dev/null +++ b/gcc/testsuite/c-c++-common/asan/auto_memory_profile_decrease.c @@ -0,0 +1,37 @@ +// Tests heap_profile=1 with heap_profile_out_decrease=1. +// Fast memory decrease detection requires higher RSS decrease resolution, so +// heap_profile_out_full_lim=1 is required. +// Printing memory profiling only works in the configuration where we can +// detect leaks. + +/* { dg-do run } */ +/* { dg-set-target-env-var ASAN_OPTIONS "heap_profile=1:heap_profile_out_decrease=1:heap_profile_out_full_lim=1" } */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +char *sink[1000]; + +int main() { + int i; + for (i = 0; i < 3; i++) { + const size_t kSize = 13000000; + char *x = (char*)malloc(sizeof(char) * kSize); + memset(x, i, kSize); + sink[i] = x; + //sleep(1); + } + sleep(1); + for (i = 0; i < 3; i++) { + free(sink[i]); + sleep(1); + } + sleep(1); + return 0; +} + +/* { dg-output ".*HEAP PROFILE at RSS \[^\n\r]*(\n|\r\n|\r)Live\[^\n\r]*(\n|\r\n|\r)39000000 byte\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output ".*HEAP PROFILE at RSS \[^\n\r]*(\n|\r\n|\r)Live\[^\n\r]*(\n|\r\n|\r)26000000 byte\[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output ".*HEAP PROFILE at RSS \[^\n\r]*(\n|\r\n|\r)Live\[^\n\r]*(\n|\r\n|\r)13000000 byte\[^\n\r]*(\n|\r\n|\r)" } */ diff --git a/gcc/testsuite/c-c++-common/asan/auto_memory_profile_timestamp.c b/gcc/testsuite/c-c++-common/asan/auto_memory_profile_timestamp.c new file mode 100644 index 00000000000..31df815000c --- /dev/null +++ b/gcc/testsuite/c-c++-common/asan/auto_memory_profile_timestamp.c @@ -0,0 +1,29 @@ +// Based on LLVM Heap Profile test. Ported to C, adopted for GCC +// Tests heap_profile=1. +// Printing memory profiling only works in the configuration where we can +// detect leaks. + +/* { dg-do run } */ +/* { dg-set-target-env-var ASAN_OPTIONS "heap_profile=1:heap_profile_timestamp=1" } */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +char *sink[1000]; + +int main() { + int i; + const size_t kSize = 13000000; + char *x = (char*)malloc(sizeof(char) * kSize); + memset(x, 0, kSize); + sink[0] = x; + sleep(1); + free(x); + + return 0; +} + +/* { dg-output "HEAP PROFILE at RSS \[^\n\r]* time \[^\n\r]*(\n|\r\n|\r)" } */ +/* { dg-output ".* byte\[^\n\r]*(\n|\r\n|\r)" } */ diff --git a/libsanitizer/asan/asan_internal.h b/libsanitizer/asan/asan_internal.h index 15a28ff777b..1a9b1335bc5 100644 --- a/libsanitizer/asan/asan_internal.h +++ b/libsanitizer/asan/asan_internal.h @@ -66,6 +66,10 @@ void InitializePlatformExceptionHandlers(); // asan_win.cc / asan_posix.cc const char *DescribeSignalOrException(int signo); +// asan_memory_profile.cc +// heap profile initialization +void init_heap_profile(); + // asan_rtl.cc void NORETURN ShowStatsAndAbort(); diff --git a/libsanitizer/asan/asan_memory_profile.cc b/libsanitizer/asan/asan_memory_profile.cc index 5a2578596ad..52a41aa9f23 100644 --- a/libsanitizer/asan/asan_memory_profile.cc +++ b/libsanitizer/asan/asan_memory_profile.cc @@ -10,9 +10,15 @@ // This file implements __sanitizer_print_memory_profile. //===----------------------------------------------------------------------===// +#include <stdio.h> +#include <stdarg.h> + +#include "sanitizer_common/sanitizer_libc.h" + #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_stacktrace.h" +#include "sanitizer_common/sanitizer_stacktrace_printer.h" #include "sanitizer_common/sanitizer_stoptheworld.h" #include "lsan/lsan_common.h" #include "asan/asan_allocator.h" @@ -21,6 +27,83 @@ namespace __asan { +ReportFile *heap_profile_report_file = &report_file; +StaticSpinMutex alt_heap_profile_report_file_mu; +ReportFile alt_heap_profile_report_file = + {&alt_heap_profile_report_file_mu, kStderrFd, "", "", 0}; + +void init_heap_profile() { + // initialize new ReportFile from options + if (internal_strcmp(common_flags()->heap_profile_log_path, "") == 0) + return; + alt_heap_profile_report_file.SetReportPath( + common_flags()->heap_profile_log_path); + heap_profile_report_file = &alt_heap_profile_report_file; +} + +void HeapProfileRawWrite(const char *buffer) { + heap_profile_report_file->Write(buffer, internal_strlen(buffer)); +} + +/* Based on SharedPrintfCode() from sanitizer_printf.cc + Prints heap profile output to standard or separate log file + using HeapProfileRawWrite(). + Removed: append_pid, syslog printing + */ +SANITIZER_INTERFACE_ATTRIBUTE +void HeapProfileVPrintf(const char *format, va_list args) { + va_list args2; + va_copy(args2, args); + const int kLen = 16 * 1024; + // |local_buffer| is small enough not to overflow the stack and/or violate + // the stack limit enforced by TSan (-Wframe-larger-than=512). On the other + // hand, the bigger the buffer is, the more the chance the error report will + // fit into it. + char local_buffer[400]; + int needed_length; + char *buffer = local_buffer; + int buffer_size = ARRAY_SIZE(local_buffer); + // First try to print a message using a local buffer, and then fall back to + // mmaped buffer. + for (int use_mmap = 0; use_mmap < 2; use_mmap++) { + if (use_mmap) { + va_end(args); + va_copy(args, args2); + buffer = (char*)MmapOrDie(kLen, "Report"); + buffer_size = kLen; + } + needed_length = 0; + // Check that data fits into the current buffer. +# define CHECK_NEEDED_LENGTH \ + if (needed_length >= buffer_size) { \ + if (!use_mmap) continue; \ + RAW_CHECK_MSG(needed_length < kLen, \ + "Buffer in Report is too short!\n"); \ + } + needed_length += internal_vsnprintf(buffer + needed_length, + buffer_size - needed_length, format, args); + CHECK_NEEDED_LENGTH + // If the message fit into the buffer, print it and exit. + break; +# undef CHECK_NEEDED_LENGTH + } + HeapProfileRawWrite(buffer); + + // If we had mapped any memory, clean up. + if (buffer != local_buffer) + UnmapOrDie((void *)buffer, buffer_size); + va_end(args2); +} + +extern "C" { + void HeapProfilePrintf(const char *format, ...) { + va_list args; + va_start(args, format); + HeapProfileVPrintf(format, args); + va_end(args); + } +} + struct AllocationSite { u32 id; uptr total_size; @@ -44,6 +127,44 @@ class HeapProfile { allocations_.push_back({id, size, 1}); } +private: + // just copy of StackTrace::Print() with HeapProfilePrintf() calls + static void PrintStackTrace(StackTrace stack_trace) { + if (stack_trace.trace == nullptr || stack_trace.size == 0) { + HeapProfilePrintf(" <empty stack>\n\n"); + return; + } + InternalScopedString frame_desc(GetPageSizeCached() * 2); + InternalScopedString dedup_token(GetPageSizeCached()); + int dedup_frames = common_flags()->dedup_token_length; + uptr frame_num = 0; + for (uptr i = 0; i < stack_trace.size && stack_trace.trace[i]; i++) { + // PCs in stack traces are actually the return addresses, that is, + // addresses of the next instructions after the call. + uptr pc = StackTrace::GetPreviousInstructionPc(stack_trace.trace[i]); + SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(pc); + CHECK(frames); + for (SymbolizedStack *cur = frames; cur; cur = cur->next) { + frame_desc.clear(); + RenderFrame(&frame_desc, common_flags()->stack_trace_format, + frame_num++, cur->info, common_flags()->symbolize_vs_style, + common_flags()->strip_path_prefix); + HeapProfilePrintf("%s\n", frame_desc.data()); + if (dedup_frames-- > 0) { + if (dedup_token.length()) + dedup_token.append("--"); + dedup_token.append(cur->info.function); + } + } + frames->ClearAll(); + } + // Always print a trailing empty line after stack trace. + HeapProfilePrintf("\n"); + if (dedup_token.length()) + HeapProfilePrintf("DEDUP_TOKEN: %s\n", dedup_token.data()); + } + +public: void Print(uptr top_percent) { InternalSort(&allocations_, allocations_.size(), [](const AllocationSite &a, const AllocationSite &b) { @@ -51,13 +172,13 @@ class HeapProfile { }); CHECK(total_allocated_); uptr total_shown = 0; - Printf("Live Heap Allocations: %zd bytes from %zd allocations; " + HeapProfilePrintf("Live Heap Allocations: %zd bytes from %zd allocations; " "showing top %zd%%\n", total_allocated_, total_count_, top_percent); for (uptr i = 0; i < allocations_.size(); i++) { auto &a = allocations_[i]; - Printf("%zd byte(s) (%zd%%) in %zd allocation(s)\n", a.total_size, - a.total_size * 100 / total_allocated_, a.count); - StackDepotGet(a.id).Print(); + HeapProfilePrintf("%zd byte(s) (%zd%%) in %zd allocation(s)\n", + a.total_size, a.total_size * 100 / total_allocated_, a.count); + PrintStackTrace(StackDepotGet(a.id)); total_shown += a.total_size; if (total_shown * 100 / total_allocated_ > top_percent) break; diff --git a/libsanitizer/asan/asan_rtl.cc b/libsanitizer/asan/asan_rtl.cc index 8b28e6aa247..051e1e00f1e 100644 --- a/libsanitizer/asan/asan_rtl.cc +++ b/libsanitizer/asan/asan_rtl.cc @@ -617,6 +617,8 @@ static void AsanInitInternal() { Symbolizer::LateInitialize(); } + init_heap_profile(); + VReport(1, "AddressSanitizer Init done\n"); } diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator_interface.h b/libsanitizer/sanitizer_common/sanitizer_allocator_interface.h index 166f8c22049..c6e1244e9a4 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator_interface.h +++ b/libsanitizer/sanitizer_common/sanitizer_allocator_interface.h @@ -38,6 +38,8 @@ SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE + void HeapProfilePrintf(const char *format, ...); +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __sanitizer_print_memory_profile(int top_percent); } // extern "C" diff --git a/libsanitizer/sanitizer_common/sanitizer_common.cc b/libsanitizer/sanitizer_common/sanitizer_common.cc index 51ba4a1b509..eb2f325eb67 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common.cc +++ b/libsanitizer/sanitizer_common/sanitizer_common.cc @@ -510,5 +510,9 @@ SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __sanitizer_print_memory_profile(int top_percent) { (void)top_percent; } + +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +void HeapProfilePrintf(const char *format, va_list args) { +} #endif } // extern "C" diff --git a/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cc index a50ab14e1ac..e827dcfc5f6 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cc +++ b/libsanitizer/sanitizer_common/sanitizer_common_libcdep.cc @@ -17,6 +17,11 @@ #include "sanitizer_stacktrace.h" #include "sanitizer_symbolizer.h" +#if SANITIZER_LINUX && !SANITIZER_GO +#include <sys/time.h> +#include <time.h> +#endif + #if SANITIZER_POSIX #include "sanitizer_posix.h" #endif @@ -69,6 +74,66 @@ void SetSoftRssLimitExceededCallback(void (*Callback)(bool exceeded)) { } #if SANITIZER_LINUX && !SANITIZER_GO +void timespec2timeval(const struct timespec *ts, struct timeval *tv) +{ + tv->tv_sec = ts->tv_sec; + tv->tv_usec = ts->tv_nsec / 1000; +} + +int isPrintMemoryProfile(uptr current_rss, uptr last_rss, + struct timeval *last_time, struct timeval *cur_time) +{ + float lim_percent = + (float)(common_flags()->heap_profile_out_full_lim) / 100; + if (current_rss > last_rss * (1 + lim_percent)) { + // Memory increase + return 1; + } + if (common_flags()->heap_profile_out_decrease + && (current_rss < last_rss * (1 - lim_percent))) { + // Memory decrease + return 1; + } + + // timeout check + if (common_flags()->heap_profile_full_out_time == 0) + return 0; + + // glibc-specific implementation + uptr full_time_ms = common_flags()->heap_profile_full_out_time; + struct timeval time_lim; + time_lim.tv_sec = full_time_ms / 1000; + time_lim.tv_usec = (full_time_ms - time_lim.tv_sec * 1000) * 1000; + struct timeval timeout_time; + timeradd(last_time, &time_lim, &timeout_time); + if (!timercmp(cur_time, &timeout_time, <)) { + // timeout + return 1; + } + + return 0; +} + +int isPrintShortMemoryProfile( + struct timeval *last_time, struct timeval *cur_time) +{ + if (common_flags()->heap_profile_short_out_time == 0) + return 0; + + uptr full_time_ms = common_flags()->heap_profile_short_out_time; + struct timeval time_lim; + time_lim.tv_sec = full_time_ms / 1000; + time_lim.tv_usec = (full_time_ms - time_lim.tv_sec * 1000) * 1000; + struct timeval timeout_time; + timeradd(last_time, &time_lim, &timeout_time); + if (!timercmp(cur_time, &timeout_time, <)) { + // short snapshot timeout + return 1; + } + + return 0; +} + void BackgroundThread(void *arg) { uptr hard_rss_limit_mb = common_flags()->hard_rss_limit_mb; uptr soft_rss_limit_mb = common_flags()->soft_rss_limit_mb; @@ -77,9 +142,14 @@ void BackgroundThread(void *arg) { uptr prev_reported_stack_depot_size = 0; bool reached_soft_rss_limit = false; uptr rss_during_last_reported_profile = 0; + struct timeval last_profile_time = {0, 0}; + struct timeval last_short_profile_time = {0, 0}; while (true) { SleepForMillis(100); - uptr current_rss_mb = GetRSS() >> 20; + uptr current_rss = GetRSS(); + uptr current_rss_mb = current_rss >> 20; + if (common_flags()->heap_profile_rss_mb) + current_rss = current_rss_mb; if (Verbosity()) { // If RSS has grown 10% since last time, print some information. if (prev_reported_rss * 11 / 10 < current_rss_mb) { @@ -118,11 +188,46 @@ void BackgroundThread(void *arg) { SoftRssLimitExceededCallback(false); } } - if (heap_profile && - current_rss_mb > rss_during_last_reported_profile * 1.1) { - Printf("\n\nHEAP PROFILE at RSS %zdMb\n", current_rss_mb); - __sanitizer_print_memory_profile(90); - rss_during_last_reported_profile = current_rss_mb; + if (heap_profile) { + struct timespec ts; + struct timeval cur_time = {0, 0}; + if (common_flags()->heap_profile_full_out_time + || common_flags()->heap_profile_timestamp) { + clock_gettime(CLOCK_MONOTONIC, &ts); + timespec2timeval(&ts, &cur_time); + } + if (isPrintMemoryProfile(current_rss, + rss_during_last_reported_profile, &last_profile_time, &cur_time)) { + HeapProfilePrintf("\n\nHEAP PROFILE at RSS %zd", current_rss); + if (common_flags()->heap_profile_rss_mb) + HeapProfilePrintf("Mb"); + else + HeapProfilePrintf("b"); + if (common_flags()->heap_profile_timestamp) { + //long mksec = cur_time.tv_nsec / 1000; + HeapProfilePrintf(" time %lld.%06zd", + (s64)cur_time.tv_sec, (s32)cur_time.tv_usec); + } + HeapProfilePrintf("\n"); + __sanitizer_print_memory_profile( + common_flags()->heap_profile_top_percent); + rss_during_last_reported_profile = current_rss; + last_profile_time = cur_time; + last_short_profile_time = cur_time; + } + else if (isPrintShortMemoryProfile(&last_short_profile_time, &cur_time)) { + HeapProfilePrintf("\nSHORT HEAP PROFILE at RSS %zd", current_rss); + if (common_flags()->heap_profile_rss_mb) + HeapProfilePrintf("Mb"); + else + HeapProfilePrintf("b"); + if (common_flags()->heap_profile_timestamp) { + HeapProfilePrintf(" time %lld.%06zd", + (s64)cur_time.tv_sec, (s32)cur_time.tv_usec); + } + HeapProfilePrintf("\n"); + last_short_profile_time = cur_time; + } } } } diff --git a/libsanitizer/sanitizer_common/sanitizer_flags.inc b/libsanitizer/sanitizer_common/sanitizer_flags.inc index cb49473c4a8..479eff6a6b9 100644 --- a/libsanitizer/sanitizer_common/sanitizer_flags.inc +++ b/libsanitizer/sanitizer_common/sanitizer_flags.inc @@ -118,6 +118,18 @@ COMMON_FLAG(uptr, soft_rss_limit_mb, 0, " This limit does not affect memory allocations other than" " malloc/new.") COMMON_FLAG(bool, heap_profile, false, "Experimental heap profiler, asan-only") +COMMON_FLAG(bool, heap_profile_timestamp, false, "Print heap profiler timestamp") +COMMON_FLAG(bool, heap_profile_out_decrease, false, "Print memory profile on memory usage decrease") +COMMON_FLAG(int, heap_profile_out_full_lim, 10, "Memory usage change limit to print memory profile") +COMMON_FLAG(uptr, heap_profile_full_out_time, 0, "Timeout to print memory profile without of limit, ms; or 0 if no timeout is needed.") +COMMON_FLAG(uptr, heap_profile_short_out_time, 0, "Short memory profile print time interval, ms") +COMMON_FLAG(int, heap_profile_top_percent, 90, "Show top memory usage percent, 1-100") +COMMON_FLAG(bool, heap_profile_rss_mb, true, "Convert RSS memory usage to Mb from bytes") +COMMON_FLAG(const char *, heap_profile_log_path, "", + "If not empty, write heap profile logs to separate file.") +COMMON_FLAG(bool, allocator_release_to_os, false, + "Experimental. If true, try to periodically release unused" + " memory to the OS.\n") COMMON_FLAG(s32, allocator_release_to_os_interval_ms, kReleaseToOSIntervalNever, "Experimental. Only affects a 64-bit allocator. If set, tries to " "release unused memory to the OS, but not more often than this " diff --git a/libsanitizer/sanitizer_common/sanitizer_libc.h b/libsanitizer/sanitizer_common/sanitizer_libc.h index 98a4a9d2867..38328bd62c0 100644 --- a/libsanitizer/sanitizer_common/sanitizer_libc.h +++ b/libsanitizer/sanitizer_common/sanitizer_libc.h @@ -15,6 +15,8 @@ #ifndef SANITIZER_LIBC_H #define SANITIZER_LIBC_H +#include <stdarg.h> + // ----------- ATTENTION ------------- // This header should NOT include any other headers from sanitizer runtime. #include "sanitizer_internal_defs.h" @@ -53,6 +55,8 @@ uptr internal_wcslen(const wchar_t *s); char *internal_strstr(const char *haystack, const char *needle); // Works only for base=10 and doesn't set errno. s64 internal_simple_strtoll(const char *nptr, char **endptr, int base); +int internal_vsnprintf(char *buff, int buff_length, + const char *format, va_list args); int internal_snprintf(char *buffer, uptr length, const char *format, ...); // Return true if all bytes in [mem, mem+size) are zero. diff --git a/libsanitizer/sanitizer_common/sanitizer_printf.cc b/libsanitizer/sanitizer_common/sanitizer_printf.cc index c11113da244..f51d0ef847d 100644 --- a/libsanitizer/sanitizer_common/sanitizer_printf.cc +++ b/libsanitizer/sanitizer_common/sanitizer_printf.cc @@ -305,6 +305,11 @@ void Report(const char *format, ...) { va_end(args); } +int internal_vsnprintf(char *buff, int buff_length, + const char *format, va_list args) { + return VSNPrintf(buff, buff_length, format, args); +} + // Writes at most "length" symbols to "buffer" (including trailing '\0'). // Returns the number of symbols that should have been written to buffer // (not including trailing '\0'). Thus, the string is truncated |