Skip to content

Commit

Permalink
Merge "Prepare to fail in RefBase destructor if count is untouched"
Browse files Browse the repository at this point in the history
  • Loading branch information
hboehm authored and Gerrit Code Review committed Aug 6, 2018
2 parents c7815d6 + 9d3146a commit f502182
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 30 deletions.
1 change: 1 addition & 0 deletions libbacktrace/include/backtrace/BacktraceMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@

#include <deque>
#include <iterator>
#include <memory>
#include <string>
#include <vector>

Expand Down
29 changes: 25 additions & 4 deletions libutils/CallStack.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,15 @@

#define LOG_TAG "CallStack"

#include <utils/CallStack.h>

#include <memory>

#include <utils/Printer.h>
#include <utils/Errors.h>
#include <utils/Log.h>

#include <backtrace/Backtrace.h>

#define CALLSTACK_WEAK // Don't generate weak definitions.
#include <utils/CallStack.h>

namespace android {

CallStack::CallStack() {
Expand Down Expand Up @@ -76,4 +75,26 @@ void CallStack::print(Printer& printer) const {
}
}

// The following four functions may be used via weak symbol references from libutils.
// Clients assume that if any of these symbols are available, then deleteStack() is.

CallStack::CallStackUPtr CallStack::getCurrentInternal(int ignoreDepth) {
CallStack::CallStackUPtr stack(new CallStack());
stack->update(ignoreDepth + 1);
return stack;
}

void CallStack::logStackInternal(const char* logtag, const CallStack* stack,
android_LogPriority priority) {
stack->log(logtag, priority);
}

String8 CallStack::stackToStringInternal(const char* prefix, const CallStack* stack) {
return stack->toString(prefix);
}

void CallStack::deleteStack(CallStack* stack) {
delete stack;
}

}; // namespace android
57 changes: 34 additions & 23 deletions libutils/RefBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,41 @@
#define LOG_TAG "RefBase"
// #define LOG_NDEBUG 0

#include <memory>

#include <utils/RefBase.h>

#include <utils/CallStack.h>

#include <utils/Mutex.h>

#ifndef __unused
#define __unused __attribute__((__unused__))
#endif

// compile with refcounting debugging enabled
#define DEBUG_REFS 0
// Compile with refcounting debugging enabled.
#define DEBUG_REFS 0

// The following three are ignored unless DEBUG_REFS is set.

// whether ref-tracking is enabled by default, if not, trackMe(true, false)
// needs to be called explicitly
#define DEBUG_REFS_ENABLED_BY_DEFAULT 0
#define DEBUG_REFS_ENABLED_BY_DEFAULT 0

// whether callstack are collected (significantly slows things down)
#define DEBUG_REFS_CALLSTACK_ENABLED 1
#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"
#define DEBUG_REFS_CALLSTACK_PATH "/data/debug"

// log all reference counting operations
#define PRINT_REFS 0
#define PRINT_REFS 0

// Continue after logging a stack trace if ~RefBase discovers that reference
// count has never been incremented. Normally we conspicuously crash in that
// case.
#define DEBUG_REFBASE_DESTRUCTION 1

// ---------------------------------------------------------------------------

Expand Down Expand Up @@ -184,7 +195,7 @@ class RefBase::weakref_impl : public RefBase::weakref_type
char inc = refs->ref >= 0 ? '+' : '-';
ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
#if DEBUG_REFS_CALLSTACK_ENABLED
refs->stack.log(LOG_TAG);
CallStack::logStack(LOG_TAG, refs->stack.get());
#endif
refs = refs->next;
}
Expand All @@ -198,14 +209,14 @@ class RefBase::weakref_impl : public RefBase::weakref_type
char inc = refs->ref >= 0 ? '+' : '-';
ALOGD("\t%c ID %p (ref %d):", inc, refs->id, refs->ref);
#if DEBUG_REFS_CALLSTACK_ENABLED
refs->stack.log(LOG_TAG);
CallStack::logStack(LOG_TAG, refs->stack.get());
#endif
refs = refs->next;
}
}
if (dumpStack) {
ALOGE("above errors at:");
CallStack stack(LOG_TAG);
CallStack::logStack(LOG_TAG);
}
}

Expand Down Expand Up @@ -279,7 +290,7 @@ class RefBase::weakref_impl : public RefBase::weakref_type
this);
int rc = open(name, O_RDWR | O_CREAT | O_APPEND, 644);
if (rc >= 0) {
write(rc, text.string(), text.length());
(void)write(rc, text.string(), text.length());
close(rc);
ALOGD("STACK TRACE for %p saved in %s", this, name);
}
Expand All @@ -294,7 +305,7 @@ class RefBase::weakref_impl : public RefBase::weakref_type
ref_entry* next;
const void* id;
#if DEBUG_REFS_CALLSTACK_ENABLED
CallStack stack;
CallStack::CallStackUPtr stack;
#endif
int32_t ref;
};
Expand All @@ -311,7 +322,7 @@ class RefBase::weakref_impl : public RefBase::weakref_type
ref->ref = mRef;
ref->id = id;
#if DEBUG_REFS_CALLSTACK_ENABLED
ref->stack.update(2);
ref->stack = CallStack::getCurrent(2);
#endif
ref->next = *refs;
*refs = ref;
Expand Down Expand Up @@ -346,7 +357,7 @@ class RefBase::weakref_impl : public RefBase::weakref_type
ref = ref->next;
}

CallStack stack(LOG_TAG);
CallStack::logStack(LOG_TAG);
}
}

