| /* |
| * Copyright 2010 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| // Author: jmarantz@google.com (Joshua Marantz) |
| |
| // Very simple memory debugging overrides for operator new/delete, to |
| // help us quickly find simple memory violations: |
| // 1. Double destruct |
| // 2. Read before write (via scribbling) |
| // 3. Read after delete (via scribbling) |
| // |
| // Note that valgrind does all of this much better, but is too slow to |
| // run all the time, and it's not obvious how well it works when |
| // Apache is forking processes that do all the interesting work. However, |
| // if this code helps find a problem then you can run valgrind with Apache |
| // in -X mode and that will be a better debug tool. |
| // |
| // Principle of operation: add 8 bytes to every allocation. The first |
| // 4 bytes are a marker (kLiveMarker or kDeadMarker1). The next 4 |
| // bytes are used to store size of the allocation, which helps us |
| // know how many bytes to scribble when we free. |
| // |
| // TODO(jmarantz): consider integrating a richer memory debug library such |
| // as dmalloc, efence, or the heap-checking features of tcmalloc. |
| // |
| // Note: this memory debugging will interfere with Valgrind's ability to |
| // detect read-before-write errors, and hence should be disabled if you |
| // want to run with valgrind. This can be detected automatically using |
| // macros from valgrind.h, but then that would *require* valgrind be installed |
| // before building mod_pagespeed. See |
| // |
| // http://valgrind.org/docs/manual/manual-core-adv.html#manual-core-adv.clientreq |
| |
| // TODO(jmarantz): consider controlling inclusion of this module in the gyp |
| // files rather than as an ifdef. |
| // |
| // Note: to be assured that these overrides are included in Debug builds but |
| // are not included in Release builds, type: |
| // nm out/Debug/libmod_pagespeed.so | /usr/bin/c++filt |grep 'operator new' |
| // nm out/Release/libmod_pagespeed.so | /usr/bin/c++filt |grep 'operator new' |
| #ifndef NDEBUG |
| |
| #include <cstdlib> |
| #include "base/logging.h" |
| #include "pagespeed/kernel/base/basictypes.h" |
| |
| namespace { |
| |
| const int32 kLiveMarker = 0xfeedface; // first 4 bytes after alloc |
| const int32 kDeadMarker1 = 0xabacabff; // first 4 bytes after free |
| const int32 kDeadMarker2 = 0xdeadbeef; // overwrites the 'size' field on free |
| const size_t kOverhead = 2 * sizeof(int32); // number of extra bytes to alloc |
| |
| size_t rounded_size(size_t size) { |
| if (size == 0) { |
| size = kOverhead; |
| } else if ((size % kOverhead) != 0) { |
| size = size + kOverhead - (size % kOverhead); |
| } |
| return size; |
| } |
| |
| void scribble(void* ptr, size_t size, int32 scribble_word) { |
| CHECK_EQ(0U, size % sizeof(int32)); |
| int num_ints = size / sizeof(int32); |
| int* p = static_cast<int*>(ptr); |
| for (int i = 0; i < num_ints; ++i, ++p) { |
| *p = scribble_word; |
| } |
| } |
| |
| void* debug_malloc(size_t size) { |
| size_t rounded = rounded_size(size); |
| int32* marker = static_cast<int*>(malloc(rounded + kOverhead)); |
| CHECK(marker != NULL); |
| marker[0] = kLiveMarker; |
| marker[1] = size; |
| int32* ret = marker + 2; |
| scribble(ret, rounded, kLiveMarker); |
| return reinterpret_cast<char*>(marker) + kOverhead; |
| } |
| |
| void debug_free(void* ptr) { |
| if (ptr != NULL) { |
| char* alloced_ptr = static_cast<char*>(ptr) - kOverhead; |
| int32* marker = reinterpret_cast<int32*>(alloced_ptr); |
| scribble(ptr, rounded_size(marker[1]), kDeadMarker2); |
| CHECK_EQ(kLiveMarker, marker[0]); |
| marker[0] = kDeadMarker1; |
| marker[1] = kDeadMarker2; |
| free(marker); |
| } |
| } |
| |
| } // namespace |
| |
| // C++ operator new/delete overrides, in all 8 combinations: |
| // (new vs delete) * (const std::nothrow_t& vs not) * ([] vs not) |
| // On MacOS __THROW appears to be missing so hide those in an ifdef. |
| #ifndef __THROW |
| #define __THROW |
| #endif |
| |
| void* operator new(size_t size) throw (std::bad_alloc) { |
| return debug_malloc(size); |
| } |
| |
| void* operator new[](size_t size) throw (std::bad_alloc) { |
| return debug_malloc(size); |
| } |
| |
| void* operator new(size_t size, const std::nothrow_t&) __THROW { |
| return debug_malloc(size); |
| } |
| |
| void operator delete(void* ptr) __THROW { |
| debug_free(ptr); |
| } |
| |
| void operator delete(void* ptr, const std::nothrow_t&) __THROW { |
| debug_free(ptr); |
| } |
| |
| void* operator new[](size_t size, const std::nothrow_t&) __THROW { |
| return debug_malloc(size); |
| } |
| |
| void operator delete[](void* ptr) __THROW { |
| debug_free(ptr); |
| } |
| |
| void operator delete[](void* ptr, const std::nothrow_t&) __THROW { |
| debug_free(ptr); |
| } |
| |
| #endif // !NDEBUG |