aboutsummaryrefslogtreecommitdiffstats
path: root/emulator/qtools/trace_reader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'emulator/qtools/trace_reader.cpp')
-rw-r--r--emulator/qtools/trace_reader.cpp1201
1 files changed, 1201 insertions, 0 deletions
diff --git a/emulator/qtools/trace_reader.cpp b/emulator/qtools/trace_reader.cpp
new file mode 100644
index 0000000..b38c0b4
--- /dev/null
+++ b/emulator/qtools/trace_reader.cpp
@@ -0,0 +1,1201 @@
+// Copyright 2006 The Android Open Source Project
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include <inttypes.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <elf.h>
+#include "trace_reader.h"
+#include "decoder.h"
+
+// A struct for creating temporary linked-lists of DexSym structs
+struct DexSymList {
+ DexSymList *next;
+ DexSym sym;
+};
+
+// Declare static functions used in this file
+static char *ExtractDexPathFromMmap(const char *mmap_path);
+static void CopyDexSymbolsToArray(DexFileList *dexfile,
+ DexSymList *head, int num_symbols);
+
+// This function creates the pathname to the a specific trace file. The
+// string space is allocated in this routine and must be freed by the
+// caller.
+static char *CreateTracePath(const char *filename, const char *ext)
+{
+ char *fname;
+ const char *base_start, *base_end;
+ int ii, len, base_len, dir_len, path_len, qtrace_len;
+
+ // Handle error cases
+ if (filename == NULL || *filename == 0 || strcmp(filename, "/") == 0)
+ return NULL;
+
+ // Ignore a trailing slash, if any
+ len = strlen(filename);
+ if (filename[len - 1] == '/')
+ len -= 1;
+
+ // Find the basename. We don't use basename(3) because there are
+ // different behaviors for GNU and Posix in the case where the
+ // last character is a slash.
+ base_start = base_end = &filename[len];
+ for (ii = 0; ii < len; ++ii) {
+ base_start -= 1;
+ if (*base_start == '/') {
+ base_start += 1;
+ break;
+ }
+ }
+ base_len = base_end - base_start;
+ dir_len = len - base_len;
+ qtrace_len = strlen("/qtrace");
+
+ // Create space for the pathname: "/dir/basename/qtrace.ext"
+ // The "ext" string already contains the dot, so just add a byte
+ // for the terminating zero.
+ path_len = dir_len + base_len + qtrace_len + strlen(ext) + 1;
+ fname = new char[path_len];
+ if (dir_len > 0)
+ strncpy(fname, filename, dir_len);
+ fname[dir_len] = 0;
+ strncat(fname, base_start, base_len);
+ strcat(fname, "/qtrace");
+ strcat(fname, ext);
+ return fname;
+}
+
+inline BBReader::Future *BBReader::AllocFuture()
+{
+ Future *future = free_;
+ free_ = free_->next;
+ return future;
+}
+
+inline void BBReader::FreeFuture(Future *future)
+{
+ future->next = free_;
+ free_ = future;
+}
+
+inline void BBReader::InsertFuture(Future *future)
+{
+ uint64_t future_time = future->bb.next_time;
+ Future *prev = NULL;
+ Future *ptr;
+ for (ptr = head_; ptr; prev = ptr, ptr = ptr->next) {
+ if (future_time <= ptr->bb.next_time)
+ break;
+ }
+ if (prev == NULL) {
+ // link it at the front
+ future->next = head_;
+ head_ = future;
+ } else {
+ // link it after "prev"
+ future->next = prev->next;
+ prev->next = future;
+ }
+}
+
+// Decodes the next basic block record from the file. Returns 1
+// at end-of-file, otherwise returns 0.
+inline int BBReader::DecodeNextRec()
+{
+ int64_t bb_diff = decoder_->Decode(true);
+ uint64_t time_diff = decoder_->Decode(false);
+ nextrec_.bb_rec.repeat = decoder_->Decode(false);
+ if (time_diff == 0)
+ return 1;
+ if (nextrec_.bb_rec.repeat)
+ nextrec_.bb_rec.time_diff = decoder_->Decode(false);
+ nextrec_.bb_rec.bb_num += bb_diff;
+ nextrec_.bb_rec.start_time += time_diff;
+ return 0;
+}
+
+BBReader::BBReader(TraceReaderBase *trace)
+{
+ trace_ = trace;
+ decoder_ = new Decoder;
+}
+
+BBReader::~BBReader()
+{
+ delete decoder_;
+}
+
+void BBReader::Open(char *filename)
+{
+ // Initialize the class variables
+ memset(&nextrec_, 0, sizeof(TimeRec));
+ memset(futures_, 0, sizeof(Future) * kMaxNumBasicBlocks);
+ head_ = NULL;
+
+ // Link all of the futures_[] array elements on the free list.
+ for (int ii = 0; ii < kMaxNumBasicBlocks - 1; ++ii) {
+ futures_[ii].next = &futures_[ii + 1];
+ }
+ futures_[kMaxNumBasicBlocks - 1].next = 0;
+ free_ = &futures_[0];
+
+ // Open the trace.bb file
+ char *fname = CreateTracePath(filename, ".bb");
+ decoder_->Open(fname);
+ is_eof_ = DecodeNextRec();
+ delete[] fname;
+}
+
+void BBReader::Close()
+{
+ decoder_->Close();
+}
+
+// Returns true at end of file.
+bool BBReader::ReadBB(BBEvent *event)
+{
+ if (is_eof_ && head_ == NULL) {
+ return true;
+ }
+
+#if 0
+ if (nextrec_) {
+ printf("nextrec: buffer[%d], bb_num: %lld start: %d diff %d repeat %d next %u\n",
+ nextrec_ - &buffer_[0],
+ nextrec_->bb_rec.bb_num, nextrec_->bb_rec.start_time,
+ nextrec_->bb_rec.time_diff, nextrec_->bb_rec.repeat,
+ nextrec_->next_time);
+ }
+ if (head_) {
+ printf("head: 0x%x, bb_num: %lld start: %d diff %d repeat %d next %u\n",
+ head_,
+ head_->bb->bb_rec.bb_num, head_->bb->bb_rec.start_time,
+ head_->bb->bb_rec.time_diff, head_->bb->bb_rec.repeat,
+ head_->bb->next_time);
+ }
+#endif
+ if (!is_eof_) {
+ if (head_) {
+ TimeRec *bb = &head_->bb;
+ if (bb->next_time < nextrec_.bb_rec.start_time) {
+ // The head is earlier.
+ event->time = bb->next_time;
+ event->bb_num = bb->bb_rec.bb_num;
+ event->bb_addr = trace_->GetBBAddr(event->bb_num);
+ event->insns = trace_->GetInsns(event->bb_num);
+ event->num_insns = trace_->FindNumInsns(event->bb_num, event->time);
+ event->pid = trace_->FindCurrentPid(event->time);
+ event->is_thumb = trace_->GetIsThumb(event->bb_num);
+
+ // Remove the head element from the list
+ Future *future = head_;
+ head_ = head_->next;
+ if (bb->bb_rec.repeat > 0) {
+ // there are more repetitions of this bb
+ bb->bb_rec.repeat -= 1;
+ bb->next_time += bb->bb_rec.time_diff;
+
+ // Insert this future into the sorted list
+ InsertFuture(future);
+ } else {
+ // Add this future to the free list
+ FreeFuture(future);
+ }
+ return false;
+ }
+ }
+ // The nextrec is earlier (or there was no head)
+ event->time = nextrec_.bb_rec.start_time;
+ event->bb_num = nextrec_.bb_rec.bb_num;
+ event->bb_addr = trace_->GetBBAddr(event->bb_num);
+ event->insns = trace_->GetInsns(event->bb_num);
+ event->num_insns = trace_->FindNumInsns(event->bb_num, event->time);
+ event->pid = trace_->FindCurrentPid(event->time);
+ event->is_thumb = trace_->GetIsThumb(event->bb_num);
+ if (nextrec_.bb_rec.repeat > 0) {
+ Future *future = AllocFuture();
+ future->bb.bb_rec = nextrec_.bb_rec;
+ future->bb.bb_rec.repeat -= 1;
+ future->bb.next_time = nextrec_.bb_rec.start_time + nextrec_.bb_rec.time_diff;
+ InsertFuture(future);
+ }
+
+ is_eof_ = DecodeNextRec();
+ return false;
+ }
+
+ //printf("using head_ 0x%x\n", head_);
+ assert(head_);
+ TimeRec *bb = &head_->bb;
+ event->time = bb->next_time;
+ event->bb_num = bb->bb_rec.bb_num;
+ event->bb_addr = trace_->GetBBAddr(event->bb_num);
+ event->insns = trace_->GetInsns(event->bb_num);
+ event->num_insns = trace_->FindNumInsns(event->bb_num, event->time);
+ event->pid = trace_->FindCurrentPid(event->time);
+ event->is_thumb = trace_->GetIsThumb(event->bb_num);
+
+ // Remove the head element from the list
+ Future *future = head_;
+ head_ = head_->next;
+ if (bb->bb_rec.repeat > 0) {
+ // there are more repetitions of this bb
+ bb->bb_rec.repeat -= 1;
+ bb->next_time += bb->bb_rec.time_diff;
+
+ // Insert this future into the sorted list
+ InsertFuture(future);
+ } else {
+ // Add this future to the free list
+ FreeFuture(future);
+ }
+ return false;
+}
+
+InsnReader::InsnReader()
+{
+ decoder_ = new Decoder;
+}
+
+InsnReader::~InsnReader()
+{
+ delete decoder_;
+}
+
+void InsnReader::Open(char *filename)
+{
+ prev_time_ = 0;
+ time_diff_ = 0;
+ repeat_ = -1;
+
+ // Open the trace.insn file
+ char *fname = CreateTracePath(filename, ".insn");
+ decoder_->Open(fname);
+ delete[] fname;
+}
+
+void InsnReader::Close()
+{
+ decoder_->Close();
+}
+
+uint64_t InsnReader::ReadInsnTime(uint64_t min_time)
+{
+ do {
+ if (repeat_ == -1) {
+ time_diff_ = decoder_->Decode(false);
+ repeat_ = decoder_->Decode(false);
+ }
+ prev_time_ += time_diff_;
+ repeat_ -= 1;
+ } while (prev_time_ < min_time);
+ return prev_time_;
+}
+
+AddrReader::AddrReader()
+{
+ decoder_ = new Decoder;
+ opened_ = false;
+}
+
+AddrReader::~AddrReader()
+{
+ delete decoder_;
+}
+
+// Returns true if there is an error opening the file
+bool AddrReader::Open(char *filename, char *suffix)
+{
+ struct stat stat_buf;
+
+ prev_addr_ = 0;
+ prev_time_ = 0;
+
+ // Open the trace.addr file
+ char *fname = CreateTracePath(filename, suffix);
+ int rval = stat(fname, &stat_buf);
+ if (rval == -1) {
+ // The file does not exist
+ delete[] fname;
+ return true;
+ }
+ decoder_->Open(fname);
+ opened_ = true;
+ delete[] fname;
+ return false;
+}
+
+void AddrReader::Close()
+{
+ decoder_->Close();
+}
+
+// Returns true at end of file.
+bool AddrReader::ReadAddr(uint64_t *time, uint32_t *addr)
+{
+ if (!opened_) {
+ fprintf(stderr, "Cannot read address trace\n");
+ exit(1);
+ }
+ uint32_t addr_diff = decoder_->Decode(true);
+ uint64_t time_diff = decoder_->Decode(false);
+ if (time_diff == 0 && addr_diff == 0) {
+ *addr = 0;
+ *time = 0;
+ return true;
+ }
+ prev_addr_ += addr_diff;
+ prev_time_ += time_diff;
+ *addr = prev_addr_;
+ *time = prev_time_;
+ return false;
+}
+
+ExcReader::ExcReader()
+{
+ decoder_ = new Decoder;
+}
+
+ExcReader::~ExcReader()
+{
+ delete decoder_;
+}
+
+void ExcReader::Open(char *filename)
+{
+ prev_time_ = 0;
+ prev_recnum_ = 0;
+
+ // Open the trace.exc file
+ char *fname = CreateTracePath(filename, ".exc");
+ decoder_->Open(fname);
+ delete[] fname;
+}
+
+void ExcReader::Close()
+{
+ decoder_->Close();
+}
+
+// Returns true at end of file.
+bool ExcReader::ReadExc(uint64_t *time, uint32_t *current_pc, uint64_t *recnum,
+ uint32_t *target_pc, uint64_t *bb_num,
+ uint64_t *bb_start_time, int *num_insns)
+{
+ uint64_t time_diff = decoder_->Decode(false);
+ uint32_t pc = decoder_->Decode(false);
+ if ((time_diff | pc) == 0) {
+ decoder_->Decode(false);
+ decoder_->Decode(false);
+ decoder_->Decode(false);
+ decoder_->Decode(false);
+ decoder_->Decode(false);
+ return true;
+ }
+ uint64_t recnum_diff = decoder_->Decode(false);
+ prev_time_ += time_diff;
+ prev_recnum_ += recnum_diff;
+ *time = prev_time_;
+ *current_pc = pc;
+ *recnum = prev_recnum_;
+ *target_pc = decoder_->Decode(false);
+ *bb_num = decoder_->Decode(false);
+ *bb_start_time = decoder_->Decode(false);
+ *num_insns = decoder_->Decode(false);
+ return false;
+}
+
+PidReader::PidReader()
+{
+ decoder_ = new Decoder;
+}
+
+PidReader::~PidReader()
+{
+ delete decoder_;
+}
+
+void PidReader::Open(char *filename)
+{
+ prev_time_ = 0;
+
+ // Open the trace.pid file
+ char *fname = CreateTracePath(filename, ".pid");
+ decoder_->Open(fname);
+ delete[] fname;
+}
+
+void PidReader::Close()
+{
+ decoder_->Close();
+}
+
+// Returns true at end of file.
+bool PidReader::ReadPidEvent(PidEvent *event)
+{
+ uint64_t time_diff = decoder_->Decode(false);
+ int rec_type = decoder_->Decode(false);
+ prev_time_ += time_diff;
+ event->time = prev_time_;
+ event->rec_type = rec_type;
+ switch(rec_type) {
+ case kPidEndOfFile:
+ return true;
+ case kPidSwitch:
+ case kPidExit:
+ event->pid = decoder_->Decode(false);
+ break;
+ case kPidFork:
+ case kPidClone:
+ event->tgid = decoder_->Decode(false);
+ event->pid = decoder_->Decode(false);
+ break;
+ case kPidMmap:
+ {
+ event->vstart = decoder_->Decode(false);
+ event->vend = decoder_->Decode(false);
+ event->offset = decoder_->Decode(false);
+ int len = decoder_->Decode(false);
+ char *path = new char[len + 1];
+ decoder_->Read(path, len);
+ path[len] = 0;
+ event->path = path;
+ event->mmap_path = path;
+ char *dexfile = ExtractDexPathFromMmap(path);
+ if (dexfile != NULL) {
+ delete[] event->path;
+ event->path = dexfile;
+ }
+ }
+ break;
+ case kPidMunmap:
+ {
+ event->vstart = decoder_->Decode(false);
+ event->vend = decoder_->Decode(false);
+ }
+ break;
+ case kPidSymbolAdd:
+ {
+ event->vstart = decoder_->Decode(false);
+ int len = decoder_->Decode(false);
+ char *path = new char[len + 1];
+ decoder_->Read(path, len);
+ path[len] = 0;
+ event->path = path;
+ }
+ break;
+ case kPidSymbolRemove:
+ event->vstart = decoder_->Decode(false);
+ break;
+ case kPidExec:
+ {
+ int argc = decoder_->Decode(false);
+ event->argc = argc;
+ char **argv = new char*[argc];
+ event->argv = argv;
+ for (int ii = 0; ii < argc; ++ii) {
+ int alen = decoder_->Decode(false);
+ argv[ii] = new char[alen + 1];
+ decoder_->Read(argv[ii], alen);
+ argv[ii][alen] = 0;
+ }
+ }
+ break;
+ case kPidName:
+ case kPidKthreadName:
+ {
+ if (rec_type == kPidKthreadName) {
+ event->tgid = decoder_->Decode(false);
+ }
+ event->pid = decoder_->Decode(false);
+ int len = decoder_->Decode(false);
+ char *path = new char[len + 1];
+ decoder_->Read(path, len);
+ path[len] = 0;
+ event->path = path;
+ }
+ break;
+ }
+ return false;
+}
+
+// Frees the memory that might have been allocated for the given event.
+void PidReader::Dispose(PidEvent *event)
+{
+ switch(event->rec_type) {
+ case kPidMmap:
+ case kPidSymbolAdd:
+ case kPidName:
+ case kPidKthreadName:
+ delete[] event->path;
+ event->path = NULL;
+ event->mmap_path = NULL;
+ break;
+
+ case kPidExec:
+ for (int ii = 0; ii < event->argc; ++ii) {
+ delete[] event->argv[ii];
+ }
+ delete[] event->argv;
+ event->argv = NULL;
+ event->argc = 0;
+ break;
+ }
+}
+
+
+MethodReader::MethodReader()
+{
+ decoder_ = new Decoder;
+ opened_ = false;
+}
+
+MethodReader::~MethodReader()
+{
+ delete decoder_;
+}
+
+bool MethodReader::Open(char *filename)
+{
+ struct stat stat_buf;
+
+ prev_time_ = 0;
+ prev_addr_ = 0;
+ prev_pid_ = 0;
+
+ // Open the trace.method file
+ char *fname = CreateTracePath(filename, ".method");
+ int rval = stat(fname, &stat_buf);
+ if (rval == -1) {
+ // The file does not exist
+ delete[] fname;
+ return true;
+ }
+ decoder_->Open(fname);
+ delete[] fname;
+ opened_ = true;
+ return false;
+}
+
+void MethodReader::Close()
+{
+ decoder_->Close();
+}
+
+// Returns true at end of file.
+bool MethodReader::ReadMethod(MethodRec *method_record)
+{
+ if (!opened_)
+ return true;
+ uint64_t time_diff = decoder_->Decode(false);
+ int32_t addr_diff = decoder_->Decode(true);
+ if (time_diff == 0) {
+ method_record->time = 0;
+ method_record->addr = 0;
+ method_record->flags = 0;
+ return true;
+ }
+ int32_t pid_diff = decoder_->Decode(true);
+ prev_time_ += time_diff;
+ prev_addr_ += addr_diff;
+ prev_pid_ += pid_diff;
+ method_record->time = prev_time_;
+ method_record->addr = prev_addr_;
+ method_record->pid = prev_pid_;
+ method_record->flags = decoder_->Decode(false);
+ return false;
+}
+
+TraceReaderBase::TraceReaderBase()
+{
+ static_filename_ = NULL;
+ static_fstream_ = NULL;
+ header_ = new TraceHeader;
+ bb_reader_ = new BBReader(this);
+ insn_reader_ = new InsnReader;
+ load_addr_reader_ = new AddrReader;
+ store_addr_reader_ = new AddrReader;
+ exc_reader_ = new ExcReader;
+ pid_reader_ = new PidReader;
+ method_reader_ = new MethodReader;
+ internal_exc_reader_ = new ExcReader;
+ internal_pid_reader_ = new PidReader;
+ internal_method_reader_ = new MethodReader;
+ blocks_ = NULL;
+ bb_recnum_ = 0;
+ exc_recnum_ = 0;
+ exc_end_ = false;
+ exc_bb_num_ = 0;
+ exc_time_ = 0;
+ exc_num_insns_ = 0;
+ current_pid_ = 0;
+ next_pid_ = 0;
+ next_pid_switch_time_ = 0;
+ post_processing_ = false;
+ dex_hash_ = NULL;
+ load_eof_ = false;
+ load_time_ = 0;
+ load_addr_ = 0;
+ store_eof_ = false;
+ store_time_ = 0;
+ store_addr_ = 0;
+}
+
+TraceReaderBase::~TraceReaderBase()
+{
+ Close();
+ delete bb_reader_;
+ delete insn_reader_;
+ delete load_addr_reader_;
+ delete store_addr_reader_;
+ delete exc_reader_;
+ delete pid_reader_;
+ delete method_reader_;
+ delete internal_exc_reader_;
+ delete internal_pid_reader_;
+ delete internal_method_reader_;
+ if (blocks_) {
+ int num_static_bb = header_->num_static_bb;
+ for (int ii = 0; ii < num_static_bb; ++ii) {
+ delete[] blocks_[ii].insns;
+ }
+ delete[] blocks_;
+ }
+ delete header_;
+ if (dex_hash_ != NULL) {
+ HashTable<DexFileList*>::entry_type *ptr;
+ for (ptr = dex_hash_->GetFirst(); ptr; ptr = dex_hash_->GetNext()) {
+ DexFileList *dexfile = ptr->value;
+ delete[] dexfile->path;
+ int nsymbols = dexfile->nsymbols;
+ DexSym *symbols = dexfile->symbols;
+ for (int ii = 0; ii < nsymbols; ii++) {
+ delete[] symbols[ii].name;
+ }
+ delete[] dexfile->symbols;
+ delete dexfile;
+ }
+ }
+ delete dex_hash_;
+ delete[] static_filename_;
+}
+
+void TraceReaderBase::ReadTraceHeader(FILE *fstream, char *filename,
+ char *tracename, TraceHeader *header)
+{
+ int rval = fread(header, sizeof(TraceHeader), 1, fstream);
+ if (rval != 1) {
+ perror(filename);
+ exit(1);
+ }
+
+ if (!post_processing_ && strcmp(header->ident, TRACE_IDENT) != 0) {
+ fprintf(stderr, "%s: missing trace header; run 'post_trace %s' first\n",
+ filename, tracename);
+ exit(1);
+ }
+
+ if (header->version != TRACE_VERSION) {
+ fprintf(stderr,
+ "%s: trace header version (%d) does not match compiled tools version (%d)\n",
+ tracename, header->version, TRACE_VERSION);
+ exit(1);
+ }
+
+ convert32(header->version);
+ convert32(header->start_sec);
+ convert32(header->start_usec);
+ convert32(header->pdate);
+ convert32(header->ptime);
+ convert64(header->num_static_bb);
+ convert64(header->num_static_insn);
+ convert64(header->num_dynamic_bb);
+ convert64(header->num_dynamic_insn);
+ convert64(header->elapsed_usecs);
+}
+
+
+void TraceReaderBase::Open(char *filename)
+{
+ char *fname;
+ FILE *fstream;
+
+ // Open the qtrace.bb file
+ bb_reader_->Open(filename);
+
+ // Open the qtrace.insn file
+ insn_reader_->Open(filename);
+
+ // Open the qtrace.load file and read the first line
+ load_eof_ = load_addr_reader_->Open(filename, ".load");
+ if (!load_eof_)
+ load_eof_ = load_addr_reader_->ReadAddr(&load_time_, &load_addr_);
+
+ // Open the qtrace.store file and read the first line
+ store_eof_ = store_addr_reader_->Open(filename, ".store");
+ if (!store_eof_)
+ store_eof_ = store_addr_reader_->ReadAddr(&store_time_, &store_addr_);
+
+ // Open the qtrace.exc file
+ exc_reader_->Open(filename);
+
+ // Open another file stream to the qtrace.exc file for internal reads.
+ // This allows the caller to also read from the qtrace.exc file.
+ internal_exc_reader_->Open(filename);
+
+ // Open the qtrace.pid file
+ pid_reader_->Open(filename);
+ internal_pid_reader_->Open(filename);
+
+ // Open the qtrace.method file
+ method_reader_->Open(filename);
+ internal_method_reader_->Open(filename);
+
+ // Open the qtrace.static file
+ fname = CreateTracePath(filename, ".static");
+ static_filename_ = fname;
+
+ fstream = fopen(fname, "r");
+ if (fstream == NULL) {
+ perror(fname);
+ exit(1);
+ }
+ static_fstream_ = fstream;
+
+ // Read the header
+ ReadTraceHeader(fstream, fname, filename, header_);
+
+ // Allocate space for all of the static blocks
+ int num_static_bb = header_->num_static_bb;
+ if (num_static_bb) {
+ blocks_ = new StaticBlock[num_static_bb];
+
+ // Read in all the static blocks
+ for (int ii = 0; ii < num_static_bb; ++ii) {
+ ReadStatic(&blocks_[ii].rec);
+ int num_insns = blocks_[ii].rec.num_insns;
+ if (num_insns > 0) {
+ blocks_[ii].insns = new uint32_t[num_insns];
+ ReadStaticInsns(num_insns, blocks_[ii].insns);
+ } else {
+ blocks_[ii].insns = NULL;
+ }
+ }
+ fseek(static_fstream_, sizeof(TraceHeader), SEEK_SET);
+ }
+
+ ParseDexList(filename);
+
+ // If the dex_hash_ is NULL, then assign it a small hash table
+ // so that we can simply do a Find() operation without having
+ // to check for NULL first.
+ if (dex_hash_ == NULL) {
+ dex_hash_ = new HashTable<DexFileList*>(1, NULL);
+ }
+}
+
+// Reads the list of pid events looking for an mmap of a dex file.
+PidEvent * TraceReaderBase::FindMmapDexFileEvent()
+{
+ static PidEvent event;
+
+ while (!pid_reader_->ReadPidEvent(&event)) {
+ if (event.rec_type == kPidMmap && event.path != event.mmap_path) {
+ return &event;
+ }
+ pid_reader_->Dispose(&event);
+ }
+ return NULL;
+}
+
+static void CopyDexSymbolsToArray(DexFileList *dexfile,
+ DexSymList *head, int num_symbols)
+{
+ if (dexfile == NULL)
+ return;
+
+ DexSym *symbols = NULL;
+ if (num_symbols > 0) {
+ symbols = new DexSym[num_symbols];
+ }
+ dexfile->nsymbols = num_symbols;
+ dexfile->symbols = symbols;
+
+ // Copy the linked-list to the array.
+ DexSymList *next_sym = NULL;
+ int next_index = 0;
+ for (DexSymList *sym = head; sym; sym = next_sym) {
+ next_sym = sym->next;
+ symbols[next_index].addr = sym->sym.addr;
+ symbols[next_index].len = sym->sym.len;
+ symbols[next_index].name = sym->sym.name;
+ next_index += 1;
+ delete sym;
+ }
+}
+
+void TraceReaderBase::ParseDexList(char *filename)
+{
+ struct stat stat_buf;
+ static const int kBufSize = 4096;
+ char buf[kBufSize];
+ char current_file[kBufSize];
+
+ // Find an example dex file in the list of mmaps
+ PidEvent *event = FindMmapDexFileEvent();
+
+ // Reset the pid_reader to the beginning of the file.
+ pid_reader_->Close();
+ pid_reader_->Open(filename);
+
+ // If there were no mmapped dex files, then there is no need to parse
+ // the dexlist.
+ if (event == NULL)
+ return;
+ char *mmap_dexfile = event->path;
+
+ // Check if the dexlist file exists. It should have the name
+ // "qtrace.dexlist"
+ char *fname = CreateTracePath(filename, ".dexlist");
+ int rval = stat(fname, &stat_buf);
+ if (rval == -1) {
+ // The file does not exist
+ delete[] fname;
+ return;
+ }
+
+ // Open the qtrace.dexlist file
+ FILE *fstream = fopen(fname, "r");
+ if (fstream == NULL) {
+ perror(fname);
+ exit(1);
+ }
+
+ // First pass: read all the filenames, looking for a match for the
+ // example mmap dex filename. Also count the files so that we
+ // know how big to make the hash table.
+ char *match = NULL;
+ int num_files = 0;
+ while (fgets(buf, kBufSize, fstream)) {
+ if (buf[0] != '#')
+ continue;
+ num_files += 1;
+ match = strstr(buf + 1, mmap_dexfile);
+
+ // Check that the dexlist file ends with the string mmap_dexfile.
+ // We add one to the length of the mmap_dexfile because buf[]
+ // ends with a newline. The strlen(mmap_dexfile) computation
+ // could be moved above the loop but it should only ever be
+ // executed once.
+ if (match != NULL && strlen(match) == strlen(mmap_dexfile) + 1)
+ break;
+ }
+
+ // Count the rest of the files
+ while (fgets(buf, kBufSize, fstream)) {
+ if (buf[0] == '#')
+ num_files += 1;
+ }
+
+ if (match == NULL) {
+ fprintf(stderr,
+ "Cannot find the mmapped dex file '%s' in the dexlist\n",
+ mmap_dexfile);
+ exit(1);
+ }
+ delete[] mmap_dexfile;
+
+ // The prefix length includes the leading '#'.
+ int prefix_len = match - buf;
+
+ // Allocate a hash table
+ dex_hash_ = new HashTable<DexFileList*>(4 * num_files, NULL);
+
+ // Reset the file stream to the beginning
+ rewind(fstream);
+
+ // Second pass: read the filenames, stripping off the common prefix.
+ // And read all the (address, method) mappings. When we read a new
+ // filename, create a new DexFileList and add it to the hash table.
+ // Add new symbol mappings to a linked list until we have the whole
+ // list and then create an array for them so that we can use binary
+ // search on the address to find the symbol name quickly.
+
+ // Use a linked list for storing the symbols
+ DexSymList *head = NULL;
+ DexSymList *prev = NULL;
+ int num_symbols = 0;
+
+ DexFileList *dexfile = NULL;
+ int linenum = 0;
+ while (fgets(buf, kBufSize, fstream)) {
+ linenum += 1;
+ if (buf[0] == '#') {
+ // Everything after the '#' is a filename.
+ // Ignore the common prefix.
+
+ // First, save all the symbols from the previous file (if any).
+ CopyDexSymbolsToArray(dexfile, head, num_symbols);
+
+ dexfile = new DexFileList;
+ // Subtract one because buf[] contains a trailing newline
+ int pathlen = strlen(buf) - prefix_len - 1;
+ char *path = new char[pathlen + 1];
+ strncpy(path, buf + prefix_len, pathlen);
+ path[pathlen] = 0;
+ dexfile->path = path;
+ dexfile->nsymbols = 0;
+ dexfile->symbols = NULL;
+ dex_hash_->Update(path, dexfile);
+ num_symbols = 0;
+ head = NULL;
+ prev = NULL;
+ continue;
+ }
+
+ uint32_t addr;
+ int len, line;
+ char clazz[kBufSize], method[kBufSize], sig[kBufSize], file[kBufSize];
+ if (sscanf(buf, "0x%x %d %s %s %s %s %d",
+ &addr, &len, clazz, method, sig, file, &line) != 7) {
+ fprintf(stderr, "Cannot parse line %d of file %s:\n%s",
+ linenum, fname, buf);
+ exit(1);
+ }
+
+ // Concatenate the class name, method name, and signature
+ // plus one for the period separating the class and method.
+ int nchars = strlen(clazz) + strlen(method) + strlen(sig) + 1;
+ char *name = new char[nchars + 1];
+ strcpy(name, clazz);
+ strcat(name, ".");
+ strcat(name, method);
+ strcat(name, sig);
+
+ DexSymList *symbol = new DexSymList;
+ symbol->sym.addr = addr;
+ symbol->sym.len = len;
+ symbol->sym.name = name;
+ symbol->next = NULL;
+
+ // Keep the list in the same order as the file
+ if (head == NULL)
+ head = symbol;
+ if (prev != NULL)
+ prev->next = symbol;
+ prev = symbol;
+ num_symbols += 1;
+ }
+ fclose(fstream);
+
+ // Copy the symbols from the last file.
+ CopyDexSymbolsToArray(dexfile, head, num_symbols);
+ delete[] fname;
+}
+
+// Extracts the pathname to a jar file (or .apk file) from the mmap pathname.
+// An example mmap pathname looks something like this:
+// /data/dalvik-cache/system@app@TestHarness.apk@classes.dex
+// We want to convert that to this:
+// /system/app/TestHarness.apk
+// If the pathname is not of the expected form, then NULL is returned.
+// The space for the extracted path is allocated in this routine and should
+// be freed by the caller after it is no longer needed.
+static char *ExtractDexPathFromMmap(const char *mmap_path)
+{
+ char *end = rindex(mmap_path, '@');
+ if (end == NULL)
+ return NULL;
+ char *start = rindex(mmap_path, '/');
+ if (start == NULL)
+ return NULL;
+ int len = end - start;
+ char *path = new char[len + 1];
+ strncpy(path, start, len);
+ path[len] = 0;
+
+ // Replace all the occurrences of '@' with '/'
+ for (int ii = 0; ii < len; ii++) {
+ if (path[ii] == '@')
+ path[ii] = '/';
+ }
+ return path;
+}
+
+void TraceReaderBase::Close()
+{
+ bb_reader_->Close();
+ insn_reader_->Close();
+ load_addr_reader_->Close();
+ store_addr_reader_->Close();
+ exc_reader_->Close();
+ pid_reader_->Close();
+ method_reader_->Close();
+ internal_exc_reader_->Close();
+ internal_pid_reader_->Close();
+ internal_method_reader_->Close();
+ fclose(static_fstream_);
+ static_fstream_ = NULL;
+}
+
+void TraceReaderBase::WriteHeader(TraceHeader *header)
+{
+ TraceHeader swappedHeader;
+
+ freopen(static_filename_, "r+", static_fstream_);
+ fseek(static_fstream_, 0, SEEK_SET);
+
+ memcpy(&swappedHeader, header, sizeof(TraceHeader));
+
+ convert32(swappedHeader.version);
+ convert32(swappedHeader.start_sec);
+ convert32(swappedHeader.start_usec);
+ convert32(swappedHeader.pdate);
+ convert32(swappedHeader.ptime);
+ convert64(swappedHeader.num_static_bb);
+ convert64(swappedHeader.num_static_insn);
+ convert64(swappedHeader.num_dynamic_bb);
+ convert64(swappedHeader.num_dynamic_insn);
+ convert64(swappedHeader.elapsed_usecs);
+
+ fwrite(&swappedHeader, sizeof(TraceHeader), 1, static_fstream_);
+}
+
+// Reads the next StaticRec from the trace file (not including the list
+// of instructions). On end-of-file, this function returns true.
+int TraceReaderBase::ReadStatic(StaticRec *rec)
+{
+ int rval = fread(rec, sizeof(StaticRec), 1, static_fstream_);
+ if (rval != 1) {
+ if (feof(static_fstream_)) {
+ return true;
+ }
+ perror(static_filename_);
+ exit(1);
+ }
+ convert64(rec->bb_num);
+ convert32(rec->bb_addr);
+ convert32(rec->num_insns);
+ return false;
+}
+
+// Reads "num" instructions into the array "insns" which must be large
+// enough to hold the "num" instructions.
+// Returns the actual number of instructions read. This will usually
+// be "num" but may be less if end-of-file occurred.
+int TraceReaderBase::ReadStaticInsns(int num, uint32_t *insns)
+{
+ if (num == 0)
+ return 0;
+ int rval = fread(insns, sizeof(uint32_t), num, static_fstream_);
+
+ // Convert from little-endian, if necessary
+ for (int ii = 0; ii < num; ++ii)
+ convert32(insns[ii]);
+
+ if (rval != num) {
+ if (feof(static_fstream_)) {
+ return rval;
+ }
+ perror(static_filename_);
+ exit(1);
+ }
+ return rval;
+}
+
+void TraceReaderBase::TruncateLastBlock(uint32_t num_insns)
+{
+ uint32_t insns[kMaxInsnPerBB];
+ StaticRec static_rec;
+ long loc = 0, prev_loc = 0;
+
+ freopen(static_filename_, "r+", static_fstream_);
+ fseek(static_fstream_, sizeof(TraceHeader), SEEK_SET);
+
+ // Find the last record
+ while (1) {
+ prev_loc = loc;
+ loc = ftell(static_fstream_);
+
+ // We don't need to byte-swap static_rec here because we are just
+ // reading the records until we get to the last one.
+ int rval = fread(&static_rec, sizeof(StaticRec), 1, static_fstream_);
+ if (rval != 1)
+ break;
+ ReadStaticInsns(static_rec.num_insns, insns);
+ }
+ if (prev_loc != 0) {
+ fseek(static_fstream_, prev_loc, SEEK_SET);
+ static_rec.num_insns = num_insns;
+
+ // Now we need to byte-swap, but just the field that we changed.
+ convert32(static_rec.num_insns);
+ fwrite(&static_rec, sizeof(StaticRec), 1, static_fstream_);
+ int fd = fileno(static_fstream_);
+ long len = ftell(static_fstream_);
+ len += num_insns * sizeof(uint32_t);
+ ftruncate(fd, len);
+ }
+}
+
+int TraceReaderBase::FindNumInsns(uint64_t bb_num, uint64_t bb_start_time)
+{
+ int num_insns;
+
+ // Read the exception trace file. "bb_recnum_" is the number of
+ // basic block records that have been read so far, and "exc_recnum_"
+ // is the record number from the exception trace.
+ while (!exc_end_ && exc_recnum_ < bb_recnum_) {
+ uint32_t current_pc, target_pc;
+ uint64_t time;
+
+ exc_end_ = internal_exc_reader_->ReadExc(&time, &current_pc, &exc_recnum_,
+ &target_pc, &exc_bb_num_,
+ &exc_time_, &exc_num_insns_);
+ }
+
+ // If an exception occurred in this basic block, then use the
+ // number of instructions specified in the exception record.
+ if (!exc_end_ && exc_recnum_ == bb_recnum_) {
+ num_insns = exc_num_insns_;
+ } else {
+ // Otherwise, use the number of instructions specified in the
+ // static basic block.
+ num_insns = blocks_[bb_num].rec.num_insns;
+ }
+ return num_insns;
+}
+
+// Finds the current pid for the given time. This routine reads the pid
+// trace file and assumes that the "time" parameter is monotonically
+// increasing.
+int TraceReaderBase::FindCurrentPid(uint64_t time)
+{
+ PidEvent event;
+
+ if (time < next_pid_switch_time_)
+ return current_pid_;
+
+ current_pid_ = next_pid_;
+ while (1) {
+ if (internal_pid_reader_->ReadPidEvent(&event)) {
+ next_pid_switch_time_ = ~0ull;
+ break;
+ }
+ if (event.rec_type != kPidSwitch)
+ continue;
+ if (event.time > time) {
+ next_pid_ = event.pid;
+ next_pid_switch_time_ = event.time;
+ break;
+ }
+ current_pid_ = event.pid;
+ }
+ return current_pid_;
+}