Bug 533055: Part 3: Track |Message|s being processed on the C++ stack and offer a DumpRPCStack() method to print them. r=bent

This commit is contained in:
Chris Jones 2010-03-18 17:52:32 -05:00
Родитель 83622b0ddc
Коммит e15df582cd
2 изменённых файлов: 83 добавлений и 20 удалений

Просмотреть файл

@ -96,8 +96,7 @@ RPCChannel::RPCChannel(RPCListener* aListener)
mOutOfTurnReplies(),
mDeferred(),
mRemoteStackDepthGuess(0),
mBlockedOnParent(false),
mCxxStackFrames(0)
mBlockedOnParent(false)
{
MOZ_COUNT_CTOR(RPCChannel);
@ -109,7 +108,7 @@ RPCChannel::RPCChannel(RPCListener* aListener)
RPCChannel::~RPCChannel()
{
MOZ_COUNT_DTOR(RPCChannel);
RPC_ASSERT(0 == mCxxStackFrames, "mismatched CxxStackFrame ctor/dtors");
RPC_ASSERT(mCxxStackFrames.empty(), "mismatched CxxStackFrame ctor/dtors");
}
void
@ -142,14 +141,16 @@ RPCChannel::EventOccurred()
bool
RPCChannel::Send(Message* msg)
{
CxxStackFrame f(*this);
Message copy = *msg;
CxxStackFrame f(*this, OUT_MESSAGE, &copy);
return AsyncChannel::Send(msg);
}
bool
RPCChannel::Send(Message* msg, Message* reply)
{
CxxStackFrame f(*this);
Message copy = *msg;
CxxStackFrame f(*this, OUT_MESSAGE, &copy);
return SyncChannel::Send(msg, reply);
}
@ -162,7 +163,8 @@ RPCChannel::Call(Message* msg, Message* reply)
"violation of sync handler invariant");
RPC_ASSERT(msg->is_rpc(), "can only Call() RPC messages here");
CxxStackFrame f(*this);
Message copy = *msg;
CxxStackFrame f(*this, OUT_MESSAGE, &copy);
MutexAutoLock lock(mMutex);
@ -227,7 +229,10 @@ RPCChannel::Call(Message* msg, Message* reply)
if (!recvd.is_sync() && !recvd.is_rpc()) {
MutexAutoUnlock unlock(mMutex);
CxxStackFrame f(*this, IN_MESSAGE, &recvd);
AsyncChannel::OnDispatchMessage(recvd);
continue;
}
@ -235,7 +240,10 @@ RPCChannel::Call(Message* msg, Message* reply)
RPC_ASSERT(mPending.empty(),
"other side should have been blocked");
MutexAutoUnlock unlock(mMutex);
CxxStackFrame f(*this, IN_MESSAGE, &recvd);
SyncChannel::OnDispatchMessage(recvd);
continue;
}
@ -296,6 +304,7 @@ RPCChannel::Call(Message* msg, Message* reply)
{
MutexAutoUnlock unlock(mMutex);
// someone called in to us from the other side. handle the call
CxxStackFrame f(*this, IN_MESSAGE, &recvd);
Incall(recvd, stackDepth);
// FIXME/cjones: error handling
}
@ -332,6 +341,8 @@ RPCChannel::MaybeProcessDeferredIncall()
MutexAutoUnlock unlock(mMutex);
fprintf(stderr, " (processing deferred in-call)\n");
CxxStackFrame f(*this, IN_MESSAGE, &call);
Incall(call, stackDepth);
}
@ -383,7 +394,7 @@ RPCChannel::OnMaybeDequeueOne()
mPending.pop();
}
CxxStackFrame f(*this);
CxxStackFrame f(*this, IN_MESSAGE, &recvd);
if (recvd.is_rpc())
return Incall(recvd, 0);
@ -554,6 +565,8 @@ RPCChannel::BlockOnParent()
mPending.pop();
MutexAutoUnlock unlock(mMutex);
CxxStackFrame f(*this, IN_MESSAGE, &recvd);
if (recvd.is_rpc()) {
// stack depth must be 0 here
Incall(recvd, 0);
@ -594,8 +607,7 @@ RPCChannel::DebugAbort(const char* file, int line, const char* cond,
why,
type, reply ? "reply" : "");
// technically we need the mutex for this, but we're dying anyway
fprintf(stderr, " local RPC stack size: %lu\n",
mStack.size());
DumpRPCStack(stderr, " ");
fprintf(stderr, " remote RPC stack guess: %lu\n",
mRemoteStackDepthGuess);
fprintf(stderr, " deferred stack size: %lu\n",
@ -615,6 +627,28 @@ RPCChannel::DebugAbort(const char* file, int line, const char* cond,
NS_RUNTIMEABORT(why);
}
void
RPCChannel::DumpRPCStack(FILE* outfile, const char* const pfx)
{
NS_WARN_IF_FALSE(MessageLoop::current() != mWorkerLoop,
"The worker thread had better be paused in a debugger!");
if (!outfile)
outfile = stdout;
fprintf(outfile, "%sRPCChannel 'backtrace':\n", pfx);
// print a python-style backtrace, first frame to last
for (PRUint32 i = 0; i < mCxxStackFrames.size(); ++i) {
int32 id;
const char* dir, *sems, *name;
mCxxStackFrames[i].Describe(&id, &dir, &sems, &name);
fprintf(outfile, "%s[(%u) %s %s %s(actor=%d) ]\n", pfx,
i, dir, sems, name, id);
}
}
//
// The methods below run in the context of the IO thread, and can proxy
// back to the methods above

Просмотреть файл

@ -39,9 +39,12 @@
#ifndef ipc_glue_RPCChannel_h
#define ipc_glue_RPCChannel_h 1
#include <stdio.h>
// FIXME/cjones probably shouldn't depend on STL
#include <queue>
#include <stack>
#include <vector>
#include "base/basictypes.h"
@ -140,7 +143,7 @@ public:
// Return true iff this has code on the C++ stack.
bool IsOnCxxStack() const {
return 0 < mCxxStackFrames;
return !mCxxStackFrames.empty();
}
NS_OVERRIDE
@ -183,7 +186,7 @@ protected:
NS_OVERRIDE
virtual bool ShouldDeferNotifyMaybeError() {
return 0 < mCxxStackFrames;
return IsOnCxxStack();
}
bool EventOccurred();
@ -212,20 +215,42 @@ protected:
Listener()->OnExitedCxxStack();
}
enum Direction { IN_MESSAGE, OUT_MESSAGE };
struct RPCFrame {
RPCFrame(Direction direction, const Message* msg) :
mDirection(direction), mMsg(msg)
{ }
void Describe(int32* id, const char** dir, const char** sems,
const char** name)
const
{
*id = mMsg->routing_id();
*dir = (IN_MESSAGE == mDirection) ? "in" : "out";
*sems = mMsg->is_rpc() ? "rpc" : mMsg->is_sync() ? "sync" : "async";
*name = mMsg->name();
}
Direction mDirection;
const Message* mMsg;
};
class NS_STACK_CLASS CxxStackFrame
{
public:
CxxStackFrame(RPCChannel& that) : mThat(that) {
NS_ABORT_IF_FALSE(0 <= mThat.mCxxStackFrames,
"mismatched CxxStackFrame ctor/dtor");
CxxStackFrame(RPCChannel& that, Direction direction,
const Message* msg) : mThat(that) {
mThat.AssertWorkerThread();
if (0 == mThat.mCxxStackFrames++)
if (mThat.mCxxStackFrames.empty())
mThat.EnteredCxxStack();
mThat.mCxxStackFrames.push_back(RPCFrame(direction, msg));
}
~CxxStackFrame() {
bool exitingStack = (0 == --mThat.mCxxStackFrames);
mThat.mCxxStackFrames.pop_back();
bool exitingStack = mThat.mCxxStackFrames.empty();
// mListener could have gone away if Close() was called while
// RPCChannel code was still on the stack
@ -255,6 +280,10 @@ protected:
const char* why,
const char* type="rpc", bool reply=false);
// This method is only safe to call on the worker thread, or in a
// debugger with all threads paused. |outfile| defaults to stdout.
void DumpRPCStack(FILE* outfile=NULL, const char* const pfx="");
//
// Queue of all incoming messages, except for replies to sync
// messages, which are delivered directly to the SyncChannel
@ -350,15 +379,15 @@ protected:
// True iff the parent has put us in a |BlockChild()| state.
bool mBlockedOnParent;
// Approximation of number of Sync/RPCChannel-code frames on the
// C++ stack. It can only be interpreted as the implication
// Approximation of Sync/RPCChannel-code frames on the C++ stack.
// It can only be interpreted as the implication
//
// mCxxStackDepth > 0 => RPCChannel code on C++ stack
// !mCxxStackFrames.empty() => RPCChannel code on C++ stack
//
// This member is only accessed on the worker thread, and so is
// not protected by mMutex. It is managed exclusively by the
// helper |class CxxStackFrame|.
int mCxxStackFrames;
std::vector<RPCFrame> mCxxStackFrames;
private: