aboutsummaryrefslogtreecommitdiffstats
path: root/emulator/qtools/trace_reader.h
diff options
context:
space:
mode:
authorJack Veenstra <veenstra@android.com>2009-05-19 15:07:29 -0700
committerJack Veenstra <veenstra@android.com>2009-05-19 15:07:29 -0700
commitf3c2a68229b30e88d0645c1853e9a4ddc8154995 (patch)
tree3953c158618eb8e7f92ef040a9c2e41a0e6b073b /emulator/qtools/trace_reader.h
parent7357369c643d3c53e33c64593965840b42ce2d00 (diff)
downloadsdk-f3c2a68229b30e88d0645c1853e9a4ddc8154995.zip
sdk-f3c2a68229b30e88d0645c1853e9a4ddc8154995.tar.gz
sdk-f3c2a68229b30e88d0645c1853e9a4ddc8154995.tar.bz2
Handle munmap() and add support for tracing JNI (native) calls.
The munmap() kernel calls are traced but the tracing code wasn't doing anything with them. This caused the number of mapped regions in a process to grow large in some cases and also caused symbol lookup errors in some rare cases. This change also adds support for new trace record types for supporting JNI (native) calls from Java into native code. This helps with constructing a more accurate call stack.
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)