// Copyright 2006 The Android Open Source Project #include #include #include #include #include #include #include #include #include #include #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::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(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(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_; }