Merge change 2004 into donut
* changes: Handle munmap() and add support for tracing JNI (native) calls.
This commit is contained in:
@@ -32,7 +32,9 @@ class StackFrame {
|
||||
typedef SYM symbol_type;
|
||||
static const uint32_t kCausedException = 0x01;
|
||||
static const uint32_t kInterpreted = 0x02;
|
||||
static const uint32_t kPopBarrier = (kCausedException | kInterpreted);
|
||||
static const uint32_t kStartNative = 0x04;
|
||||
static const uint32_t kPopBarrier = (kCausedException | kInterpreted
|
||||
| kStartNative);
|
||||
|
||||
symbol_type *function; // the symbol for the function we entered
|
||||
uint32_t addr; // return address when this function returns
|
||||
@@ -43,7 +45,7 @@ class StackFrame {
|
||||
|
||||
template <class FRAME, class BASE = CallStackBase>
|
||||
class CallStack : public BASE {
|
||||
public:
|
||||
public:
|
||||
typedef FRAME frame_type;
|
||||
typedef typename FRAME::symbol_type symbol_type;
|
||||
typedef typename FRAME::symbol_type::region_type region_type;
|
||||
@@ -57,7 +59,7 @@ class CallStack : public BASE {
|
||||
void threadStart(uint64_t time);
|
||||
void threadStop(uint64_t time);
|
||||
|
||||
// Set to true if you don't want to see any Java methods
|
||||
// Set to true if you don't want to see any Java methods ever
|
||||
void setNativeOnly(bool nativeOnly) {
|
||||
mNativeOnly = nativeOnly;
|
||||
}
|
||||
@@ -66,38 +68,36 @@ class CallStack : public BASE {
|
||||
|
||||
uint64_t getGlobalTime(uint64_t time) { return time + mSkippedTime; }
|
||||
void showStack(FILE *stream);
|
||||
void showSnapshotStack(FILE *stream);
|
||||
|
||||
int mNumFrames;
|
||||
FRAME *mFrames;
|
||||
int mTop; // index of the next stack frame to write
|
||||
|
||||
private:
|
||||
enum Action { NONE, PUSH, POP };
|
||||
private:
|
||||
enum Action { NONE, PUSH, POP, NATIVE_PUSH };
|
||||
|
||||
Action getAction(BBEvent *event, symbol_type *function);
|
||||
Action getMethodAction(BBEvent *event, symbol_type *function);
|
||||
void doMethodAction(BBEvent *event, symbol_type *function);
|
||||
void doMethodPop(BBEvent *event, uint32_t addr, const uint32_t flags);
|
||||
void doSimplePush(symbol_type *function, uint32_t addr,
|
||||
uint64_t time);
|
||||
uint64_t time, int flags);
|
||||
void doSimplePop(uint64_t time);
|
||||
void doPush(BBEvent *event, symbol_type *function);
|
||||
void doPop(BBEvent *event, symbol_type *function, Action methodAction);
|
||||
|
||||
void transitionToJava();
|
||||
void transitionFromJava(uint64_t time);
|
||||
|
||||
TraceReaderType *mTrace;
|
||||
|
||||
// This is a global switch that disables Java methods from appearing
|
||||
// on the stack.
|
||||
bool mNativeOnly;
|
||||
|
||||
// This keeps track of whether native frames are currently allowed on the
|
||||
// stack.
|
||||
bool mAllowNativeFrames;
|
||||
|
||||
symbol_type mDummyFunction;
|
||||
region_type mDummyRegion;
|
||||
|
||||
int mJavaTop;
|
||||
|
||||
int mSnapshotNumFrames;
|
||||
FRAME *mSnapshotFrames;
|
||||
int mSnapshotTop; // index of the next stack frame to write
|
||||
|
||||
symbol_type *mPrevFunction;
|
||||
BBEvent mPrevEvent;
|
||||
|
||||
@@ -125,10 +125,7 @@ CallStack<FRAME, BASE>::CallStack(int id, int numFrames, TraceReaderType *trace)
|
||||
mNumFrames = numFrames;
|
||||
mFrames = new FRAME[mNumFrames];
|
||||
mTop = 0;
|
||||
|
||||
mSnapshotNumFrames = numFrames;
|
||||
mSnapshotFrames = new FRAME[mSnapshotNumFrames];
|
||||
mSnapshotTop = 0;
|
||||
mAllowNativeFrames = true;
|
||||
|
||||
memset(&mDummyFunction, 0, sizeof(symbol_type));
|
||||
memset(&mDummyRegion, 0, sizeof(region_type));
|
||||
@@ -139,7 +136,6 @@ CallStack<FRAME, BASE>::CallStack(int id, int numFrames, TraceReaderType *trace)
|
||||
memset(&mUserEvent, 0, sizeof(BBEvent));
|
||||
mSkippedTime = 0;
|
||||
mLastRunTime = 0;
|
||||
mJavaTop = 0;
|
||||
|
||||
// Read the first two methods from the trace if we haven't already read
|
||||
// from the method trace yet.
|
||||
@@ -169,11 +165,29 @@ CallStack<FRAME, BASE>::updateStack(BBEvent *event, symbol_type *function)
|
||||
// instead.
|
||||
if (function->vm_sym != NULL)
|
||||
function = function->vm_sym;
|
||||
} else {
|
||||
doMethodAction(event, function);
|
||||
}
|
||||
|
||||
Action action = getAction(event, function);
|
||||
Action methodAction = getMethodAction(event, function);
|
||||
|
||||
// Allow native frames if we are executing in the kernel.
|
||||
if (!mAllowNativeFrames
|
||||
&& (function->region->flags & region_type::kIsKernelRegion) == 0) {
|
||||
action = NONE;
|
||||
}
|
||||
|
||||
if (function->vm_sym != NULL) {
|
||||
function = function->vm_sym;
|
||||
function->vm_sym = NULL;
|
||||
}
|
||||
if (action == PUSH) {
|
||||
doPush(event, function);
|
||||
} else if (action == POP) {
|
||||
doPop(event, function, NONE);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// Pop off native functions before pushing or popping Java methods.
|
||||
if (action == POP && mPrevFunction->vm_sym == NULL) {
|
||||
// Pop off the previous function first.
|
||||
@@ -198,11 +212,16 @@ CallStack<FRAME, BASE>::updateStack(BBEvent *event, symbol_type *function)
|
||||
doPush(event, function);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// If the stack is now empty, then push the current function.
|
||||
if (mTop == 0) {
|
||||
uint64_t time = event->time - mSkippedTime;
|
||||
doSimplePush(function, 0, time);
|
||||
int flags = 0;
|
||||
if (function->vm_sym != NULL) {
|
||||
flags = FRAME::kInterpreted;
|
||||
}
|
||||
doSimplePush(function, 0, time, 0);
|
||||
}
|
||||
|
||||
mPrevFunction = function;
|
||||
@@ -465,12 +484,19 @@ void CallStack<FRAME, BASE>::doPush(BBEvent *event, symbol_type *function)
|
||||
if ((function->flags & symbol_type::kIsVectorStart) && mTop > 0)
|
||||
mFrames[mTop - 1].flags |= FRAME::kCausedException;
|
||||
|
||||
doSimplePush(function, retAddr, time);
|
||||
// If the function being pushed is a Java method, then mark it on
|
||||
// the stack so that we don't pop it off until we get a matching
|
||||
// trace record from the method trace file.
|
||||
int flags = 0;
|
||||
if (function->vm_sym != NULL) {
|
||||
flags = FRAME::kInterpreted;
|
||||
}
|
||||
doSimplePush(function, retAddr, time, flags);
|
||||
}
|
||||
|
||||
template<class FRAME, class BASE>
|
||||
void CallStack<FRAME, BASE>::doSimplePush(symbol_type *function,
|
||||
uint32_t addr, uint64_t time)
|
||||
void CallStack<FRAME, BASE>::doSimplePush(symbol_type *function, uint32_t addr,
|
||||
uint64_t time, int flags)
|
||||
{
|
||||
// Check for stack overflow
|
||||
if (mTop >= mNumFrames) {
|
||||
@@ -479,30 +505,12 @@ void CallStack<FRAME, BASE>::doSimplePush(symbol_type *function,
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Keep track of the number of Java methods we push on the stack.
|
||||
if (!mNativeOnly && function->vm_sym != NULL) {
|
||||
// If we are pushing the first Java method on the stack, then
|
||||
// save a snapshot of the stack so that we can clean things up
|
||||
// later when we pop off the last Java stack frame.
|
||||
if (mJavaTop == 0) {
|
||||
transitionToJava();
|
||||
}
|
||||
mJavaTop += 1;
|
||||
}
|
||||
|
||||
mFrames[mTop].addr = addr;
|
||||
mFrames[mTop].function = function;
|
||||
mFrames[mTop].flags = 0;
|
||||
mFrames[mTop].flags = flags;
|
||||
mFrames[mTop].time = time;
|
||||
mFrames[mTop].global_time = time + mSkippedTime;
|
||||
|
||||
// If the function being pushed is a Java method, then mark it on
|
||||
// the stack so that we don't pop it off until we get a matching
|
||||
// trace record from the method trace file.
|
||||
if (function->vm_sym != NULL) {
|
||||
mFrames[mTop].flags = FRAME::kInterpreted;
|
||||
}
|
||||
|
||||
mFrames[mTop].push(mTop, time, this);
|
||||
mTop += 1;
|
||||
}
|
||||
@@ -517,17 +525,25 @@ void CallStack<FRAME, BASE>::doSimplePop(uint64_t time)
|
||||
mTop -= 1;
|
||||
mFrames[mTop].pop(mTop, time, this);
|
||||
|
||||
// Keep track of the number of Java methods we have on the stack.
|
||||
symbol_type *function = mFrames[mTop].function;
|
||||
if (!mNativeOnly && function->vm_sym != NULL) {
|
||||
mJavaTop -= 1;
|
||||
if (mNativeOnly)
|
||||
return;
|
||||
|
||||
// When there are no more Java stack frames, then clean up
|
||||
// the client's stack. We need to do this because the client
|
||||
// doesn't see the changes to the native stack underlying the
|
||||
// fake Java stack until the last Java method is popped off.
|
||||
if (mJavaTop == 0) {
|
||||
transitionFromJava(time);
|
||||
// If the stack is empty, then allow more native frames.
|
||||
// Otherwise, if we are transitioning from Java to native, then allow
|
||||
// more native frames.
|
||||
// Otherwise, if we are transitioning from native to Java, then disallow
|
||||
// more native frames.
|
||||
if (mTop == 0) {
|
||||
mAllowNativeFrames = true;
|
||||
} else {
|
||||
bool newerIsJava = (mFrames[mTop].flags & FRAME::kInterpreted) != 0;
|
||||
bool olderIsJava = (mFrames[mTop - 1].flags & FRAME::kInterpreted) != 0;
|
||||
if (newerIsJava && !olderIsJava) {
|
||||
// We are transitioning from Java to native
|
||||
mAllowNativeFrames = true;
|
||||
} else if (!newerIsJava && olderIsJava) {
|
||||
// We are transitioning from native to Java
|
||||
mAllowNativeFrames = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -671,20 +687,45 @@ void CallStack<FRAME, BASE>::popAll(uint64_t time)
|
||||
}
|
||||
|
||||
template<class FRAME, class BASE>
|
||||
typename CallStack<FRAME, BASE>::Action
|
||||
CallStack<FRAME, BASE>::getMethodAction(BBEvent *event, symbol_type *function)
|
||||
void CallStack<FRAME, BASE>::doMethodPop(BBEvent *event, uint32_t addr,
|
||||
const uint32_t flags)
|
||||
{
|
||||
if (function->vm_sym == NULL && mPrevFunction->vm_sym == NULL) {
|
||||
return NONE;
|
||||
uint64_t time = event->time - mSkippedTime;
|
||||
|
||||
// Search the stack from the top down for a frame that contains a
|
||||
// matching method.
|
||||
int stackLevel;
|
||||
for (stackLevel = mTop - 1; stackLevel >= 0; --stackLevel) {
|
||||
if (mFrames[stackLevel].flags & flags) {
|
||||
// If we are searching for a native method, then don't bother trying
|
||||
// to match the address.
|
||||
if (flags == FRAME::kStartNative)
|
||||
break;
|
||||
symbol_type *func = mFrames[stackLevel].function;
|
||||
uint32_t methodAddr = func->region->base_addr + func->addr;
|
||||
if (methodAddr == addr) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Action action = NONE;
|
||||
uint32_t prevAddr = mPrevFunction->addr + mPrevFunction->region->base_addr;
|
||||
uint32_t addr = function->addr + function->region->base_addr;
|
||||
// If we found a matching frame then pop the stack up to and including
|
||||
// that frame.
|
||||
if (stackLevel >= 0) {
|
||||
// Pop the stack frames
|
||||
for (int ii = mTop - 1; ii >= stackLevel; --ii)
|
||||
doSimplePop(time);
|
||||
}
|
||||
}
|
||||
|
||||
template<class FRAME, class BASE>
|
||||
void CallStack<FRAME, BASE>::doMethodAction(BBEvent *event, symbol_type *function)
|
||||
{
|
||||
// If the events get ahead of the method trace, then read ahead until we
|
||||
// sync up again. This can happen if there is a pop of a method in the
|
||||
// method trace for which we don't have a previous push.
|
||||
// method trace for which we don't have a previous push. Such an unmatched
|
||||
// pop can happen because the user can start tracing at any time and so
|
||||
// there might already be a stack when we start tracing.
|
||||
while (event->time >= sNextMethod.time) {
|
||||
sCurrentMethod = sNextMethod;
|
||||
if (mTrace->ReadMethod(&sNextMethod)) {
|
||||
@@ -693,59 +734,26 @@ CallStack<FRAME, BASE>::getMethodAction(BBEvent *event, symbol_type *function)
|
||||
}
|
||||
|
||||
if (event->time >= sCurrentMethod.time && event->pid == sCurrentMethod.pid) {
|
||||
if (addr == sCurrentMethod.addr || prevAddr == sCurrentMethod.addr) {
|
||||
action = (sCurrentMethod.flags == 0) ? PUSH : POP;
|
||||
// We found a match, so read the next record.
|
||||
sCurrentMethod = sNextMethod;
|
||||
if (sNextMethod.time != ~0ull && mTrace->ReadMethod(&sNextMethod)) {
|
||||
sNextMethod.time = ~0ull;
|
||||
}
|
||||
}
|
||||
}
|
||||
return action;
|
||||
}
|
||||
|
||||
// When the first Java method is pushed on the stack, this method is
|
||||
// called to save a snapshot of the current native stack so that the
|
||||
// client's view of the native stack can be patched up later when the
|
||||
// Java stack is empty.
|
||||
template<class FRAME, class BASE>
|
||||
void CallStack<FRAME, BASE>::transitionToJava()
|
||||
{
|
||||
mSnapshotTop = mTop;
|
||||
for (int ii = 0; ii < mTop; ++ii) {
|
||||
mSnapshotFrames[ii] = mFrames[ii];
|
||||
}
|
||||
}
|
||||
|
||||
// When the Java stack becomes empty, the native stack becomes
|
||||
// visible. This method is called when the Java stack becomes empty
|
||||
// to patch up the client's view of the native stack, which may have
|
||||
// changed underneath the Java stack. The stack snapshot is used to
|
||||
// create a sequence of pops and pushes to make the client's view of
|
||||
// the native stack match the current native stack.
|
||||
template<class FRAME, class BASE>
|
||||
void CallStack<FRAME, BASE>::transitionFromJava(uint64_t time)
|
||||
{
|
||||
int top = mTop;
|
||||
if (top > mSnapshotTop) {
|
||||
top = mSnapshotTop;
|
||||
}
|
||||
for (int ii = 0; ii < top; ++ii) {
|
||||
if (mSnapshotFrames[ii].function->addr == mFrames[ii].function->addr) {
|
||||
continue;
|
||||
uint64_t time = event->time - mSkippedTime;
|
||||
int flags = sCurrentMethod.flags;
|
||||
if (flags == kMethodEnter) {
|
||||
doSimplePush(function, 0, time, FRAME::kInterpreted);
|
||||
mAllowNativeFrames = false;
|
||||
} else if (flags == kNativeEnter) {
|
||||
doSimplePush(function, 0, time, FRAME::kStartNative);
|
||||
mAllowNativeFrames = true;
|
||||
} else if (flags == kMethodExit || flags == kMethodException) {
|
||||
doMethodPop(event, sCurrentMethod.addr, FRAME::kInterpreted);
|
||||
} else if (flags == kNativeExit || flags == kNativeException) {
|
||||
doMethodPop(event, sCurrentMethod.addr, FRAME::kStartNative);
|
||||
}
|
||||
|
||||
// Pop off all the rest of the frames from the snapshot
|
||||
for (int jj = top - 1; jj >= ii; --jj) {
|
||||
mSnapshotFrames[jj].pop(jj, time, this);
|
||||
// We found a match, so read the next record. When we get to the end
|
||||
// of the trace, we set the time to the maximum value (~0).
|
||||
sCurrentMethod = sNextMethod;
|
||||
if (sNextMethod.time != ~0ull && mTrace->ReadMethod(&sNextMethod)) {
|
||||
sNextMethod.time = ~0ull;
|
||||
}
|
||||
|
||||
// Push the new frames from the native stack
|
||||
for (int jj = ii; jj < mTop; ++jj) {
|
||||
mFrames[jj].push(jj, time, this);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -764,16 +772,4 @@ void CallStack<FRAME, BASE>::showStack(FILE *stream)
|
||||
}
|
||||
}
|
||||
|
||||
template<class FRAME, class BASE>
|
||||
void CallStack<FRAME, BASE>::showSnapshotStack(FILE *stream)
|
||||
{
|
||||
fprintf(stream, "mSnapshotTop: %d\n", mSnapshotTop);
|
||||
for (int ii = 0; ii < mSnapshotTop; ++ii) {
|
||||
fprintf(stream, " %d: t %d f %x 0x%08x 0x%08x %s\n",
|
||||
ii, mSnapshotFrames[ii].time, mSnapshotFrames[ii].flags,
|
||||
mSnapshotFrames[ii].addr, mSnapshotFrames[ii].function->addr,
|
||||
mSnapshotFrames[ii].function->name);
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* CALL_STACK_H */
|
||||
|
||||
@@ -21,6 +21,7 @@ class HashTable {
|
||||
typedef T value_type;
|
||||
|
||||
void Update(const char *key, T value);
|
||||
bool Remove(const char *key);
|
||||
T Find(const char *key);
|
||||
entry_type* GetFirst();
|
||||
entry_type* GetNext();
|
||||
@@ -120,6 +121,31 @@ void HashTable<T>::Update(const char *key, T value)
|
||||
num_entries_ += 1;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
bool HashTable<T>::Remove(const char *key)
|
||||
{
|
||||
// Hash the key to get the table position
|
||||
int len = strlen(key);
|
||||
int pos = HashFunction(key) & mask_;
|
||||
|
||||
// Search the chain for a matching key and keep track of the previous
|
||||
// element in the chain.
|
||||
entry_type *prev = NULL;
|
||||
for (entry_type *ptr = table_[pos]; ptr; prev = ptr, ptr = ptr->next) {
|
||||
if (strcmp(ptr->key, key) == 0) {
|
||||
if (prev == NULL) {
|
||||
table_[pos] = ptr->next;
|
||||
} else {
|
||||
prev->next = ptr->next;
|
||||
}
|
||||
delete ptr->key;
|
||||
delete ptr;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
typename HashTable<T>::value_type HashTable<T>::Find(const char *key)
|
||||
{
|
||||
|
||||
@@ -62,6 +62,19 @@ class TraceReader : public TraceReaderBase {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
region_entry *MakePrivateCopy(region_entry *dest) {
|
||||
dest->refs = 0;
|
||||
dest->path = Strdup(path);
|
||||
dest->vstart = vstart;
|
||||
dest->vend = vend;
|
||||
dest->base_addr = base_addr;
|
||||
dest->file_offset = file_offset;
|
||||
dest->flags = flags;
|
||||
dest->nsymbols = nsymbols;
|
||||
dest->symbols = symbols;
|
||||
return dest;
|
||||
}
|
||||
|
||||
int refs; // reference count
|
||||
char *path;
|
||||
uint32_t vstart;
|
||||
@@ -100,6 +113,11 @@ class TraceReader : public TraceReaderBase {
|
||||
static const int kHasKernelRegion = 0x08;
|
||||
static const int kHasFirstMmap = 0x10;
|
||||
|
||||
struct methodFrame {
|
||||
uint32_t addr;
|
||||
bool isNative;
|
||||
};
|
||||
|
||||
ProcessState() {
|
||||
cpu_time = 0;
|
||||
tgid = 0;
|
||||
@@ -153,7 +171,7 @@ class TraceReader : public TraceReaderBase {
|
||||
}
|
||||
|
||||
// Dumps the stack contents to standard output. For debugging.
|
||||
void DumpStack();
|
||||
void DumpStack(FILE *stream);
|
||||
|
||||
uint64_t cpu_time;
|
||||
uint64_t start_time;
|
||||
@@ -173,7 +191,7 @@ class TraceReader : public TraceReaderBase {
|
||||
ProcessState *addr_manager; // the address space manager process
|
||||
ProcessState *next;
|
||||
int method_stack_top;
|
||||
uint32_t method_stack[kMaxMethodStackSize];
|
||||
methodFrame method_stack[kMaxMethodStackSize];
|
||||
symbol_type *current_method_sym;
|
||||
};
|
||||
|
||||
@@ -184,6 +202,7 @@ class TraceReader : public TraceReaderBase {
|
||||
void CopyKernelRegion(ProcessState *pstate);
|
||||
void ClearRegions(ProcessState *pstate);
|
||||
void CopyRegions(ProcessState *parent, ProcessState *child);
|
||||
void DumpRegions(FILE *stream, ProcessState *pstate);
|
||||
symbol_type *LookupFunction(int pid, uint32_t addr, uint64_t time);
|
||||
symbol_type *GetSymbols(int *num_syms);
|
||||
ProcessState *GetCurrentProcess() { return current_; }
|
||||
@@ -217,6 +236,10 @@ class TraceReader : public TraceReaderBase {
|
||||
void AddRegion(ProcessState *pstate, region_type *region);
|
||||
region_type *FindRegion(uint32_t addr, int nregions,
|
||||
region_type **regions);
|
||||
int FindRegionIndex(uint32_t addr, int nregions,
|
||||
region_type **regions);
|
||||
void FindAndRemoveRegion(ProcessState *pstate,
|
||||
uint32_t vstart, uint32_t vend);
|
||||
symbol_type *FindFunction(uint32_t addr, int nsyms,
|
||||
symbol_type *symbols, bool exact_match);
|
||||
symbol_type *FindCurrentMethod(int pid, uint64_t time);
|
||||
@@ -925,6 +948,63 @@ void TraceReader<T>::AddRegion(ProcessState *pstate, region_type *region)
|
||||
qsort(manager->regions, nregions, sizeof(region_type*), cmp_region_addr<T>);
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void TraceReader<T>::FindAndRemoveRegion(ProcessState *pstate, uint32_t vstart,
|
||||
uint32_t vend)
|
||||
{
|
||||
ProcessState *manager = pstate->addr_manager;
|
||||
int nregions = manager->nregions;
|
||||
int index = FindRegionIndex(vstart, nregions, manager->regions);
|
||||
region_type *region = manager->regions[index];
|
||||
|
||||
// If the region does not contain [vstart,vend], then return.
|
||||
if (vstart < region->vstart || vend > region->vend)
|
||||
return;
|
||||
|
||||
// If the existing region exactly matches the address range [vstart,vend]
|
||||
// then remove the whole region.
|
||||
if (vstart == region->vstart && vend == region->vend) {
|
||||
// The regions are reference-counted.
|
||||
if (region->refs == 0) {
|
||||
// Free the region
|
||||
hash_->Remove(region->path);
|
||||
delete region;
|
||||
} else {
|
||||
region->refs -= 1;
|
||||
}
|
||||
|
||||
if (nregions > 1) {
|
||||
// Assign the region at the end of the array to this empty slot
|
||||
manager->regions[index] = manager->regions[nregions - 1];
|
||||
|
||||
// Resort the regions into increasing start address
|
||||
qsort(manager->regions, nregions - 1, sizeof(region_type*),
|
||||
cmp_region_addr<T>);
|
||||
}
|
||||
manager->nregions = nregions - 1;
|
||||
return;
|
||||
}
|
||||
|
||||
// If the existing region contains the given range and ends at the
|
||||
// end of the given range (a common case for some reason), then
|
||||
// truncate the existing region so that it ends at vstart (because
|
||||
// we are deleting the range [vstart,vend]).
|
||||
if (vstart > region->vstart && vend == region->vend) {
|
||||
region_type *truncated;
|
||||
|
||||
if (region->refs == 0) {
|
||||
// This region is not shared, so truncate it directly
|
||||
truncated = region;
|
||||
} else {
|
||||
// This region is shared, so make a copy that we can truncate
|
||||
region->refs -= 1;
|
||||
truncated = region->MakePrivateCopy(new region_type);
|
||||
}
|
||||
truncated->vend = vstart;
|
||||
manager->regions[index] = truncated;
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void TraceReader<T>::CopyRegions(ProcessState *parent, ProcessState *child)
|
||||
{
|
||||
@@ -943,6 +1023,20 @@ void TraceReader<T>::CopyRegions(ProcessState *parent, ProcessState *child)
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
void TraceReader<T>::DumpRegions(FILE *stream, ProcessState *pstate) {
|
||||
ProcessState *manager = pstate->addr_manager;
|
||||
for (int ii = 0; ii < manager->nregions; ++ii) {
|
||||
fprintf(stream, " %08x - %08x offset: %5x nsyms: %4d refs: %d %s\n",
|
||||
manager->regions[ii]->vstart,
|
||||
manager->regions[ii]->vend,
|
||||
manager->regions[ii]->file_offset,
|
||||
manager->regions[ii]->nsymbols,
|
||||
manager->regions[ii]->refs,
|
||||
manager->regions[ii]->path);
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
typename TraceReader<T>::region_type *
|
||||
TraceReader<T>::FindRegion(uint32_t addr, int nregions, region_type **regions)
|
||||
@@ -967,6 +1061,30 @@ TraceReader<T>::FindRegion(uint32_t addr, int nregions, region_type **regions)
|
||||
return regions[low];
|
||||
}
|
||||
|
||||
template<class T>
|
||||
int TraceReader<T>::FindRegionIndex(uint32_t addr, int nregions,
|
||||
region_type **regions)
|
||||
{
|
||||
int high = nregions;
|
||||
int low = -1;
|
||||
while (low + 1 < high) {
|
||||
int middle = (high + low) / 2;
|
||||
uint32_t middle_addr = regions[middle]->vstart;
|
||||
if (middle_addr == addr)
|
||||
return middle;
|
||||
if (middle_addr > addr)
|
||||
high = middle;
|
||||
else
|
||||
low = middle;
|
||||
}
|
||||
|
||||
// If we get here then we did not find an exact address match. So use
|
||||
// the closest region address that is less than the given address.
|
||||
if (low < 0)
|
||||
low = 0;
|
||||
return low;
|
||||
}
|
||||
|
||||
template<class T>
|
||||
typename TraceReader<T>::symbol_type *
|
||||
TraceReader<T>::FindFunction(uint32_t addr, int nsyms, symbol_type *symbols,
|
||||
@@ -1007,15 +1125,12 @@ TraceReader<T>::LookupFunction(int pid, uint32_t addr, uint64_t time)
|
||||
uint32_t sym_addr = addr - cached_func_->region->base_addr;
|
||||
if (sym_addr >= cached_func_->addr
|
||||
&& sym_addr < (cached_func_ + 1)->addr) {
|
||||
// If this function is the virtual machine interpreter, then
|
||||
// read the method trace to find the "real" method name based
|
||||
// on the current time and pid.
|
||||
if (cached_func_->flags & symbol_type::kIsInterpreter) {
|
||||
symbol_type *sym = FindCurrentMethod(pid, time);
|
||||
if (sym != NULL) {
|
||||
sym->vm_sym = cached_func_;
|
||||
return sym;
|
||||
}
|
||||
|
||||
// Check if there is a Java method on the method trace.
|
||||
symbol_type *sym = FindCurrentMethod(pid, time);
|
||||
if (sym != NULL) {
|
||||
sym->vm_sym = cached_func_;
|
||||
return sym;
|
||||
}
|
||||
return cached_func_;
|
||||
}
|
||||
@@ -1040,15 +1155,11 @@ TraceReader<T>::LookupFunction(int pid, uint32_t addr, uint64_t time)
|
||||
if (cached_func_ != NULL) {
|
||||
cached_func_->region = region;
|
||||
|
||||
// If this function is the virtual machine interpreter, then
|
||||
// read the method trace to find the "real" method name based
|
||||
// on the current time and pid.
|
||||
if (cached_func_->flags & symbol_type::kIsInterpreter) {
|
||||
symbol_type *sym = FindCurrentMethod(pid, time);
|
||||
if (sym != NULL) {
|
||||
sym->vm_sym = cached_func_;
|
||||
return sym;
|
||||
}
|
||||
// Check if there is a Java method on the method trace.
|
||||
symbol_type *sym = FindCurrentMethod(pid, time);
|
||||
if (sym != NULL) {
|
||||
sym->vm_sym = cached_func_;
|
||||
return sym;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1142,11 +1253,17 @@ void TraceReader<T>::HandlePidEvent(PidEvent *event)
|
||||
current_->exit_val = event->pid;
|
||||
current_->flags |= ProcessState::kCalledExit;
|
||||
break;
|
||||
case kPidMunmap:
|
||||
FindAndRemoveRegion(current_, event->vstart, event->vend);
|
||||
break;
|
||||
case kPidMmap:
|
||||
{
|
||||
region_type *region;
|
||||
region_type *existing_region = hash_->Find(event->path);
|
||||
if (existing_region == NULL || existing_region->vstart != event->vstart) {
|
||||
if (existing_region == NULL
|
||||
|| existing_region->vstart != event->vstart
|
||||
|| existing_region->vend != event->vend
|
||||
|| existing_region->file_offset != event->offset) {
|
||||
// Create a new region and add it to the current process'
|
||||
// address space.
|
||||
region = new region_type;
|
||||
@@ -1264,10 +1381,12 @@ int TraceReader<T>::FindCurrentPid(uint64_t time)
|
||||
}
|
||||
|
||||
template <class T>
|
||||
void TraceReader<T>::ProcessState::DumpStack()
|
||||
void TraceReader<T>::ProcessState::DumpStack(FILE *stream)
|
||||
{
|
||||
const char *native;
|
||||
for (int ii = 0; ii < method_stack_top; ii++) {
|
||||
printf("%2d: 0x%08x\n", ii, method_stack[ii]);
|
||||
native = method_stack[ii].isNative ? "n" : " ";
|
||||
fprintf(stream, "%2d: %s 0x%08x\n", ii, native, method_stack[ii].addr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1277,13 +1396,17 @@ void TraceReader<T>::HandleMethodRecord(ProcessState *pstate,
|
||||
{
|
||||
uint32_t addr;
|
||||
int top = pstate->method_stack_top;
|
||||
if (method_rec->flags == kMethodEnter) {
|
||||
int flags = method_rec->flags;
|
||||
bool isNative;
|
||||
if (flags == kMethodEnter || flags == kNativeEnter) {
|
||||
// Push this method on the stack
|
||||
if (top >= pstate->kMaxMethodStackSize) {
|
||||
fprintf(stderr, "Stack overflow at time %llu\n", method_rec->time);
|
||||
exit(1);
|
||||
}
|
||||
pstate->method_stack[top] = method_rec->addr;
|
||||
pstate->method_stack[top].addr = method_rec->addr;
|
||||
isNative = (flags == kNativeEnter);
|
||||
pstate->method_stack[top].isNative = isNative;
|
||||
pstate->method_stack_top = top + 1;
|
||||
addr = method_rec->addr;
|
||||
} else {
|
||||
@@ -1293,14 +1416,27 @@ void TraceReader<T>::HandleMethodRecord(ProcessState *pstate,
|
||||
return;
|
||||
}
|
||||
top -= 1;
|
||||
addr = pstate->method_stack[top];
|
||||
if (addr != method_rec->addr) {
|
||||
addr = pstate->method_stack[top].addr;
|
||||
|
||||
// If this is a non-native method then the address we are popping should
|
||||
// match the top-of-stack address. Native pops don't always match the
|
||||
// address of the native push for some reason.
|
||||
if (addr != method_rec->addr && !pstate->method_stack[top].isNative) {
|
||||
fprintf(stderr,
|
||||
"Stack method (0x%x) at index %d does not match trace record (0x%x) at time %llu\n",
|
||||
addr, top, method_rec->addr, method_rec->time);
|
||||
for (int ii = 0; ii <= top; ii++) {
|
||||
fprintf(stderr, " %d: 0x%x\n", ii, pstate->method_stack[ii]);
|
||||
}
|
||||
pstate->DumpStack(stderr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// If we are popping a native method, then the top-of-stack should also
|
||||
// be a native method.
|
||||
bool poppingNative = (flags == kNativeExit) || (flags == kNativeException);
|
||||
if (poppingNative != pstate->method_stack[top].isNative) {
|
||||
fprintf(stderr,
|
||||
"Popping native vs. non-native mismatch at index %d time %llu\n",
|
||||
top, method_rec->time);
|
||||
pstate->DumpStack(stderr);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
@@ -1310,8 +1446,17 @@ void TraceReader<T>::HandleMethodRecord(ProcessState *pstate,
|
||||
pstate->current_method_sym = NULL;
|
||||
return;
|
||||
}
|
||||
addr = pstate->method_stack[top - 1];
|
||||
addr = pstate->method_stack[top - 1].addr;
|
||||
isNative = pstate->method_stack[top - 1].isNative;
|
||||
}
|
||||
|
||||
// If the top-of-stack is a native method, then set the current method
|
||||
// to NULL.
|
||||
if (isNative) {
|
||||
pstate->current_method_sym = NULL;
|
||||
return;
|
||||
}
|
||||
|
||||
ProcessState *manager = pstate->addr_manager;
|
||||
region_type *region = FindRegion(addr, manager->nregions, manager->regions);
|
||||
uint32_t sym_addr = addr - region->base_addr;
|
||||
@@ -1324,6 +1469,11 @@ void TraceReader<T>::HandleMethodRecord(ProcessState *pstate,
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the current top-of-stack Java method, if any, for the given pid
|
||||
// at the given time. The "time" parameter must be monotonically increasing
|
||||
// across successive calls to this method.
|
||||
// If the Java method stack is empty or if a native JNI method is on the
|
||||
// top of the stack, then this method returns NULL.
|
||||
template <class T>
|
||||
typename TraceReader<T>::symbol_type*
|
||||
TraceReader<T>::FindCurrentMethod(int pid, uint64_t time)
|
||||
|
||||
Reference in New Issue
Block a user