diff options
author | Android (Google) Code Review <android-gerrit@google.com> | 2009-05-19 17:34:56 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-05-19 17:34:56 -0700 |
commit | bd6e9b0dc3e8443ca09130e1be89b79dfdd55c21 (patch) | |
tree | 7ba8be4c25b323a92435a2464f9d846cfc131ff3 | |
parent | bee73fa01740e5d6bb6703db1e9c7bcecb01e877 (diff) | |
parent | 068103ba1d8d4d8cf3ca94384f0ed31a0cd88202 (diff) | |
download | sdk-bd6e9b0dc3e8443ca09130e1be89b79dfdd55c21.zip sdk-bd6e9b0dc3e8443ca09130e1be89b79dfdd55c21.tar.gz sdk-bd6e9b0dc3e8443ca09130e1be89b79dfdd55c21.tar.bz2 |
am b08d3a39: Merge change 2004 into donut
Merge commit 'b08d3a396914b1aaba1270a9021289bdbe3cac5f'
* commit 'b08d3a396914b1aaba1270a9021289bdbe3cac5f':
Handle munmap() and add support for tracing JNI (native) calls.
-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) |