aboutsummaryrefslogtreecommitdiffstats
path: root/emulator/qtools/trace_reader.h
diff options
context:
space:
mode:
Diffstat (limited to 'emulator/qtools/trace_reader.h')
-rw-r--r--emulator/qtools/trace_reader.h212
1 files changed, 181 insertions, 31 deletions
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)