diff options
Diffstat (limited to 'o3d/plugin/cross/out_of_memory.cc')
-rw-r--r-- | o3d/plugin/cross/out_of_memory.cc | 231 |
1 files changed, 231 insertions, 0 deletions
diff --git a/o3d/plugin/cross/out_of_memory.cc b/o3d/plugin/cross/out_of_memory.cc new file mode 100644 index 0000000..47937f1 --- /dev/null +++ b/o3d/plugin/cross/out_of_memory.cc @@ -0,0 +1,231 @@ +/* + * Copyright 2009, 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. + */ + + +// This file implements a failure handler for the new +// operator and malloc. + +// TODO: This does not currently work on linux. The replacement +// operator new, malloc, etc do not take priority over those declared in +// the standard libraries. + +#include <stdio.h> +#include <stdlib.h> +#include <wchar.h> +#include <build/build_config.h> + +#ifdef _MSC_VER +#include <new.h> +#endif + +#ifdef OS_WIN +#include <windows.h> +#endif + +#if defined(OS_MACOSX) || defined(OS_LINUX) +#include <dlfcn.h> +#endif + +#include "plugin/cross/out_of_memory.h" +#include "plugin/cross/plugin_metrics.h" + +#ifdef _MSC_VER +extern "C" { + void* _getptd(); +} // _MSC_VER +#endif + +namespace o3d { +namespace { +// The reserve is allocated just after the plugin starts. +// In the event that an allocation fails, the reserve is +// freed, hopefully freeing enough memory to allow any code +// run after abort() to do its work. +const size_t kReserveSize = 1024 * 256; +void* g_reserve; +} // namespace anonymous + +// This is called when a memory allocation fails in the plugin. Note +// that it will not be called if a memory allocation fails in another +// shared library, such as in the c runtime library on platforms where +// we use a shared c runtime library. In those cases, we have to hope +// that the allocation failed because new was called (in which case an +// exception will be thrown but the reserve will not be freed) or that +// the calling library correctly checks for a NULL return and does +// something appropriate. +int HandleOutOfMemory(size_t size) { + if (g_reserve != NULL) { + // First time round, free the reserve and exit with abort. + // This should allow some crash reporting before the process + // exits.signal + free(g_reserve); + g_reserve = NULL; + + // Do this on MacOSX and Linux when they support metrics. + // Also, at the time of writing, the matrics logging is not + // hooked up to breakpad, so this metric will not get logged + // anywhere! Remove this comment when that is done and tested. +#ifdef OS_WIN + ++metric_out_of_memory_total; +#endif // OS_MACOSX + + fprintf(stderr, "Aborting: out of memory allocating %lu bytes\n", + static_cast<unsigned long>(size)); + +#ifdef OS_WIN + // This is different on Windows so that it is comnpatible with + // the way that breakpad works. On Windows, it intercepts + // exceptions. On unixy platforms, it handles signals. Also, + // on Windows, this is friendlier to the browser's own crash + // logging (for browsers that log crashes). + RaiseException(ERROR_OUTOFMEMORY, + EXCEPTION_NONCONTINUABLE_EXCEPTION, + 0, NULL); +#else // OS_WIN + abort(); +#endif // OS_WIN + } else { + // If the handler is reentered, try to exit without raising + // SIGABRT (or executing exit handlers). + _exit(EXIT_FAILURE); + } + return 0; +} + +bool SetupOutOfMemoryHandler() { +#ifdef _MSC_VER + // Workaround for MSVC. Sometimes the runtime calls this when + // there is not enough memory to allocate the "per-thread + // data structure". Calling this forces the "per-thread data + // structure" to be allocated. + _getptd(); + + // This causes malloc failures to call the new handler as well. + // It is not necessary to replace the implementations of malloc, + // etc under MSVC. + _set_new_mode(1); + _set_new_handler(HandleOutOfMemory); +#endif // _MSC_VER + + g_reserve = malloc(kReserveSize); + + return true; +} +} // namespace o3d + +#if defined(OS_MACOSX) || defined(OS_LINUX) +namespace { +void* dlsym_helper(const char* symbol_name) { + void* ptr = dlsym(RTLD_NEXT, symbol_name); + if (ptr == NULL) { + fprintf(stderr, "Error: could not locate symbol \"%s\"\n", symbol_name); + abort(); + } + return ptr; +} +} // namespace anonymous + +void* operator new(size_t size) { + return malloc(size); +} + +void operator delete(void* ptr) { + free(ptr); +} + +extern "C" { +void *malloc(size_t size) { + typedef void* (*Func)(size_t); + static Func func = (Func) dlsym_helper("malloc"); + void* ptr = func(size); + if (ptr == NULL) + o3d::HandleOutOfMemory(size); + return ptr; +} + +void *realloc(void* old_ptr, size_t new_size) { + typedef void* (*Func)(void*, size_t); + static Func func = reinterpret_cast<Func>(dlsym_helper("realloc")); + void* ptr = func(old_ptr, new_size); + + // realloc() returns NULL when you ask for zero bytes. This differs + // from malloc() which returns a pointer to a zero sized allocated block. + if (new_size != 0 && ptr == NULL) + o3d::HandleOutOfMemory(new_size); + return ptr; +} + +void *calloc(size_t num_items, size_t size) { + typedef void* (*Func)(size_t, size_t); + static Func func = reinterpret_cast<Func>(dlsym_helper("calloc")); + void* ptr = func(num_items, size); + if (ptr == NULL) + o3d::HandleOutOfMemory(size); + return ptr; +} + +void *valloc(size_t size) { + typedef void* (*Func)(size_t); + static Func func = reinterpret_cast<Func>(dlsym_helper("valloc")); + void* ptr = func(size); + if (ptr == NULL) + o3d::HandleOutOfMemory(size); + return ptr; +} + +void* memalign(size_t alignment, size_t size) { + typedef void* (*Func)(size_t, size_t); + static Func func = reinterpret_cast<Func>(dlsym_helper("memalign")); + void* ptr = func(alignment, size); + if (ptr == NULL) + o3d::HandleOutOfMemory(size); + return ptr; +} + +char* strdup(const char* ptr) { + typedef char* (*Func)(const char*); + static Func func = reinterpret_cast<Func>(dlsym_helper("strdup")); + char* result = func(ptr); + if (ptr != NULL && result == NULL) + o3d::HandleOutOfMemory((strlen(ptr) + 1) * sizeof(char)); + return result; +} + +wchar_t* wcsdup(const wchar_t* ptr) { + typedef wchar_t* (*Func)(const wchar_t*); + static Func func = reinterpret_cast<Func>(dlsym_helper("wcsdup")); + wchar_t* result = func(ptr); + if (ptr != NULL && result == NULL) + o3d::HandleOutOfMemory((wcslen(ptr) + 1) * sizeof(wchar_t)); + return result; +} +} +#endif // defined(OS_MACOSX) || defined(OS_LINUX) |