Expand All @@ -373,7 +384,7 @@ class RefBase::weakref_impl : public RefBase::weakref_type
inc, refs->id, refs->ref);
out->append(buf);
#if DEBUG_REFS_CALLSTACK_ENABLED
out->append(refs->stack.toString("\t\t"));
out->append(CallStack::stackToString("\t\t", refs->stack.get()));
#else
out->append("\t\t(call stacks disabled)");
#endif
Expand Down Expand Up @@ -700,16 +711,16 @@ RefBase::~RefBase()
if (mRefs->mWeak.load(std::memory_order_relaxed) == 0) {
delete mRefs;
}
} else if (mRefs->mStrong.load(std::memory_order_relaxed)
== INITIAL_STRONG_VALUE) {
} else if (mRefs->mStrong.load(std::memory_order_relaxed) == INITIAL_STRONG_VALUE) {
// We never acquired a strong reference on this object.
LOG_ALWAYS_FATAL_IF(mRefs->mWeak.load() != 0,
"RefBase: Explicit destruction with non-zero weak "
"reference count");
// TODO: Always report if we get here. Currently MediaMetadataRetriever
// C++ objects are inconsistently managed and sometimes get here.
// There may be other cases, but we believe they should all be fixed.
delete mRefs;
#if DEBUG_REFBASE_DESTRUCTION
// Treating this as fatal is prone to causing boot loops. For debugging, it's
// better to treat as non-fatal.
ALOGD("RefBase: Explicit destruction, weak count = %d (in %p)", mRefs->mWeak.load(), this);
CallStack::logStack(LOG_TAG);
#else
LOG_ALWAYS_FATAL("RefBase: Explicit destruction, weak count = %d", mRefs->mWeak.load());
#endif
}
// For debugging purposes, clear mRefs. Ineffective against outstanding wp's.
const_cast<weakref_impl*&>(mRefs) = nullptr;
Expand Down
64 changes: 61 additions & 3 deletions libutils/include/utils/CallStack.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
#ifndef ANDROID_CALLSTACK_H
#define ANDROID_CALLSTACK_H

#include <memory>

#include <android/log.h>
#include <backtrace/backtrace_constants.h>
#include <utils/String8.h>
Expand All @@ -25,6 +27,11 @@
#include <stdint.h>
#include <sys/types.h>

#ifndef CALLSTACK_WEAK
#define CALLSTACK_WEAK __attribute__((weak))
#endif
#define ALWAYS_INLINE __attribute__((always_inline))

namespace android {

class Printer;
Expand All @@ -36,15 +43,15 @@ class CallStack {
CallStack();
// Create a callstack with the current thread's stack trace.
// Immediately dump it to logcat using the given logtag.
CallStack(const char* logtag, int32_t ignoreDepth=1);
CallStack(const char* logtag, int32_t ignoreDepth = 1);
~CallStack();

// Reset the stack frames (same as creating an empty call stack).
void clear() { mFrameLines.clear(); }

// Immediately collect the stack traces for the specified thread.
// The default is to dump the stack of the current call.
void update(int32_t ignoreDepth=1, pid_t tid=BACKTRACE_CURRENT_THREAD);
void update(int32_t ignoreDepth = 1, pid_t tid = BACKTRACE_CURRENT_THREAD);

// Dump a stack trace to the log using the supplied logtag.
void log(const char* logtag,
Expand All @@ -63,7 +70,58 @@ class CallStack {
// Get the count of stack frames that are in this call stack.
size_t size() const { return mFrameLines.size(); }

private:
// DO NOT USE ANYTHING BELOW HERE. The following public members are expected
// to disappear again shortly, once a better replacement facility exists.
// The replacement facility will be incompatible!

// Debugging accesses to some basic functionality. These use weak symbols to
// avoid introducing a dependency on libutilscallstack. Such a dependency from
// libutils results in a cyclic build dependency. These routines can be called
// from within libutils. But if the actual library is unavailable, they do
// nothing.
//
// DO NOT USE THESE. They will disappear.
struct StackDeleter {
void operator()(CallStack* stack) { deleteStack(stack); }
};

typedef std::unique_ptr<CallStack, StackDeleter> CallStackUPtr;

// Return current call stack if possible, nullptr otherwise.
static CallStackUPtr ALWAYS_INLINE getCurrent(int32_t ignoreDepth = 1) {
if (reinterpret_cast<uintptr_t>(getCurrentInternal) == 0) {
ALOGW("CallStack::getCurrentInternal not linked, returning null");
return CallStackUPtr(nullptr);
} else {
return getCurrentInternal(ignoreDepth);
}
}
static void ALWAYS_INLINE logStack(const char* logtag, CallStack* stack = getCurrent().get(),
android_LogPriority priority = ANDROID_LOG_DEBUG) {
if (reinterpret_cast<uintptr_t>(logStackInternal) != 0 && stack != nullptr) {
logStackInternal(logtag, stack, priority);
} else {
ALOGW("CallStack::logStackInternal not linked");
}
}
static String8 ALWAYS_INLINE stackToString(const char* prefix = nullptr,
const CallStack* stack = getCurrent().get()) {
if (reinterpret_cast<uintptr_t>(stackToStringInternal) != 0 && stack != nullptr) {
return stackToStringInternal(prefix, stack);
} else {
return String8("<CallStack package not linked>");
}
}

private:
static CallStackUPtr CALLSTACK_WEAK getCurrentInternal(int32_t ignoreDepth);
static void CALLSTACK_WEAK logStackInternal(const char* logtag, const CallStack* stack,
android_LogPriority priority);
static String8 CALLSTACK_WEAK stackToStringInternal(const char* prefix, const CallStack* stack);
// The deleter is only invoked on non-null pointers. Hence it will never be
// invoked if CallStack is not linked.
static void CALLSTACK_WEAK deleteStack(CallStack* stack);

Vector<String8> mFrameLines;
};

Expand Down

0 comments on commit f502182

Please sign in to comment.