| // 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(const 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(const 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(const char *filename, const 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(const 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(const 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(const 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, const char *filename, | 
 |                                       const 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(const 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(const 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) | 
 | { | 
 |     const char *end = rindex(mmap_path, '@'); | 
 |     if (end == NULL) | 
 |         return NULL; | 
 |     const 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, ¤t_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_; | 
 | } |