summaryrefslogtreecommitdiffstats
path: root/third_party/tcmalloc
diff options
context:
space:
mode:
authorglider@chromium.org <glider@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-27 10:13:21 +0000
committerglider@chromium.org <glider@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-27 10:13:21 +0000
commit64e2d4a477055eab43bd5a66e98a468f3ea38520 (patch)
tree7671902c765dde36a6fb7d178423eb8b92af9dcc /third_party/tcmalloc
parent2ba8f892990cf6baec7d386c833e3f009d2fd44c (diff)
downloadchromium_src-64e2d4a477055eab43bd5a66e98a468f3ea38520.zip
chromium_src-64e2d4a477055eab43bd5a66e98a468f3ea38520.tar.gz
chromium_src-64e2d4a477055eab43bd5a66e98a468f3ea38520.tar.bz2
This CL introduces the stack shadowing mechanism that should help TCMalloc's
heap leak checker to unwind the memory allocation stacks better. Currently, if a memory region is allocated from a library built without frame pointers heapchecker is unable to unwind the stack and records only the top frame. This is inconvenient, because: -- several leaks from different places are treated as leaks from the same source -- it's hard to suppress such leaks, because a one-line suppression is uninformative linux_shadow_stacks.cc keeps the threads' IP and SP values in thread-local stacks upon each function entry/exit using gcc function instrumentation (-finstrument-functions). The GetStackTrace routine from stacktrace_shadow-inl.h unwinds the stack as usual (using frame pointers), but then updates the result with the shadow stack frames which SP values are below the bottom frame of the unwind result. Note that -finstrument-functions affects only Chromium code, not the libraries. This means that we cannot get more than one library function frame at the top of the stack. For example, consider a libfoo library that has a public foo_do_something() routine which allocates memory via foo_alloc(). If Chromium calls foo_do_something() from ChromeCallFoo(), then the following call chain effectively happens: main -> ChromeCallFoo -> foo_do_something -> foo_alloc If libfoo is built with -fomit-frame-pointers, heapcheck can unwind only the last stack frame: foo_alloc On the other hand, the shadow stack at the allocation site contains everything below the libfoo calls: main -> ChromeCallFoo As a result the following allocation stack is recorded: main -> ChromeCallFoo -> foo_alloc This is enough to distinguish between e.g. ChromeCallFoo1 and ChromeCallFoo2 Review URL: http://codereview.chromium.org/3120017 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@57658 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'third_party/tcmalloc')
-rw-r--r--third_party/tcmalloc/chromium/src/linux_shadow_stacks.cc128
-rw-r--r--third_party/tcmalloc/chromium/src/linux_shadow_stacks.h20
-rw-r--r--third_party/tcmalloc/chromium/src/stacktrace_x86-inl.h45
3 files changed, 193 insertions, 0 deletions
diff --git a/third_party/tcmalloc/chromium/src/linux_shadow_stacks.cc b/third_party/tcmalloc/chromium/src/linux_shadow_stacks.cc
new file mode 100644
index 0000000..a060b54
--- /dev/null
+++ b/third_party/tcmalloc/chromium/src/linux_shadow_stacks.cc
@@ -0,0 +1,128 @@
+// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "linux_shadow_stacks.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static const int kMaxShadowIndex = 2048;
+static const char kOverflowMessage[] = "Shadow stack overflow\n";
+
+// Thread-local vars.
+__thread
+int shadow_index = -1;
+__thread
+void *shadow_ip_stack[kMaxShadowIndex];
+__thread
+void *shadow_sp_stack[kMaxShadowIndex];
+
+enum Status {UNINITIALIZED = -1, DISABLED, ENABLED};
+Status status = UNINITIALIZED;
+
+void init() {
+ if (!getenv("KEEP_SHADOW_STACKS")) {
+ status = DISABLED;
+ return;
+ }
+ status = ENABLED;
+}
+
+void __cyg_profile_func_enter(void *this_fn, void *call_site) {
+ if (status == DISABLED) return;
+ if (status == UNINITIALIZED) {
+ init();
+ if (status == DISABLED) return;
+ }
+ shadow_index++;
+ if (shadow_index > kMaxShadowIndex) {
+ // Avoid memory allocation when reporting an error.
+ write(2, kOverflowMessage, sizeof(kOverflowMessage));
+ int a = 0;
+ a = a / a;
+ }
+ // Update the shadow IP stack
+ shadow_ip_stack[shadow_index] = this_fn;
+ // Update the shadow SP stack. The code for obtaining the frame address was
+ // borrowed from Google Perftools, http://code.google.com/p/google-perftools/
+ //
+ // Copyright (c) 2005, Google Inc.
+ // All rights reserved.
+ //
+ // Redistribution and use in source and binary forms, with or without
+ // modification, are permitted provided that the following conditions are
+ // met:
+ //
+ // * Redistributions of source code must retain the above copyright
+ // notice, this list of conditions and the following disclaimer.
+ // * Redistributions in binary form must reproduce the above
+ // copyright notice, this list of conditions and the following disclaimer
+ // in the documentation and/or other materials provided with the
+ // distribution.
+ // * Neither the name of Google Inc. nor the names of its
+ // contributors may be used to endorse or promote products derived from
+ // this software without specific prior written permission.
+ //
+ // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ void **sp;
+#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2) || __llvm__
+ // __builtin_frame_address(0) can return the wrong address on gcc-4.1.0-k8.
+ // It's always correct on llvm, and the techniques below aren't (in
+ // particular, llvm-gcc will make a copy of this_fn, so it's not in sp[2]),
+ // so we also prefer __builtin_frame_address when running under llvm.
+ sp = reinterpret_cast<void**>(__builtin_frame_address(0));
+#elif defined(__i386__)
+ // Stack frame format:
+ // sp[0] pointer to previous frame
+ // sp[1] caller address
+ // sp[2] first argument
+ // ...
+ // NOTE: This will break under llvm, since result is a copy and not in sp[2]
+ sp = (void **)&this_fn - 2;
+#elif defined(__x86_64__)
+ unsigned long rbp;
+ // Move the value of the register %rbp into the local variable rbp.
+ // We need 'volatile' to prevent this instruction from getting moved
+ // around during optimization to before function prologue is done.
+ // An alternative way to achieve this
+ // would be (before this __asm__ instruction) to call Noop() defined as
+ // static void Noop() __attribute__ ((noinline)); // prevent inlining
+ // static void Noop() { asm(""); } // prevent optimizing-away
+ __asm__ volatile ("mov %%rbp, %0" : "=r" (rbp));
+ // Arguments are passed in registers on x86-64, so we can't just
+ // offset from &result
+ sp = (void **) rbp;
+#else
+# error Cannot obtain SP (possibly compiling on a non x86 architecture)
+#endif
+ shadow_sp_stack[shadow_index] = (void*)sp;
+ return;
+}
+
+void __cyg_profile_func_exit(void *this_fn, void *call_site) {
+ if (status == DISABLED) return;
+ shadow_index--;
+}
+
+void *get_shadow_ip_stack(int *index /*OUT*/) {
+ *index = shadow_index;
+ return shadow_ip_stack;
+}
+
+void *get_shadow_sp_stack(int *index /*OUT*/) {
+ *index = shadow_index;
+ return shadow_sp_stack;
+}
diff --git a/third_party/tcmalloc/chromium/src/linux_shadow_stacks.h b/third_party/tcmalloc/chromium/src/linux_shadow_stacks.h
new file mode 100644
index 0000000..e519d29
--- /dev/null
+++ b/third_party/tcmalloc/chromium/src/linux_shadow_stacks.h
@@ -0,0 +1,20 @@
+// Copyright (c) 2006-2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef THIRD_PARTY_TCMALLOC_CHROMIUM_SRC_LINUX_SHADOW_STACKS_H__
+#define THIRD_PARTY_TCMALLOC_CHROMIUM_SRC_LINUX_SHADOW_STACKS_H__
+
+#define NO_INSTRUMENT __attribute__((no_instrument_function))
+
+extern "C" {
+void init() NO_INSTRUMENT;
+void __cyg_profile_func_enter(void *this_fn, void *call_site) NO_INSTRUMENT;
+void __cyg_profile_func_exit(void *this_fn, void *call_site) NO_INSTRUMENT;
+void *get_shadow_ip_stack(int *index /*OUT*/) NO_INSTRUMENT;
+void *get_shadow_sp_stack(int *index /*OUT*/) NO_INSTRUMENT;
+}
+
+#undef NO_INSTRUMENT
+
+#endif // THIRD_PARTY_TCMALLOC_CHROMIUM_SRC_LINUX_SHADOW_STACKS_H__
diff --git a/third_party/tcmalloc/chromium/src/stacktrace_x86-inl.h b/third_party/tcmalloc/chromium/src/stacktrace_x86-inl.h
index a140ab6..0f8c4de 100644
--- a/third_party/tcmalloc/chromium/src/stacktrace_x86-inl.h
+++ b/third_party/tcmalloc/chromium/src/stacktrace_x86-inl.h
@@ -65,6 +65,9 @@ typedef ucontext ucontext_t;
#endif
#include "google/stacktrace.h"
+#if defined(KEEP_SHADOW_STACKS)
+#include "linux_shadow_stacks.h"
+#endif // KEEP_SHADOW_STACKS
#if defined(__linux__) && defined(__i386__) && defined(__ELF__) && defined(HAVE_MMAP)
// Count "push %reg" instructions in VDSO __kernel_vsyscall(),
@@ -316,6 +319,21 @@ int GET_STACK_TRACE_OR_FRAMES {
#endif
int n = 0;
+#if defined(KEEP_SHADOW_STACKS)
+ void **shadow_ip_stack;
+ void **shadow_sp_stack;
+ int stack_size;
+ shadow_ip_stack = (void**) get_shadow_ip_stack(&stack_size);
+ shadow_sp_stack = (void**) get_shadow_sp_stack(&stack_size);
+ int shadow_index = stack_size - 1;
+ for (int i = stack_size - 1; i >= 0; i--) {
+ if (sp == shadow_sp_stack[i]) {
+ shadow_index = i;
+ break;
+ }
+ }
+ void **prev_sp = NULL;
+#endif // KEEP_SHADOW_STACKS
while (sp && n < max_depth) {
if (*(sp+1) == reinterpret_cast<void *>(0)) {
// In 64-bit code, we often see a frame that
@@ -328,8 +346,17 @@ int GET_STACK_TRACE_OR_FRAMES {
void **next_sp = NextStackFrame<!IS_STACK_FRAMES, IS_WITH_CONTEXT>(sp, ucp);
if (skip_count > 0) {
skip_count--;
+#if defined(KEEP_SHADOW_STACKS)
+ shadow_index--;
+#endif // KEEP_SHADOW_STACKS
} else {
result[n] = *(sp+1);
+#if defined(KEEP_SHADOW_STACKS)
+ if ((shadow_index > 0) && (sp == shadow_sp_stack[shadow_index])) {
+ shadow_index--;
+ }
+#endif // KEEP_SHADOW_STACKS
+
#if IS_STACK_FRAMES
if (next_sp > sp) {
sizes[n] = (uintptr_t)next_sp - (uintptr_t)sp;
@@ -340,7 +367,25 @@ int GET_STACK_TRACE_OR_FRAMES {
#endif
n++;
}
+#if defined(KEEP_SHADOW_STACKS)
+ prev_sp = sp;
+#endif // KEEP_SHADOW_STACKS
sp = next_sp;
}
+
+#if defined(KEEP_SHADOW_STACKS)
+ if (shadow_index >= 0) {
+ for (int i = shadow_index; i >= 0; i--) {
+ if (shadow_sp_stack[i] > prev_sp) {
+ result[n] = shadow_ip_stack[i];
+ if (n + 1 < max_depth) {
+ n++;
+ continue;
+ }
+ }
+ break;
+ }
+ }
+#endif // KEEP_SHADOW_STACKS
return n;
}