1202 lines
34 KiB
C++
1202 lines
34 KiB
C++
// 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, ¤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_;
|
|
}
|