diff options
Diffstat (limited to 'libs/utils')
-rw-r--r-- | libs/utils/Android.mk | 6 | ||||
-rw-r--r-- | libs/utils/BasicHashtable.cpp | 2 | ||||
-rw-r--r-- | libs/utils/CallStack.cpp | 15 | ||||
-rw-r--r-- | libs/utils/JenkinsHash.cpp | 64 | ||||
-rw-r--r-- | libs/utils/LinearAllocator.cpp | 227 | ||||
-rw-r--r-- | libs/utils/RefBase.cpp | 160 | ||||
-rw-r--r-- | libs/utils/Threads.cpp | 45 | ||||
-rw-r--r-- | libs/utils/Trace.cpp | 50 | ||||
-rw-r--r-- | libs/utils/VectorImpl.cpp | 10 | ||||
-rw-r--r-- | libs/utils/tests/Android.mk | 1 | ||||
-rw-r--r-- | libs/utils/tests/LruCache_test.cpp | 291 |
11 files changed, 743 insertions, 128 deletions
diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index c9f8fd4..cbfe7bd 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -25,6 +25,8 @@ commonSources:= \ Debug.cpp \ FileMap.cpp \ Flattenable.cpp \ + JenkinsHash.cpp \ + LinearAllocator.cpp \ LinearTransform.cpp \ Log.cpp \ PropertyMap.cpp \ @@ -111,6 +113,10 @@ ifeq ($(TARGET_OS),linux) LOCAL_LDLIBS += -lrt -ldl endif +ifeq ($(TARGET_ARCH),mips) +LOCAL_CFLAGS += -DALIGN_DOUBLE +endif + LOCAL_C_INCLUDES += \ bionic/libc/private \ external/zlib diff --git a/libs/utils/BasicHashtable.cpp b/libs/utils/BasicHashtable.cpp index fb8ec9f..fd51b7b 100644 --- a/libs/utils/BasicHashtable.cpp +++ b/libs/utils/BasicHashtable.cpp @@ -80,7 +80,7 @@ void BasicHashtableImpl::clear() { SharedBuffer* sb = SharedBuffer::bufferFromData(mBuckets); if (sb->onlyOwner()) { destroyBuckets(mBuckets, mBucketCount); - for (size_t i = 0; i < mSize; i++) { + for (size_t i = 0; i < mBucketCount; i++) { Bucket& bucket = bucketAt(mBuckets, i); bucket.cookie = 0; } diff --git a/libs/utils/CallStack.cpp b/libs/utils/CallStack.cpp index 18fd84f..e60f5d8 100644 --- a/libs/utils/CallStack.cpp +++ b/libs/utils/CallStack.cpp @@ -30,6 +30,11 @@ CallStack::CallStack() : mCount(0) { } +CallStack::CallStack(const char* logtag, int32_t ignoreDepth, int32_t maxDepth) { + this->update(ignoreDepth+1, maxDepth); + this->dump(logtag); +} + CallStack::CallStack(const CallStack& rhs) : mCount(rhs.mCount) { if (mCount) { @@ -96,7 +101,7 @@ void CallStack::update(int32_t ignoreDepth, int32_t maxDepth) { mCount = count > 0 ? count : 0; } -void CallStack::dump(const char* prefix) const { +void CallStack::dump(const char* logtag, const char* prefix) const { backtrace_symbol_t symbols[mCount]; get_backtrace_symbols(mStack, mCount, symbols); @@ -104,7 +109,9 @@ void CallStack::dump(const char* prefix) const { char line[MAX_BACKTRACE_LINE_LENGTH]; format_backtrace_line(i, &mStack[i], &symbols[i], line, MAX_BACKTRACE_LINE_LENGTH); - ALOGD("%s%s", prefix, line); + ALOG(LOG_DEBUG, logtag, "%s%s", + prefix ? prefix : "", + line); } free_backtrace_symbols(symbols, mCount); } @@ -118,7 +125,9 @@ String8 CallStack::toString(const char* prefix) const { char line[MAX_BACKTRACE_LINE_LENGTH]; format_backtrace_line(i, &mStack[i], &symbols[i], line, MAX_BACKTRACE_LINE_LENGTH); - str.append(prefix); + if (prefix) { + str.append(prefix); + } str.append(line); str.append("\n"); } diff --git a/libs/utils/JenkinsHash.cpp b/libs/utils/JenkinsHash.cpp new file mode 100644 index 0000000..52c9bb7 --- /dev/null +++ b/libs/utils/JenkinsHash.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * 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. + */ + +/* Implementation of Jenkins one-at-a-time hash function. These choices are + * optimized for code size and portability, rather than raw speed. But speed + * should still be quite good. + **/ + +#include <utils/JenkinsHash.h> + +namespace android { + +hash_t JenkinsHashWhiten(uint32_t hash) { + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return hash; +} + +uint32_t JenkinsHashMixBytes(uint32_t hash, const uint8_t* bytes, size_t size) { + hash = JenkinsHashMix(hash, (uint32_t)size); + size_t i; + for (i = 0; i < (size & -4); i += 4) { + uint32_t data = bytes[i] | (bytes[i+1] << 8) | (bytes[i+2] << 16) | (bytes[i+3] << 24); + hash = JenkinsHashMix(hash, data); + } + if (size & 3) { + uint32_t data = bytes[i]; + data |= ((size & 3) > 1) ? (bytes[i+1] << 8) : 0; + data |= ((size & 3) > 2) ? (bytes[i+2] << 16) : 0; + hash = JenkinsHashMix(hash, data); + } + return hash; +} + +uint32_t JenkinsHashMixShorts(uint32_t hash, const uint16_t* shorts, size_t size) { + hash = JenkinsHashMix(hash, (uint32_t)size); + size_t i; + for (i = 0; i < (size & -2); i += 2) { + uint32_t data = shorts[i] | (shorts[i+1] << 16); + hash = JenkinsHashMix(hash, data); + } + if (size & 1) { + uint32_t data = shorts[i]; + hash = JenkinsHashMix(hash, data); + } + return hash; +} + +} + diff --git a/libs/utils/LinearAllocator.cpp b/libs/utils/LinearAllocator.cpp new file mode 100644 index 0000000..a07a291 --- /dev/null +++ b/libs/utils/LinearAllocator.cpp @@ -0,0 +1,227 @@ +/* + * Copyright 2012, The Android Open Source Project + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``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. + */ + +#define LOG_TAG "LinearAllocator" +#define LOG_NDEBUG 1 + +#include <stdlib.h> +#include <utils/LinearAllocator.h> +#include <utils/Log.h> + + +// The ideal size of a page allocation (these need to be multiples of 8) +#define INITIAL_PAGE_SIZE ((size_t)4096) // 4kb +#define MAX_PAGE_SIZE ((size_t)131072) // 128kb + +// The maximum amount of wasted space we can have per page +// Allocations exceeding this will have their own dedicated page +// If this is too low, we will malloc too much +// Too high, and we may waste too much space +// Must be smaller than INITIAL_PAGE_SIZE +#define MAX_WASTE_SIZE ((size_t)1024) + +#if ALIGN_DOUBLE +#define ALIGN_SZ (sizeof(double)) +#else +#define ALIGN_SZ (sizeof(int)) +#endif + +#define ALIGN(x) ((x + ALIGN_SZ - 1 ) & ~(ALIGN_SZ - 1)) +#define ALIGN_PTR(p) ((void*)(ALIGN((size_t)p))) + +#if LOG_NDEBUG +#define ADD_ALLOCATION(size) +#define RM_ALLOCATION(size) +#else +#include <utils/Thread.h> +#include <utils/Timers.h> +static size_t s_totalAllocations = 0; +static nsecs_t s_nextLog = 0; +static android::Mutex s_mutex; + +static void _logUsageLocked() { + nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); + if (now > s_nextLog) { + s_nextLog = now + milliseconds_to_nanoseconds(10); + ALOGV("Total memory usage: %zu kb", s_totalAllocations / 1024); + } +} + +static void _addAllocation(size_t size) { + android::AutoMutex lock(s_mutex); + s_totalAllocations += size; + _logUsageLocked(); +} + +#define ADD_ALLOCATION(size) _addAllocation(size); +#define RM_ALLOCATION(size) _addAllocation(-size); +#endif + +#define min(x,y) (((x) < (y)) ? (x) : (y)) + +namespace android { + +class LinearAllocator::Page { +public: + Page* next() { return mNextPage; } + void setNext(Page* next) { mNextPage = next; } + + Page() + : mNextPage(0) + {} + + void* operator new(size_t size, void* buf) { return buf; } + + void* start() { + return (void*) (((size_t)this) + sizeof(Page)); + } + + void* end(int pageSize) { + return (void*) (((size_t)start()) + pageSize); + } + +private: + Page(const Page& other) {} + Page* mNextPage; +}; + +LinearAllocator::LinearAllocator() + : mPageSize(INITIAL_PAGE_SIZE) + , mMaxAllocSize(MAX_WASTE_SIZE) + , mNext(0) + , mCurrentPage(0) + , mPages(0) + , mTotalAllocated(0) + , mWastedSpace(0) + , mPageCount(0) + , mDedicatedPageCount(0) {} + +LinearAllocator::~LinearAllocator(void) { + Page* p = mPages; + while (p) { + Page* next = p->next(); + p->~Page(); + free(p); + RM_ALLOCATION(mPageSize); + p = next; + } +} + +void* LinearAllocator::start(Page* p) { + return ALIGN_PTR(((size_t*)p) + sizeof(Page)); +} + +void* LinearAllocator::end(Page* p) { + return ((char*)p) + mPageSize; +} + +bool LinearAllocator::fitsInCurrentPage(size_t size) { + return mNext && ((char*)mNext + size) <= end(mCurrentPage); +} + +void LinearAllocator::ensureNext(size_t size) { + if (fitsInCurrentPage(size)) return; + + if (mCurrentPage && mPageSize < MAX_PAGE_SIZE) { + mPageSize = min(MAX_PAGE_SIZE, mPageSize * 2); + mPageSize = ALIGN(mPageSize); + } + mWastedSpace += mPageSize; + Page* p = newPage(mPageSize); + if (mCurrentPage) { + mCurrentPage->setNext(p); + } + mCurrentPage = p; + if (!mPages) { + mPages = mCurrentPage; + } + mNext = start(mCurrentPage); +} + +void* LinearAllocator::alloc(size_t size) { + size = ALIGN(size); + if (size > mMaxAllocSize && !fitsInCurrentPage(size)) { + ALOGV("Exceeded max size %zu > %zu", size, mMaxAllocSize); + // Allocation is too large, create a dedicated page for the allocation + Page* page = newPage(size); + mDedicatedPageCount++; + page->setNext(mPages); + mPages = page; + if (!mCurrentPage) + mCurrentPage = mPages; + return start(page); + } + ensureNext(size); + void* ptr = mNext; + mNext = ((char*)mNext) + size; + mWastedSpace -= size; + return ptr; +} + +void LinearAllocator::rewindIfLastAlloc(void* ptr, size_t allocSize) { + // Don't bother rewinding across pages + allocSize = ALIGN(allocSize); + if (ptr >= start(mCurrentPage) && ptr < end(mCurrentPage) + && ptr == ((char*)mNext - allocSize)) { + mTotalAllocated -= allocSize; + mWastedSpace += allocSize; + mNext = ptr; + } +} + +LinearAllocator::Page* LinearAllocator::newPage(size_t pageSize) { + pageSize = ALIGN(pageSize + sizeof(LinearAllocator::Page)); + ADD_ALLOCATION(pageSize); + mTotalAllocated += pageSize; + mPageCount++; + void* buf = malloc(pageSize); + return new (buf) Page(); +} + +static const char* toSize(size_t value, float& result) { + if (value < 2000) { + result = value; + return "B"; + } + if (value < 2000000) { + result = value / 1024.0f; + return "KB"; + } + result = value / 1048576.0f; + return "MB"; +} + +void LinearAllocator::dumpMemoryStats(const char* prefix) { + float prettySize; + const char* prettySuffix; + prettySuffix = toSize(mTotalAllocated, prettySize); + ALOGD("%sTotal allocated: %.2f%s", prefix, prettySize, prettySuffix); + prettySuffix = toSize(mWastedSpace, prettySize); + ALOGD("%sWasted space: %.2f%s (%.1f%%)", prefix, prettySize, prettySuffix, + (float) mWastedSpace / (float) mTotalAllocated * 100.0f); + ALOGD("%sPages %zu (dedicated %zu)", prefix, mPageCount, mDedicatedPageCount); +} + +}; // namespace android diff --git a/libs/utils/RefBase.cpp b/libs/utils/RefBase.cpp index e80a795..e538f68 100644 --- a/libs/utils/RefBase.cpp +++ b/libs/utils/RefBase.cpp @@ -15,6 +15,7 @@ */ #define LOG_TAG "RefBase" +// #define LOG_NDEBUG 0 #include <utils/RefBase.h> @@ -34,10 +35,18 @@ // compile with refcounting debugging enabled #define DEBUG_REFS 0 -#define DEBUG_REFS_FATAL_SANITY_CHECKS 0 -#define DEBUG_REFS_ENABLED_BY_DEFAULT 1 + +// whether ref-tracking is enabled by default, if not, trackMe(true, false) +// needs to be called explicitly +#define DEBUG_REFS_ENABLED_BY_DEFAULT 0 + +// whether callstack are collected (significantly slows things down) #define DEBUG_REFS_CALLSTACK_ENABLED 1 +// folder where stack traces are saved when DEBUG_REFS is enabled +// this folder needs to exist and be writable +#define DEBUG_REFS_CALLSTACK_PATH "/data/debug" + // log all reference counting operations #define PRINT_REFS 0 @@ -95,17 +104,13 @@ public: bool dumpStack = false; if (!mRetain && mStrongRefs != NULL) { dumpStack = true; -#if DEBUG_REFS_FATAL_SANITY_CHECKS - LOG_ALWAYS_FATAL("Strong references remain!"); -#else ALOGE("Strong references remain:"); -#endif ref_entry* refs = mStrongRefs; while (refs) { char inc = refs->ref >= 0 ? '+' : '-'; ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref); #if DEBUG_REFS_CALLSTACK_ENABLED - refs->stack.dump(); + refs->stack.dump(LOG_TAG); #endif refs = refs->next; } @@ -113,26 +118,20 @@ public: if (!mRetain && mWeakRefs != NULL) { dumpStack = true; -#if DEBUG_REFS_FATAL_SANITY_CHECKS - LOG_ALWAYS_FATAL("Weak references remain:"); -#else ALOGE("Weak references remain!"); -#endif ref_entry* refs = mWeakRefs; while (refs) { char inc = refs->ref >= 0 ? '+' : '-'; ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref); #if DEBUG_REFS_CALLSTACK_ENABLED - refs->stack.dump(); + refs->stack.dump(LOG_TAG); #endif refs = refs->next; } } if (dumpStack) { ALOGE("above errors at:"); - CallStack stack; - stack.update(); - stack.dump(); + CallStack stack(LOG_TAG); } } @@ -198,8 +197,8 @@ public: { char name[100]; - snprintf(name, 100, "/data/%p.stack", this); - int rc = open(name, O_RDWR | O_CREAT | O_APPEND); + snprintf(name, 100, DEBUG_REFS_CALLSTACK_PATH "/%p.stack", this); + int rc = open(name, O_RDWR | O_CREAT | O_APPEND, 644); if (rc >= 0) { write(rc, text.string(), text.length()); close(rc); @@ -257,12 +256,6 @@ private: ref = *refs; } -#if DEBUG_REFS_FATAL_SANITY_CHECKS - LOG_ALWAYS_FATAL("RefBase: removing id %p on RefBase %p" - "(weakref_type %p) that doesn't exist!", - id, mBase, this); -#endif - ALOGE("RefBase: removing id %p on RefBase %p" "(weakref_type %p) that doesn't exist!", id, mBase, this); @@ -274,9 +267,7 @@ private: ref = ref->next; } - CallStack stack; - stack.update(); - stack.dump(); + CallStack stack(LOG_TAG); } } @@ -440,39 +431,68 @@ bool RefBase::weakref_type::attemptIncStrong(const void* id) incWeak(id); weakref_impl* const impl = static_cast<weakref_impl*>(this); - int32_t curCount = impl->mStrong; - ALOG_ASSERT(curCount >= 0, "attemptIncStrong called on %p after underflow", - this); + + ALOG_ASSERT(curCount >= 0, + "attemptIncStrong called on %p after underflow", this); + while (curCount > 0 && curCount != INITIAL_STRONG_VALUE) { + // we're in the easy/common case of promoting a weak-reference + // from an existing strong reference. if (android_atomic_cmpxchg(curCount, curCount+1, &impl->mStrong) == 0) { break; } + // the strong count has changed on us, we need to re-assert our + // situation. curCount = impl->mStrong; } if (curCount <= 0 || curCount == INITIAL_STRONG_VALUE) { - bool allow; - if (curCount == INITIAL_STRONG_VALUE) { - // Attempting to acquire first strong reference... this is allowed - // if the object does NOT have a longer lifetime (meaning the - // implementation doesn't need to see this), or if the implementation - // allows it to happen. - allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) != OBJECT_LIFETIME_WEAK - || impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id); + // we're now in the harder case of either: + // - there never was a strong reference on us + // - or, all strong references have been released + if ((impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_STRONG) { + // this object has a "normal" life-time, i.e.: it gets destroyed + // when the last strong reference goes away + if (curCount <= 0) { + // the last strong-reference got released, the object cannot + // be revived. + decWeak(id); + return false; + } + + // here, curCount == INITIAL_STRONG_VALUE, which means + // there never was a strong-reference, so we can try to + // promote this object; we need to do that atomically. + while (curCount > 0) { + if (android_atomic_cmpxchg(curCount, curCount + 1, + &impl->mStrong) == 0) { + break; + } + // the strong count has changed on us, we need to re-assert our + // situation (e.g.: another thread has inc/decStrong'ed us) + curCount = impl->mStrong; + } + + if (curCount <= 0) { + // promote() failed, some other thread destroyed us in the + // meantime (i.e.: strong count reached zero). + decWeak(id); + return false; + } } else { - // Attempting to revive the object... this is allowed - // if the object DOES have a longer lifetime (so we can safely - // call the object with only a weak ref) and the implementation - // allows it to happen. - allow = (impl->mFlags&OBJECT_LIFETIME_WEAK) == OBJECT_LIFETIME_WEAK - && impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id); - } - if (!allow) { - decWeak(id); - return false; + // this object has an "extended" life-time, i.e.: it can be + // revived from a weak-reference only. + // Ask the object's implementation if it agrees to be revived + if (!impl->mBase->onIncStrongAttempted(FIRST_INC_STRONG, id)) { + // it didn't so give-up. + decWeak(id); + return false; + } + // grab a strong-reference, which is always safe due to the + // extended life-time. + curCount = android_atomic_inc(&impl->mStrong); } - curCount = android_atomic_inc(&impl->mStrong); // If the strong reference count has already been incremented by // someone else, the implementor of onIncStrongAttempted() is holding @@ -490,11 +510,23 @@ bool RefBase::weakref_type::attemptIncStrong(const void* id) ALOGD("attemptIncStrong of %p from %p: cnt=%d\n", this, id, curCount); #endif - if (curCount == INITIAL_STRONG_VALUE) { - android_atomic_add(-INITIAL_STRONG_VALUE, &impl->mStrong); - impl->mBase->onFirstRef(); + // now we need to fix-up the count if it was INITIAL_STRONG_VALUE + // this must be done safely, i.e.: handle the case where several threads + // were here in attemptIncStrong(). + curCount = impl->mStrong; + while (curCount >= INITIAL_STRONG_VALUE) { + ALOG_ASSERT(curCount > INITIAL_STRONG_VALUE, + "attemptIncStrong in %p underflowed to INITIAL_STRONG_VALUE", + this); + if (android_atomic_cmpxchg(curCount, curCount-INITIAL_STRONG_VALUE, + &impl->mStrong) == 0) { + break; + } + // the strong-count changed on us, we need to re-assert the situation, + // for e.g.: it's possible the fix-up happened in another thread. + curCount = impl->mStrong; } - + return true; } @@ -595,21 +627,27 @@ void RefBase::onLastWeakRef(const void* /*id*/) // --------------------------------------------------------------------------- -void RefBase::moveReferences(void* dst, void const* src, size_t n, - const ReferenceConverterBase& caster) -{ +void RefBase::renameRefs(size_t n, const ReferenceRenamer& renamer) { #if DEBUG_REFS - const size_t itemSize = caster.getReferenceTypeSize(); for (size_t i=0 ; i<n ; i++) { - void* d = reinterpret_cast<void *>(intptr_t(dst) + i*itemSize); - void const* s = reinterpret_cast<void const*>(intptr_t(src) + i*itemSize); - RefBase* ref(reinterpret_cast<RefBase*>(caster.getReferenceBase(d))); - ref->mRefs->renameStrongRefId(s, d); - ref->mRefs->renameWeakRefId(s, d); + renamer(i); } #endif } +void RefBase::renameRefId(weakref_type* ref, + const void* old_id, const void* new_id) { + weakref_impl* const impl = static_cast<weakref_impl*>(ref); + impl->renameStrongRefId(old_id, new_id); + impl->renameWeakRefId(old_id, new_id); +} + +void RefBase::renameRefId(RefBase* ref, + const void* old_id, const void* new_id) { + ref->mRefs->renameStrongRefId(old_id, new_id); + ref->mRefs->renameWeakRefId(old_id, new_id); +} + // --------------------------------------------------------------------------- TextOutput& printStrongPointer(TextOutput& to, const void* val) diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index a25a81f..7b877e0 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -109,30 +109,34 @@ struct thread_data_t { } if (name) { -#if defined(HAVE_PRCTL) - // Mac OS doesn't have this, and we build libutil for the host too - int hasAt = 0; - int hasDot = 0; - char *s = name; - while (*s) { - if (*s == '.') hasDot = 1; - else if (*s == '@') hasAt = 1; - s++; - } - int len = s - name; - if (len < 15 || hasAt || !hasDot) { - s = name; - } else { - s = name + len - 15; - } - prctl(PR_SET_NAME, (unsigned long) s, 0, 0, 0); -#endif + androidSetThreadName(name); free(name); } return f(u); } }; +void androidSetThreadName(const char* name) { +#if defined(HAVE_PRCTL) + // Mac OS doesn't have this, and we build libutil for the host too + int hasAt = 0; + int hasDot = 0; + const char *s = name; + while (*s) { + if (*s == '.') hasDot = 1; + else if (*s == '@') hasAt = 1; + s++; + } + int len = s - name; + if (len < 15 || hasAt || !hasDot) { + s = name; + } else { + s = name + len - 15; + } + prctl(PR_SET_NAME, (unsigned long) s, 0, 0, 0); +#endif +} + int androidCreateRawThreadEtc(android_thread_func_t entryFunction, void *userData, const char* threadName, @@ -871,6 +875,11 @@ status_t Thread::join() return mStatus; } +bool Thread::isRunning() const { + Mutex::Autolock _l(mLock); + return mRunning; +} + #ifdef HAVE_ANDROID_OS pid_t Thread::getTid() const { diff --git a/libs/utils/Trace.cpp b/libs/utils/Trace.cpp index f5aaea3..36fd802 100644 --- a/libs/utils/Trace.cpp +++ b/libs/utils/Trace.cpp @@ -14,52 +14,12 @@ * limitations under the License. */ -#define LOG_TAG "Trace" - -#include <cutils/properties.h> -#include <utils/Log.h> -#include <utils/Trace.h> #include <utils/misc.h> +#include <utils/Trace.h> -namespace android { - -volatile int32_t Tracer::sIsReady = 0; -int Tracer::sTraceFD = -1; -uint64_t Tracer::sEnabledTags = ATRACE_TAG_NOT_READY; -Mutex Tracer::sMutex; - -void Tracer::changeCallback() { - Mutex::Autolock lock(sMutex); - if (sIsReady && sTraceFD >= 0) { - loadSystemProperty(); - } -} - -void Tracer::init() { - Mutex::Autolock lock(sMutex); - - if (!sIsReady) { - add_sysprop_change_callback(changeCallback, 0); - - const char* const traceFileName = - "/sys/kernel/debug/tracing/trace_marker"; - sTraceFD = open(traceFileName, O_WRONLY); - if (sTraceFD == -1) { - ALOGE("error opening trace file: %s (%d)", strerror(errno), errno); - sEnabledTags = 0; // no tracing can occur - } else { - loadSystemProperty(); - } - - android_atomic_release_store(1, &sIsReady); - } -} +static void traceInit() __attribute__((constructor)); -void Tracer::loadSystemProperty() { - char value[PROPERTY_VALUE_MAX]; - property_get("debug.atrace.tags.enableflags", value, "0"); - sEnabledTags = (strtoll(value, NULL, 0) & ATRACE_TAG_VALID_MASK) - | ATRACE_TAG_ALWAYS; +static void traceInit() +{ + ::android::add_sysprop_change_callback(atrace_update_tags, 0); } - -} // namespace andoid diff --git a/libs/utils/VectorImpl.cpp b/libs/utils/VectorImpl.cpp index c3257bb..70f49de 100644 --- a/libs/utils/VectorImpl.cpp +++ b/libs/utils/VectorImpl.cpp @@ -343,6 +343,16 @@ ssize_t VectorImpl::setCapacity(size_t new_capacity) return new_capacity; } +ssize_t VectorImpl::resize(size_t size) { + ssize_t result = NO_ERROR; + if (size > mCount) { + result = insertAt(mCount, size - mCount); + } else if (size < mCount) { + result = removeItemsAt(size, mCount - size); + } + return result < 0 ? result : size; +} + void VectorImpl::release_storage() { if (mStorage) { diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index 5b2b5b1..a2ca9c8 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -7,6 +7,7 @@ test_src_files := \ BasicHashtable_test.cpp \ BlobCache_test.cpp \ Looper_test.cpp \ + LruCache_test.cpp \ String8_test.cpp \ Unicode_test.cpp \ Vector_test.cpp \ diff --git a/libs/utils/tests/LruCache_test.cpp b/libs/utils/tests/LruCache_test.cpp new file mode 100644 index 0000000..e573952 --- /dev/null +++ b/libs/utils/tests/LruCache_test.cpp @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2012 The Android Open Source Project + * + * 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. + */ + +#include <stdlib.h> +#include <utils/JenkinsHash.h> +#include <utils/LruCache.h> +#include <cutils/log.h> +#include <gtest/gtest.h> + +namespace android { + +typedef int SimpleKey; +typedef const char* StringValue; + +struct ComplexKey { + int k; + + explicit ComplexKey(int k) : k(k) { + instanceCount += 1; + } + + ComplexKey(const ComplexKey& other) : k(other.k) { + instanceCount += 1; + } + + ~ComplexKey() { + instanceCount -= 1; + } + + bool operator ==(const ComplexKey& other) const { + return k == other.k; + } + + bool operator !=(const ComplexKey& other) const { + return k != other.k; + } + + static ssize_t instanceCount; +}; + +ssize_t ComplexKey::instanceCount = 0; + +template<> inline hash_t hash_type(const ComplexKey& value) { + return hash_type(value.k); +} + +struct ComplexValue { + int v; + + explicit ComplexValue(int v) : v(v) { + instanceCount += 1; + } + + ComplexValue(const ComplexValue& other) : v(other.v) { + instanceCount += 1; + } + + ~ComplexValue() { + instanceCount -= 1; + } + + static ssize_t instanceCount; +}; + +ssize_t ComplexValue::instanceCount = 0; + +typedef LruCache<ComplexKey, ComplexValue> ComplexCache; + +class EntryRemovedCallback : public OnEntryRemoved<SimpleKey, StringValue> { +public: + EntryRemovedCallback() : callbackCount(0), lastKey(-1), lastValue(NULL) { } + ~EntryRemovedCallback() {} + void operator()(SimpleKey& k, StringValue& v) { + callbackCount += 1; + lastKey = k; + lastValue = v; + } + ssize_t callbackCount; + SimpleKey lastKey; + StringValue lastValue; +}; + +class LruCacheTest : public testing::Test { +protected: + virtual void SetUp() { + ComplexKey::instanceCount = 0; + ComplexValue::instanceCount = 0; + } + + virtual void TearDown() { + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); + } + + void assertInstanceCount(ssize_t keys, ssize_t values) { + if (keys != ComplexKey::instanceCount || values != ComplexValue::instanceCount) { + FAIL() << "Expected " << keys << " keys and " << values << " values " + "but there were actually " << ComplexKey::instanceCount << " keys and " + << ComplexValue::instanceCount << " values"; + } + } +}; + +TEST_F(LruCacheTest, Empty) { + LruCache<SimpleKey, StringValue> cache(100); + + EXPECT_EQ(NULL, cache.get(0)); + EXPECT_EQ(0u, cache.size()); +} + +TEST_F(LruCacheTest, Simple) { + LruCache<SimpleKey, StringValue> cache(100); + + cache.put(1, "one"); + cache.put(2, "two"); + cache.put(3, "three"); + EXPECT_STREQ("one", cache.get(1)); + EXPECT_STREQ("two", cache.get(2)); + EXPECT_STREQ("three", cache.get(3)); + EXPECT_EQ(3u, cache.size()); +} + +TEST_F(LruCacheTest, MaxCapacity) { + LruCache<SimpleKey, StringValue> cache(2); + + cache.put(1, "one"); + cache.put(2, "two"); + cache.put(3, "three"); + EXPECT_EQ(NULL, cache.get(1)); + EXPECT_STREQ("two", cache.get(2)); + EXPECT_STREQ("three", cache.get(3)); + EXPECT_EQ(2u, cache.size()); +} + +TEST_F(LruCacheTest, RemoveLru) { + LruCache<SimpleKey, StringValue> cache(100); + + cache.put(1, "one"); + cache.put(2, "two"); + cache.put(3, "three"); + cache.removeOldest(); + EXPECT_EQ(NULL, cache.get(1)); + EXPECT_STREQ("two", cache.get(2)); + EXPECT_STREQ("three", cache.get(3)); + EXPECT_EQ(2u, cache.size()); +} + +TEST_F(LruCacheTest, GetUpdatesLru) { + LruCache<SimpleKey, StringValue> cache(100); + + cache.put(1, "one"); + cache.put(2, "two"); + cache.put(3, "three"); + EXPECT_STREQ("one", cache.get(1)); + cache.removeOldest(); + EXPECT_STREQ("one", cache.get(1)); + EXPECT_EQ(NULL, cache.get(2)); + EXPECT_STREQ("three", cache.get(3)); + EXPECT_EQ(2u, cache.size()); +} + +uint32_t hash_int(int x) { + return JenkinsHashWhiten(JenkinsHashMix(0, x)); +} + +TEST_F(LruCacheTest, StressTest) { + const size_t kCacheSize = 512; + LruCache<SimpleKey, StringValue> cache(512); + const size_t kNumKeys = 16 * 1024; + const size_t kNumIters = 100000; + char* strings[kNumKeys]; + + for (size_t i = 0; i < kNumKeys; i++) { + strings[i] = (char *)malloc(16); + sprintf(strings[i], "%d", i); + } + + srandom(12345); + int hitCount = 0; + for (size_t i = 0; i < kNumIters; i++) { + int index = random() % kNumKeys; + uint32_t key = hash_int(index); + const char *val = cache.get(key); + if (val != NULL) { + EXPECT_EQ(strings[index], val); + hitCount++; + } else { + cache.put(key, strings[index]); + } + } + size_t expectedHitCount = kNumIters * kCacheSize / kNumKeys; + EXPECT_LT(int(expectedHitCount * 0.9), hitCount); + EXPECT_GT(int(expectedHitCount * 1.1), hitCount); + EXPECT_EQ(kCacheSize, cache.size()); + + for (size_t i = 0; i < kNumKeys; i++) { + free((void *)strings[i]); + } +} + +TEST_F(LruCacheTest, NoLeak) { + ComplexCache cache(100); + + cache.put(ComplexKey(0), ComplexValue(0)); + cache.put(ComplexKey(1), ComplexValue(1)); + EXPECT_EQ(2, cache.size()); + assertInstanceCount(2, 3); // the null value counts as an instance +} + +TEST_F(LruCacheTest, Clear) { + ComplexCache cache(100); + + cache.put(ComplexKey(0), ComplexValue(0)); + cache.put(ComplexKey(1), ComplexValue(1)); + EXPECT_EQ(2, cache.size()); + assertInstanceCount(2, 3); + cache.clear(); + assertInstanceCount(0, 1); +} + +TEST_F(LruCacheTest, ClearNoDoubleFree) { + { + ComplexCache cache(100); + + cache.put(ComplexKey(0), ComplexValue(0)); + cache.put(ComplexKey(1), ComplexValue(1)); + EXPECT_EQ(2, cache.size()); + assertInstanceCount(2, 3); + cache.removeOldest(); + cache.clear(); + assertInstanceCount(0, 1); + } + assertInstanceCount(0, 0); +} + +TEST_F(LruCacheTest, ClearReuseOk) { + ComplexCache cache(100); + + cache.put(ComplexKey(0), ComplexValue(0)); + cache.put(ComplexKey(1), ComplexValue(1)); + EXPECT_EQ(2, cache.size()); + assertInstanceCount(2, 3); + cache.clear(); + assertInstanceCount(0, 1); + cache.put(ComplexKey(0), ComplexValue(0)); + cache.put(ComplexKey(1), ComplexValue(1)); + EXPECT_EQ(2, cache.size()); + assertInstanceCount(2, 3); +} + +TEST_F(LruCacheTest, Callback) { + LruCache<SimpleKey, StringValue> cache(100); + EntryRemovedCallback callback; + cache.setOnEntryRemovedListener(&callback); + + cache.put(1, "one"); + cache.put(2, "two"); + cache.put(3, "three"); + EXPECT_EQ(3, cache.size()); + cache.removeOldest(); + EXPECT_EQ(1, callback.callbackCount); + EXPECT_EQ(1, callback.lastKey); + EXPECT_STREQ("one", callback.lastValue); +} + +TEST_F(LruCacheTest, CallbackOnClear) { + LruCache<SimpleKey, StringValue> cache(100); + EntryRemovedCallback callback; + cache.setOnEntryRemovedListener(&callback); + + cache.put(1, "one"); + cache.put(2, "two"); + cache.put(3, "three"); + EXPECT_EQ(3, cache.size()); + cache.clear(); + EXPECT_EQ(3, callback.callbackCount); +} + +} |