// 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); } }