1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
13 #include "base/compiler_specific.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "tools/android/heap_profiler/heap_profiler.h"
19 typedef void* (*AllocatorFn
)(size_t);
20 typedef int (*FreeFn
)(void*, size_t);
22 const size_t kSize1
= 499 * PAGE_SIZE
;
23 const size_t kSize2
= 503 * PAGE_SIZE
;
24 const size_t kSize3
= 509 * PAGE_SIZE
;
26 // The purpose of the four functions below is to create watermarked allocations,
27 // so the test fixture can ascertain that the hooks work end-to-end.
28 __attribute__((noinline
)) void* MallocInner(size_t size
) {
29 void* ptr
= malloc(size
);
30 // The memset below is to avoid tail-call elimination optimizations and ensure
31 // that this function will be part of the stack trace.
36 __attribute__((noinline
)) void* MallocOuter(size_t size
) {
37 void* ptr
= MallocInner(size
);
42 __attribute__((noinline
)) void* DoMmap(size_t size
) {
44 0, size
, PROT_READ
| PROT_WRITE
, MAP_PRIVATE
| MAP_ANONYMOUS
, 0, 0);
47 __attribute__((noinline
)) void* MmapInner(size_t size
) {
48 void* ptr
= DoMmap(size
);
53 __attribute__((noinline
)) void* MmapOuter(size_t size
) {
54 void* ptr
= MmapInner(size
);
59 const HeapStats
* GetHeapStats() {
60 HeapStats
* const* stats_ptr
= reinterpret_cast<HeapStats
* const*>(
61 dlsym(RTLD_DEFAULT
, "heap_profiler_stats_for_tests"));
62 EXPECT_TRUE(stats_ptr
!= NULL
);
63 const HeapStats
* stats
= *stats_ptr
;
64 EXPECT_TRUE(stats
!= NULL
);
65 EXPECT_EQ(HEAP_PROFILER_MAGIC_MARKER
, stats
->magic_start
);
69 bool StackTraceContains(const StacktraceEntry
* s
, AllocatorFn fn
) {
70 // kExpectedFnLen is a gross estimation of the watermark functions' size.
71 // It tries to address the following problem: the addrs in the unwound stack
72 // stack frames will NOT point to the beginning of the functions, but to the
73 // PC after the call to malloc/mmap.
74 const size_t kExpectedFnLen
= 16;
75 const uintptr_t fn_addr
= reinterpret_cast<uintptr_t>(fn
);
76 for (size_t i
= 0; i
< HEAP_PROFILER_MAX_DEPTH
; ++i
) {
77 if (s
->frames
[i
] >= fn_addr
&& s
->frames
[i
] <= fn_addr
+ kExpectedFnLen
)
83 const StacktraceEntry
* LookupStackTrace(size_t size
, AllocatorFn fn
) {
84 const HeapStats
* stats
= GetHeapStats();
85 for (size_t i
= 0; i
< stats
->max_stack_traces
; ++i
) {
86 const StacktraceEntry
* st
= &stats
->stack_traces
[i
];
87 if (st
->alloc_bytes
== size
&& StackTraceContains(st
, fn
))
93 int DoFree(void* addr
, size_t /*size, ignored.*/) {
98 void TestStackTracesWithParams(AllocatorFn outer_fn
,
101 const HeapStats
* stats
= GetHeapStats();
103 void* m1
= outer_fn(kSize1
);
104 void* m2
= inner_fn(kSize2
);
105 void* m3
= inner_fn(kSize3
);
108 const StacktraceEntry
* st1
= LookupStackTrace(kSize1
, inner_fn
);
109 const StacktraceEntry
* st2
= LookupStackTrace(kSize2
, inner_fn
);
110 const StacktraceEntry
* st3
= LookupStackTrace(kSize3
, inner_fn
);
112 EXPECT_TRUE(st1
!= NULL
);
113 EXPECT_TRUE(StackTraceContains(st1
, outer_fn
));
114 EXPECT_TRUE(StackTraceContains(st1
, inner_fn
));
116 EXPECT_TRUE(st2
!= NULL
);
117 EXPECT_FALSE(StackTraceContains(st2
, outer_fn
));
118 EXPECT_TRUE(StackTraceContains(st2
, inner_fn
));
120 EXPECT_EQ(NULL
, st3
);
122 const size_t total_alloc_start
= stats
->total_alloc_bytes
;
123 const size_t num_stack_traces_start
= stats
->num_stack_traces
;
128 const size_t total_alloc_end
= stats
->total_alloc_bytes
;
129 const size_t num_stack_traces_end
= stats
->num_stack_traces
;
131 EXPECT_EQ(kSize1
+ kSize2
, total_alloc_start
- total_alloc_end
);
132 EXPECT_EQ(2, num_stack_traces_start
- num_stack_traces_end
);
133 EXPECT_EQ(NULL
, LookupStackTrace(kSize1
, inner_fn
));
134 EXPECT_EQ(NULL
, LookupStackTrace(kSize2
, inner_fn
));
135 EXPECT_EQ(NULL
, LookupStackTrace(kSize3
, inner_fn
));
138 TEST(HeapProfilerIntegrationTest
, TestMallocStackTraces
) {
139 TestStackTracesWithParams(&MallocOuter
, &MallocInner
, &DoFree
);
142 TEST(HeapProfilerIntegrationTest
, TestMmapStackTraces
) {
143 TestStackTracesWithParams(&MmapOuter
, &MmapInner
, &munmap
);
146 // Returns the path of the directory containing the current executable.
147 std::string
GetExePath() {
149 ssize_t len
= readlink("/proc/self/exe", buf
, sizeof(buf
) - 1);
151 return std::string();
152 std::string
path(buf
, len
);
153 size_t sep
= path
.find_last_of('/');
154 if (sep
== std::string::npos
)
155 return std::string();
162 int main(int argc
, char** argv
) {
163 // Re-launch the process itself forcing the preload of the libheap_profiler.
164 char* ld_preload
= getenv("LD_PRELOAD");
165 if (ld_preload
== NULL
|| strstr(ld_preload
, "libheap_profiler.so") == NULL
) {
166 char env_ld_lib_path
[256];
167 strlcpy(env_ld_lib_path
, "LD_LIBRARY_PATH=", sizeof(env_ld_lib_path
));
168 strlcat(env_ld_lib_path
, GetExePath().c_str(), sizeof(env_ld_lib_path
));
169 char env_ld_preload
[] = "LD_PRELOAD=libheap_profiler.so";
170 char* const env
[] = {env_ld_preload
, env_ld_lib_path
, 0};
171 execve("/proc/self/exe", argv
, env
);
172 // execve() never returns, unless something goes wrong.
177 testing::InitGoogleTest(&argc
, argv
);
178 return RUN_ALL_TESTS();