/* * 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 #include #include #include #ifdef _MSC_VER #include #endif #ifdef OS_WIN #include #endif #if defined(OS_MACOSX) || defined(OS_LINUX) #include #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(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(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(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(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(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(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(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)