| // Copyright 2009 The Android Open Source Project |
| |
| #include <stdio.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <inttypes.h> |
| #include <assert.h> |
| #include "trace_reader.h" |
| #include "bitvector.h" |
| #include "parse_options.h" |
| #include "armdis.h" |
| |
| typedef TraceReader<> TraceReaderType; |
| |
| #include "parse_options-inl.h" |
| #include "callstack.h" |
| |
| typedef CallStack<StackFrame<symbol_type> > CallStackType; |
| |
| void compareStacks(uint64_t time, int pid); |
| void dumpStacks(int pid); |
| |
| static uint64_t debugTime; |
| static const int kNumStackFrames = 500; |
| static const int kMaxThreads = (32 * 1024); |
| CallStackType *eStacks[kMaxThreads]; |
| |
| int numErrors; |
| static const int kMaxErrors = 3; |
| |
| struct frame { |
| uint64_t time; |
| uint32_t addr; |
| const char *name; |
| bool isNative; |
| |
| frame(uint64_t time, uint32_t addr, const char *name, bool isNative) { |
| this->time = time; |
| this->addr = addr; |
| this->name = name; |
| this->isNative = isNative; |
| } |
| }; |
| |
| class Stack { |
| public: |
| static const int kMaxFrames = 1000; |
| int top; |
| frame *frames[kMaxFrames]; |
| |
| Stack() { |
| top = 0; |
| } |
| |
| void push(frame *pframe); |
| frame* pop(); |
| void dump(); |
| }; |
| |
| void Stack::push(frame *pframe) { |
| if (top == kMaxFrames) { |
| fprintf(stderr, "Error: stack overflow\n"); |
| exit(1); |
| } |
| frames[top] = pframe; |
| top += 1; |
| } |
| |
| frame *Stack::pop() { |
| if (top <= 0) |
| return NULL; |
| top -= 1; |
| return frames[top]; |
| } |
| |
| Stack *mStacks[kMaxThreads]; |
| |
| void Usage(const char *program) |
| { |
| fprintf(stderr, "Usage: %s [options] trace_name elf_file\n", |
| program); |
| OptionsUsage(); |
| } |
| |
| int main(int argc, char **argv) |
| { |
| ParseOptions(argc, argv); |
| if (argc - optind != 2) { |
| Usage(argv[0]); |
| exit(1); |
| } |
| |
| char *qemu_trace_file = argv[optind++]; |
| char *elf_file = argv[optind++]; |
| |
| TraceReaderType *etrace = new TraceReaderType; |
| etrace->Open(qemu_trace_file); |
| etrace->ReadKernelSymbols(elf_file); |
| etrace->SetRoot(root); |
| |
| TraceReaderType *mtrace = new TraceReaderType; |
| mtrace->Open(qemu_trace_file); |
| mtrace->ReadKernelSymbols(elf_file); |
| mtrace->SetRoot(root); |
| |
| BBEvent event; |
| while (1) { |
| BBEvent ignored; |
| symbol_type *function; |
| MethodRec method_record; |
| symbol_type *sym; |
| TraceReaderType::ProcessState *proc; |
| frame *pframe; |
| |
| if (mtrace->ReadMethodSymbol(&method_record, &sym, &proc)) |
| break; |
| |
| if (!IsValidPid(proc->pid)) |
| continue; |
| |
| // Get the stack for the current thread |
| Stack *mStack = mStacks[proc->pid]; |
| |
| // If the stack does not exist, then allocate a new one. |
| if (mStack == NULL) { |
| mStack = new Stack(); |
| mStacks[proc->pid] = mStack; |
| } |
| |
| int flags = method_record.flags; |
| if (flags == kMethodEnter || flags == kNativeEnter) { |
| pframe = new frame(method_record.time, method_record.addr, |
| sym == NULL ? NULL: sym->name, |
| method_record.flags == kNativeEnter); |
| mStack->push(pframe); |
| } else { |
| pframe = mStack->pop(); |
| delete pframe; |
| } |
| |
| do { |
| if (GetNextValidEvent(etrace, &event, &ignored, &function)) |
| break; |
| if (event.bb_num == 0) |
| break; |
| |
| // Get the stack for the current thread |
| CallStackType *eStack = eStacks[event.pid]; |
| |
| // If the stack does not exist, then allocate a new one. |
| if (eStack == NULL) { |
| eStack = new CallStackType(event.pid, kNumStackFrames, etrace); |
| eStacks[event.pid] = eStack; |
| } |
| if (debugTime != 0 && event.time >= debugTime) |
| printf("time: %llu debug time: %lld\n", event.time, debugTime); |
| |
| // Update the stack |
| eStack->updateStack(&event, function); |
| } while (event.time < method_record.time); |
| |
| compareStacks(event.time, event.pid); |
| } |
| |
| for (int ii = 0; ii < kMaxThreads; ++ii) { |
| if (eStacks[ii]) |
| eStacks[ii]->popAll(event.time); |
| } |
| |
| delete etrace; |
| delete mtrace; |
| return 0; |
| } |
| |
| void compareStacks(uint64_t time, int pid) { |
| CallStackType *eStack = eStacks[pid]; |
| Stack *mStack = mStacks[pid]; |
| frame **mFrames = mStack->frames; |
| frame *mframe; |
| |
| int mTop = mStack->top; |
| int eTop = eStack->mTop; |
| CallStackType::frame_type *eFrames = eStack->mFrames; |
| |
| // Count the number of non-native methods (ie, Java methods) on the |
| // Java method stack |
| int numNonNativeMethods = 0; |
| for (int ii = 0; ii < mTop; ++ii) { |
| if (!mFrames[ii]->isNative) { |
| numNonNativeMethods += 1; |
| } |
| } |
| |
| // Count the number of Java methods on the native stack |
| int numMethods = 0; |
| for (int ii = 0; ii < eTop; ++ii) { |
| if (eFrames[ii].flags & CallStackType::frame_type::kInterpreted) { |
| numMethods += 1; |
| } |
| } |
| |
| // Verify that the number of Java methods on both stacks are the same. |
| // Allow the native stack to have one less Java method because the |
| // native stack might be pushing a native function first. |
| if (numNonNativeMethods != numMethods && numNonNativeMethods != numMethods + 1) { |
| printf("\nDiff at time %llu pid %d: non-native %d numMethods %d\n", |
| time, pid, numNonNativeMethods, numMethods); |
| dumpStacks(pid); |
| numErrors += 1; |
| if (numErrors >= kMaxErrors) |
| exit(1); |
| } |
| |
| // Verify that the Java methods on the method stack are the same |
| // as the Java methods on the native stack. |
| int mIndex = 0; |
| for (int ii = 0; ii < eTop; ++ii) { |
| // Ignore native functions on the native stack. |
| if ((eFrames[ii].flags & CallStackType::frame_type::kInterpreted) == 0) |
| continue; |
| uint32_t addr = eFrames[ii].function->addr; |
| addr += eFrames[ii].function->region->vstart; |
| while (mIndex < mTop && mFrames[mIndex]->isNative) { |
| mIndex += 1; |
| } |
| if (mIndex >= mTop) |
| break; |
| if (addr != mFrames[mIndex]->addr) { |
| printf("\nDiff at time %llu pid %d: frame %d\n", time, pid, ii); |
| dumpStacks(pid); |
| exit(1); |
| } |
| mIndex += 1; |
| } |
| } |
| |
| void dumpStacks(int pid) { |
| CallStackType *eStack = eStacks[pid]; |
| Stack *mStack = mStacks[pid]; |
| frame *mframe; |
| |
| int mTop = mStack->top; |
| printf("\nJava method stack\n"); |
| for (int ii = 0; ii < mTop; ii++) { |
| mframe = mStack->frames[ii]; |
| const char *native = mframe->isNative ? "n" : " "; |
| printf(" %s %d: %llu 0x%x %s\n", |
| native, ii, mframe->time, mframe->addr, |
| mframe->name == NULL ? "" : mframe->name); |
| } |
| |
| int eTop = eStack->mTop; |
| CallStackType::frame_type *eFrames = eStack->mFrames; |
| int mIndex = 0; |
| printf("\nNative stack\n"); |
| for (int ii = 0; ii < eTop; ++ii) { |
| uint32_t addr = eFrames[ii].function->addr; |
| addr += eFrames[ii].function->region->vstart; |
| const char *marker = " "; |
| if (eFrames[ii].flags & CallStackType::frame_type::kInterpreted) { |
| if (mIndex >= mTop || addr != mStack->frames[mIndex]->addr) { |
| marker = "*"; |
| } |
| mIndex += 1; |
| } |
| printf(" %s %d: %d f %d 0x%08x %s\n", |
| marker, ii, eFrames[ii].time, eFrames[ii].flags, addr, |
| eFrames[ii].function->name); |
| } |
| } |