// Copyright (c) 2012 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 #include #include #include "base/debug/alias.h" #include "base/process/memory.h" #include "build/build_config.h" #include "third_party/skia/include/core/SkTypes.h" #include "third_party/skia/include/private/SkMalloc.h" #if BUILDFLAG(IS_WIN) #include #endif // This implementation of sk_malloc_flags() and friends is similar to // SkMemory_malloc.cpp, except it uses base::UncheckedMalloc and friends // for non-SK_MALLOC_THROW calls. // // The name of this file is historic: a previous implementation tried to // use std::set_new_handler() for the same effect, but it didn't actually work. static inline void* throw_on_failure(size_t size, void* p) { if (size > 0 && p == NULL) { // If we've got a NULL here, the only reason we should have failed is running out of RAM. sk_out_of_memory(); } return p; } void sk_abort_no_print() { // Linker's ICF feature may merge this function with other functions with // the same definition (e.g. any function whose sole job is to call abort()) // and it may confuse the crash report processing system. // http://crbug.com/860850 static int static_variable_to_make_this_function_unique = 0x736b; // "sk" base::debug::Alias(&static_variable_to_make_this_function_unique); abort(); } void sk_out_of_memory(void) { SkASSERT(!"sk_out_of_memory"); base::TerminateBecauseOutOfMemory(0); // Extra safety abort(). abort(); } void* sk_realloc_throw(void* addr, size_t size) { return throw_on_failure(size, realloc(addr, size)); } void sk_free(void* p) { if (p) { free(p); } } // We get lots of bugs filed on us that amount to overcommiting bitmap memory, // then some time later failing to back that VM with physical memory. // They're hard to track down, so in Debug mode we touch all memory right up front. // // For malloc, fill is an arbitrary byte and ideally not 0. For calloc, it's got to be 0. static void* prevent_overcommit(int fill, size_t size, void* p) { // We probably only need to touch one byte per page, but memset makes things easy. SkDEBUGCODE(memset(p, fill, size)); return p; } static void* malloc_throw(size_t size) { return prevent_overcommit(0x42, size, throw_on_failure(size, malloc(size))); } static void* malloc_nothrow(size_t size) { // TODO(b.kelemen): we should always use UncheckedMalloc but currently it // doesn't work as intended everywhere. void* result; #if BUILDFLAG(IS_IOS) result = malloc(size); #else // It's the responsibility of the caller to check the return value. std::ignore = base::UncheckedMalloc(size, &result); #endif if (result) { prevent_overcommit(0x47, size, result); } return result; } static void* calloc_throw(size_t size) { return prevent_overcommit(0, size, throw_on_failure(size, calloc(size, 1))); } static void* calloc_nothrow(size_t size) { // TODO(b.kelemen): we should always use UncheckedCalloc but currently it // doesn't work as intended everywhere. void* result; #if BUILDFLAG(IS_IOS) result = calloc(1, size); #else // It's the responsibility of the caller to check the return value. std::ignore = base::UncheckedCalloc(size, 1, &result); #endif if (result) { prevent_overcommit(0, size, result); } return result; } void* sk_malloc_flags(size_t size, unsigned flags) { if (flags & SK_MALLOC_ZERO_INITIALIZE) { if (flags & SK_MALLOC_THROW) { return calloc_throw(size); } else { return calloc_nothrow(size); } } else { if (flags & SK_MALLOC_THROW) { return malloc_throw(size); } else { return malloc_nothrow(size); } } }