271 lines
7.5 KiB
C++
271 lines
7.5 KiB
C++
// 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);
|
|
}
|
|
}
|