diff options
author | Jack Veenstra <veenstra@android.com> | 2009-05-19 15:07:29 -0700 |
---|---|---|
committer | Jack Veenstra <veenstra@android.com> | 2009-05-19 15:07:29 -0700 |
commit | f3c2a68229b30e88d0645c1853e9a4ddc8154995 (patch) | |
tree | 3953c158618eb8e7f92ef040a9c2e41a0e6b073b | |
parent | 7357369c643d3c53e33c64593965840b42ce2d00 (diff) | |
download | sdk-f3c2a68229b30e88d0645c1853e9a4ddc8154995.zip sdk-f3c2a68229b30e88d0645c1853e9a4ddc8154995.tar.gz sdk-f3c2a68229b30e88d0645c1853e9a4ddc8154995.tar.bz2 |
Handle munmap() and add support for tracing JNI (native) calls.
The munmap() kernel calls are traced but the tracing code wasn't doing
anything with them. This caused the number of mapped regions in a process
to grow large in some cases and also caused symbol lookup errors in some
rare cases. This change also adds support for new trace record types
for supporting JNI (native) calls from Java into native code. This helps
with constructing a more accurate call stack.
-rw-r--r-- | emulator/qtools/callstack.h | 250 | ||||
-rw-r--r-- | emulator/qtools/hash_table.h | 26 | ||||
-rw-r--r-- | emulator/qtools/trace_reader.h | 212 |
3 files changed, 330 insertions, 158 deletions
diff --git a/emulator/qtools/callstack.h b/emulator/qtools/callstack.h index b73efea..8982330 100644 --- a/emulator/qtools/callstack.h +++ b/emulator/qtools/callstack.h @@ -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; - - // 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 (mNativeOnly) + return; + + // 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; - } - - // 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); + 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); } - // Push the new frames from the native stack - for (int jj = ii; jj < mTop; ++jj) { - mFrames[jj].push(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; } - 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 */ diff --git a/emulator/qtools/hash_table.h b/emulator/qtools/hash_table.h index 45786ec..4ea9ed5 100644 --- a/emulator/qtools/hash_table.h +++ b/emulator/qtools/hash_table.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(); @@ -121,6 +122,31 @@ void HashTable<T>::Update(const char *key, T value) } 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) { // Hash the key to get the table position diff --git a/emulator/qtools/trace_reader.h b/emulator/qtools/trace_reader.h index f73f17a..b91cb1b 100644 --- a/emulator/qtools/trace_reader.h +++ b/emulator/qtools/trace_reader.h @@ -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); @@ -926,6 +949,63 @@ void TraceReader<T>::AddRegion(ProcessState *pstate, region_type *region) } 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) { // Copy the parent's address space @@ -944,6 +1024,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) { @@ -968,6 +1062,30 @@ TraceReader<T>::FindRegion(uint32_t addr, int nregions, region_type **regions) } 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, bool exact_match) @@ -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) |