summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDmitriy Evgenevich Gonzha <d.gonzha@samsung.com>2018-03-14 13:47:05 +0300
committerDmitriy Evgenevich Gonzha <d.gonzha@samsung.com>2018-09-07 20:32:24 +0300
commit46637f11ebdbd5b1253e8e6a36fe62487efcda0f (patch)
tree3cc5bc2539071f2a23d5230c0779304bc581b191
parent5493fd8ece76a701e4a48b3e2a13575cc15255a5 (diff)
downloadlinaro-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.c35
-rw-r--r--gcc/testsuite/c-c++-common/asan/auto_memory_profile_decrease.c37
-rw-r--r--gcc/testsuite/c-c++-common/asan/auto_memory_profile_timestamp.c29
-rw-r--r--libsanitizer/asan/asan_internal.h4
-rw-r--r--libsanitizer/asan/asan_memory_profile.cc129
-rw-r--r--libsanitizer/asan/asan_rtl.cc2
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_allocator_interface.h2
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_common.cc4
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_common_libcdep.cc117
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_flags.inc12
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_libc.h4
-rw-r--r--libsanitizer/sanitizer_common/sanitizer_printf.cc5
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