Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(329)

Side by Side Diff: third_party/tcmalloc/malloc_hook.cc

Issue 340065: Upgrade google-perftools to revision 77... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 11 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « third_party/tcmalloc/config_win.h ('k') | third_party/tcmalloc/tcmalloc.gyp » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2005, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 // ---
31 // Author: Sanjay Ghemawat <opensource@google.com>
32
33 #include <config.h>
34
35 // Disable the glibc prototype of mremap(), as older versions of the
36 // system headers define this function with only four arguments,
37 // whereas newer versions allow an optional fifth argument:
38 #ifdef HAVE_MMAP
39 # define mremap glibc_mremap
40 # include <sys/mman.h>
41 # undef mremap
42 #endif
43
44 #include <algorithm>
45 #include "base/basictypes.h"
46 #include "base/logging.h"
47 #include "malloc_hook-inl.h"
48 #include <google/malloc_hook.h>
49
50 // This #ifdef should almost never be set. Set NO_TCMALLOC_SAMPLES if
51 // you're porting to a system where you really can't get a stacktrace.
52 #ifdef NO_TCMALLOC_SAMPLES
53 // We use #define so code compiles even if you #include stacktrace.h somehow.
54 # define GetStackTrace(stack, depth, skip) (0)
55 #else
56 # include <google/stacktrace.h>
57 #endif
58
59 // __THROW is defined in glibc systems. It means, counter-intuitively,
60 // "This function will never throw an exception." It's an optional
61 // optimization tool, but we may need to use it to match glibc prototypes.
62 #ifndef __THROW // I guess we're not on a glibc system
63 # define __THROW // __THROW is just an optimization, so ok to make it ""
64 #endif
65
66 using std::copy;
67
68
69 // Declarations of three default weak hook functions, that can be overridden by
70 // linking-in a strong definition (as heap-checker.cc does)
71 //
72 // These default hooks let some other library we link in
73 // to define strong versions of InitialMallocHook_New, InitialMallocHook_MMap,
74 // InitialMallocHook_PreMMap, InitialMallocHook_PreSbrk, and
75 // InitialMallocHook_Sbrk to have a chance to hook into the very first
76 // invocation of an allocation function call, mmap, or sbrk.
77 //
78 // These functions are declared here as weak, and defined later, rather than a
79 // more straightforward simple weak definition, as a workround for an icc
80 // compiler issue ((Intel reference 290819). This issue causes icc to resolve
81 // weak symbols too early, at compile rather than link time. By declaring it
82 // (weak) here, then defining it below after its use, we can avoid the problem.
83 //
84 ATTRIBUTE_WEAK
85 extern void InitialMallocHook_New(const void* ptr, size_t size);
86
87 ATTRIBUTE_WEAK
88 extern void InitialMallocHook_PreMMap(const void* start,
89 size_t size,
90 int protection,
91 int flags,
92 int fd,
93 off_t offset);
94
95 ATTRIBUTE_WEAK
96 extern void InitialMallocHook_MMap(const void* result,
97 const void* start,
98 size_t size,
99 int protection,
100 int flags,
101 int fd,
102 off_t offset);
103
104 ATTRIBUTE_WEAK
105 extern void InitialMallocHook_PreSbrk(ptrdiff_t increment);
106
107 ATTRIBUTE_WEAK
108 extern void InitialMallocHook_Sbrk(const void* result, ptrdiff_t increment);
109
110 namespace base { namespace internal {
111 template<typename PtrT>
112 PtrT AtomicPtr<PtrT>::Exchange(PtrT new_val) {
113 base::subtle::MemoryBarrier(); // Release semantics.
114 // Depending on the system, NoBarrier_AtomicExchange(AtomicWord*)
115 // may have been defined to return an AtomicWord, Atomic32, or
116 // Atomic64. We hide that implementation detail here with an
117 // explicit cast. This prevents MSVC 2005, at least, from complaining.
118 PtrT old_val = reinterpret_cast<PtrT>(static_cast<AtomicWord>(
119 base::subtle::NoBarrier_AtomicExchange(
120 &data_,
121 reinterpret_cast<AtomicWord>(new_val))));
122 base::subtle::MemoryBarrier(); // And acquire semantics.
123 return old_val;
124 }
125
126 AtomicPtr<MallocHook::NewHook> new_hook_ = {
127 reinterpret_cast<AtomicWord>(InitialMallocHook_New) };
128 AtomicPtr<MallocHook::DeleteHook> delete_hook_ = { 0 };
129 AtomicPtr<MallocHook::PreMmapHook> premmap_hook_ = {
130 reinterpret_cast<AtomicWord>(InitialMallocHook_PreMMap) };
131 AtomicPtr<MallocHook::MmapHook> mmap_hook_ = {
132 reinterpret_cast<AtomicWord>(InitialMallocHook_MMap) };
133 AtomicPtr<MallocHook::MunmapHook> munmap_hook_ = { 0 };
134 AtomicPtr<MallocHook::MremapHook> mremap_hook_ = { 0 };
135 AtomicPtr<MallocHook::PreSbrkHook> presbrk_hook_ = {
136 reinterpret_cast<AtomicWord>(InitialMallocHook_PreSbrk) };
137 AtomicPtr<MallocHook::SbrkHook> sbrk_hook_ = {
138 reinterpret_cast<AtomicWord>(InitialMallocHook_Sbrk) };
139
140 } } // namespace base::internal
141
142 using base::internal::new_hook_;
143 using base::internal::delete_hook_;
144 using base::internal::premmap_hook_;
145 using base::internal::mmap_hook_;
146 using base::internal::munmap_hook_;
147 using base::internal::mremap_hook_;
148 using base::internal::presbrk_hook_;
149 using base::internal::sbrk_hook_;
150
151
152 // These are available as C bindings as well as C++, hence their
153 // definition outside the MallocHook class.
154 extern "C"
155 MallocHook_NewHook MallocHook_SetNewHook(MallocHook_NewHook hook) {
156 return new_hook_.Exchange(hook);
157 }
158
159 extern "C"
160 MallocHook_DeleteHook MallocHook_SetDeleteHook(MallocHook_DeleteHook hook) {
161 return delete_hook_.Exchange(hook);
162 }
163
164 extern "C"
165 MallocHook_PreMmapHook MallocHook_SetPreMmapHook(MallocHook_PreMmapHook hook) {
166 return premmap_hook_.Exchange(hook);
167 }
168
169 extern "C"
170 MallocHook_MmapHook MallocHook_SetMmapHook(MallocHook_MmapHook hook) {
171 return mmap_hook_.Exchange(hook);
172 }
173
174 extern "C"
175 MallocHook_MunmapHook MallocHook_SetMunmapHook(MallocHook_MunmapHook hook) {
176 return munmap_hook_.Exchange(hook);
177 }
178
179 extern "C"
180 MallocHook_MremapHook MallocHook_SetMremapHook(MallocHook_MremapHook hook) {
181 return mremap_hook_.Exchange(hook);
182 }
183
184 extern "C"
185 MallocHook_PreSbrkHook MallocHook_SetPreSbrkHook(MallocHook_PreSbrkHook hook) {
186 return presbrk_hook_.Exchange(hook);
187 }
188
189 extern "C"
190 MallocHook_SbrkHook MallocHook_SetSbrkHook(MallocHook_SbrkHook hook) {
191 return sbrk_hook_.Exchange(hook);
192 }
193
194
195 // The definitions of weak default malloc hooks (New, MMap, and Sbrk)
196 // that self deinstall on their first call. This is entirely for
197 // efficiency: the default version of these functions will be called a
198 // maximum of one time. If these functions were a no-op instead, they'd
199 // be called every time, costing an extra function call per malloc.
200 //
201 // However, this 'delete self' isn't safe in general -- it's possible
202 // that this function will be called via a daisy chain. That is,
203 // someone else might do
204 // old_hook = MallocHook::SetNewHook(&myhook);
205 // void myhook(void* ptr, size_t size) {
206 // do_my_stuff();
207 // old_hook(ptr, size); // daisy-chain the hooks
208 // }
209 // If old_hook is InitialMallocHook_New(), then this is broken code! --
210 // after the first run it'll deregister not only InitialMallocHook_New()
211 // but also myhook. To protect against that, InitialMallocHook_New()
212 // makes sure it's the 'top-level' hook before doing the deregistration.
213 // This means the daisy-chain case will be less efficient because the
214 // hook will be called, and do an if check, for every new. Alas.
215 // TODO(csilvers): add support for removing a hook from the middle of a chain.
216
217 void InitialMallocHook_New(const void* ptr, size_t size) {
218 if (MallocHook::GetNewHook() == &InitialMallocHook_New)
219 MallocHook::SetNewHook(NULL);
220 }
221
222 void InitialMallocHook_PreMMap(const void* start,
223 size_t size,
224 int protection,
225 int flags,
226 int fd,
227 off_t offset) {
228 if (MallocHook::GetPreMmapHook() == &InitialMallocHook_PreMMap)
229 MallocHook::SetPreMmapHook(NULL);
230 }
231
232 void InitialMallocHook_MMap(const void* result,
233 const void* start,
234 size_t size,
235 int protection,
236 int flags,
237 int fd,
238 off_t offset) {
239 if (MallocHook::GetMmapHook() == &InitialMallocHook_MMap)
240 MallocHook::SetMmapHook(NULL);
241 }
242
243 void InitialMallocHook_PreSbrk(ptrdiff_t increment) {
244 if (MallocHook::GetPreSbrkHook() == &InitialMallocHook_PreSbrk)
245 MallocHook::SetPreSbrkHook(NULL);
246 }
247
248 void InitialMallocHook_Sbrk(const void* result, ptrdiff_t increment) {
249 if (MallocHook::GetSbrkHook() == &InitialMallocHook_Sbrk)
250 MallocHook::SetSbrkHook(NULL);
251 }
252
253 DEFINE_ATTRIBUTE_SECTION_VARS(google_malloc);
254 DECLARE_ATTRIBUTE_SECTION_VARS(google_malloc);
255 // actual functions are in debugallocation.cc or tcmalloc.cc
256 DEFINE_ATTRIBUTE_SECTION_VARS(malloc_hook);
257 DECLARE_ATTRIBUTE_SECTION_VARS(malloc_hook);
258 // actual functions are in this file, malloc_hook.cc, and low_level_alloc.cc
259
260 #define ADDR_IN_ATTRIBUTE_SECTION(addr, name) \
261 (reinterpret_cast<uintptr_t>(ATTRIBUTE_SECTION_START(name)) <= \
262 reinterpret_cast<uintptr_t>(addr) && \
263 reinterpret_cast<uintptr_t>(addr) < \
264 reinterpret_cast<uintptr_t>(ATTRIBUTE_SECTION_STOP(name)))
265
266 // Return true iff 'caller' is a return address within a function
267 // that calls one of our hooks via MallocHook:Invoke*.
268 // A helper for GetCallerStackTrace.
269 static inline bool InHookCaller(const void* caller) {
270 return ADDR_IN_ATTRIBUTE_SECTION(caller, google_malloc) ||
271 ADDR_IN_ATTRIBUTE_SECTION(caller, malloc_hook);
272 // We can use one section for everything except tcmalloc_or_debug
273 // due to its special linkage mode, which prevents merging of the sections.
274 }
275
276 #undef ADDR_IN_ATTRIBUTE_SECTION
277
278 static bool checked_sections = false;
279
280 static inline void CheckInHookCaller() {
281 if (!checked_sections) {
282 INIT_ATTRIBUTE_SECTION_VARS(google_malloc);
283 if (ATTRIBUTE_SECTION_START(google_malloc) ==
284 ATTRIBUTE_SECTION_STOP(google_malloc)) {
285 RAW_LOG(ERROR, "google_malloc section is missing, "
286 "thus InHookCaller is broken!");
287 }
288 INIT_ATTRIBUTE_SECTION_VARS(malloc_hook);
289 if (ATTRIBUTE_SECTION_START(malloc_hook) ==
290 ATTRIBUTE_SECTION_STOP(malloc_hook)) {
291 RAW_LOG(ERROR, "malloc_hook section is missing, "
292 "thus InHookCaller is broken!");
293 }
294 checked_sections = true;
295 }
296 }
297
298 // We can improve behavior/compactness of this function
299 // if we pass a generic test function (with a generic arg)
300 // into the implementations for GetStackTrace instead of the skip_count.
301 extern "C" int MallocHook_GetCallerStackTrace(void** result, int max_depth,
302 int skip_count) {
303 #if defined(NO_TCMALLOC_SAMPLES)
304 return 0;
305 #elif !defined(HAVE_ATTRIBUTE_SECTION_START)
306 // Fall back to GetStackTrace and good old but fragile frame skip counts.
307 // Note: this path is inaccurate when a hook is not called directly by an
308 // allocation function but is daisy-chained through another hook,
309 // search for MallocHook::(Get|Set|Invoke)* to find such cases.
310 return GetStackTrace(result, max_depth, skip_count + int(DEBUG_MODE));
311 // due to -foptimize-sibling-calls in opt mode
312 // there's no need for extra frame skip here then
313 #else
314 CheckInHookCaller();
315 // MallocHook caller determination via InHookCaller works, use it:
316 static const int kMaxSkip = 32 + 6 + 3;
317 // Constant tuned to do just one GetStackTrace call below in practice
318 // and not get many frames that we don't actually need:
319 // currently max passsed max_depth is 32,
320 // max passed/needed skip_count is 6
321 // and 3 is to account for some hook daisy chaining.
322 static const int kStackSize = kMaxSkip + 1;
323 void* stack[kStackSize];
324 int depth = GetStackTrace(stack, kStackSize, 1); // skip this function frame
325 if (depth == 0) // silenty propagate cases when GetStackTrace does not work
326 return 0;
327 for (int i = 0; i < depth; ++i) { // stack[0] is our immediate caller
328 if (InHookCaller(stack[i])) {
329 RAW_VLOG(4, "Found hooked allocator at %d: %p <- %p",
330 i, stack[i], stack[i+1]);
331 i += 1; // skip hook caller frame
332 depth -= i; // correct depth
333 if (depth > max_depth) depth = max_depth;
334 copy(stack + i, stack + i + depth, result);
335 if (depth < max_depth && depth + i == kStackSize) {
336 // get frames for the missing depth
337 depth +=
338 GetStackTrace(result + depth, max_depth - depth, 1 + kStackSize);
339 }
340 return depth;
341 }
342 }
343 RAW_LOG(WARNING, "Hooked allocator frame not found, returning empty trace");
344 // If this happens try increasing kMaxSkip
345 // or else something must be wrong with InHookCaller,
346 // e.g. for every section used in InHookCaller
347 // all functions in that section must be inside the same library.
348 return 0;
349 #endif
350 }
351
352 // On Linux/x86, we override mmap/munmap/mremap/sbrk
353 // and provide support for calling the related hooks.
354 //
355 // We define mmap() and mmap64(), which somewhat reimplements libc's mmap
356 // syscall stubs. Unfortunately libc only exports the stubs via weak symbols
357 // (which we're overriding with our mmap64() and mmap() wrappers) so we can't
358 // just call through to them.
359
360
361 #if defined(__linux) && \
362 (defined(__i386__) || defined(__x86_64__) || defined(__PPC__))
363 #include <unistd.h>
364 #include <syscall.h>
365 #include <sys/mman.h>
366 #include <errno.h>
367 #include "base/linux_syscall_support.h"
368
369 // The x86-32 case and the x86-64 case differ:
370 // 32b has a mmap2() syscall, 64b does not.
371 // 64b and 32b have different calling conventions for mmap().
372 #if defined(__x86_64__) || defined(__PPC64__)
373
374 static inline void* do_mmap64(void *start, size_t length,
375 int prot, int flags,
376 int fd, __off64_t offset) __THROW {
377 return (void *)syscall(SYS_mmap, start, length, prot, flags, fd, offset);
378 }
379
380 #elif defined(__i386__) || defined(__PPC__)
381
382 static inline void* do_mmap64(void *start, size_t length,
383 int prot, int flags,
384 int fd, __off64_t offset) __THROW {
385 void *result;
386
387 // Try mmap2() unless it's not supported
388 static bool have_mmap2 = true;
389 if (have_mmap2) {
390 static int pagesize = 0;
391 if (!pagesize) pagesize = getpagesize();
392
393 // Check that the offset is page aligned
394 if (offset & (pagesize - 1)) {
395 result = MAP_FAILED;
396 errno = EINVAL;
397 goto out;
398 }
399
400 result = (void *)syscall(SYS_mmap2,
401 start, length, prot, flags, fd, offset / pagesize);
402 if (result != MAP_FAILED || errno != ENOSYS) goto out;
403
404 // We don't have mmap2() after all - don't bother trying it in future
405 have_mmap2 = false;
406 }
407
408 if (((off_t)offset) != offset) {
409 // If we're trying to map a 64-bit offset, fail now since we don't
410 // have 64-bit mmap() support.
411 result = MAP_FAILED;
412 errno = EINVAL;
413 goto out;
414 }
415
416 {
417 // Fall back to old 32-bit offset mmap() call
418 // Old syscall interface cannot handle six args, so pass in an array
419 int32 args[6] = { (int32) start, length, prot, flags, fd, (off_t) offset };
420 result = (void *)syscall(SYS_mmap, args);
421 }
422 out:
423 return result;
424 }
425
426 # endif
427
428 #undef mmap
429
430 // We use do_mmap64 abstraction to put MallocHook::InvokeMmapHook
431 // calls right into mmap and mmap64, so that the stack frames in the caller's
432 // stack are at the same offsets for all the calls of memory allocating
433 // functions.
434
435 // Put all callers of MallocHook::Invoke* in this module into
436 // malloc_hook section,
437 // so that MallocHook::GetCallerStackTrace can function accurately:
438 extern "C" {
439 void* mmap64(void *start, size_t length, int prot, int flags,
440 int fd, __off64_t offset ) __THROW
441 ATTRIBUTE_SECTION(malloc_hook);
442 void* mmap(void *start, size_t length,int prot, int flags,
443 int fd, off_t offset) __THROW
444 ATTRIBUTE_SECTION(malloc_hook);
445 int munmap(void* start, size_t length) __THROW
446 ATTRIBUTE_SECTION(malloc_hook);
447 void* mremap(void* old_addr, size_t old_size, size_t new_size,
448 int flags, ...) __THROW
449 ATTRIBUTE_SECTION(malloc_hook);
450 void* sbrk(ptrdiff_t increment) __THROW
451 ATTRIBUTE_SECTION(malloc_hook);
452 }
453
454 extern "C" void* mmap64(void *start, size_t length, int prot, int flags,
455 int fd, __off64_t offset) __THROW {
456 MallocHook::InvokePreMmapHook(start, length, prot, flags, fd, offset);
457 void *result = do_mmap64(start, length, prot, flags, fd, offset);
458 MallocHook::InvokeMmapHook(result, start, length, prot, flags, fd, offset);
459 return result;
460 }
461
462 #if !defined(__USE_FILE_OFFSET64) || !defined(__REDIRECT_NTH)
463
464 extern "C" void* mmap(void *start, size_t length, int prot, int flags,
465 int fd, off_t offset) __THROW {
466 MallocHook::InvokePreMmapHook(start, length, prot, flags, fd, offset);
467 void *result = do_mmap64(start, length, prot, flags, fd,
468 static_cast<size_t>(offset)); // avoid sign extension
469 MallocHook::InvokeMmapHook(result, start, length, prot, flags, fd, offset);
470 return result;
471 }
472
473 #endif
474
475 extern "C" int munmap(void* start, size_t length) __THROW {
476 MallocHook::InvokeMunmapHook(start, length);
477 return syscall(SYS_munmap, start, length);
478 }
479
480 extern "C" void* mremap(void* old_addr, size_t old_size, size_t new_size,
481 int flags, ...) __THROW {
482 va_list ap;
483 va_start(ap, flags);
484 void *new_address = va_arg(ap, void *);
485 va_end(ap);
486 void* result = sys_mremap(old_addr, old_size, new_size, flags, new_address);
487 MallocHook::InvokeMremapHook(result, old_addr, old_size, new_size, flags,
488 new_address);
489 return result;
490 }
491
492 // libc's version:
493 extern "C" void* __sbrk(ptrdiff_t increment);
494
495 extern "C" void* sbrk(ptrdiff_t increment) __THROW {
496 MallocHook::InvokePreSbrkHook(increment);
497 void *result = __sbrk(increment);
498 MallocHook::InvokeSbrkHook(result, increment);
499 return result;
500 }
501
502 #endif
OLDNEW
« no previous file with comments | « third_party/tcmalloc/config_win.h ('k') | third_party/tcmalloc/tcmalloc.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698