// Copyright 2006 The Android Open Source Project

#ifndef TRACE_READER_BASE_H
#define TRACE_READER_BASE_H

#include <inttypes.h>
#include "trace_common.h"
#include "hash_table.h"

class BBReader;
class InsnReader;
class AddrReader;
class ExcReader;
class PidReader;
class MethodReader;

struct StaticRec {
    uint64_t    bb_num;
    uint32_t    bb_addr;
    uint32_t    num_insns;
};

struct StaticBlock {
    StaticRec   rec;
    uint32_t    *insns;
};

struct BBEvent {
    uint64_t    time;
    uint64_t    bb_num;
    uint32_t    bb_addr;
    uint32_t    *insns;
    int         num_insns;
    int         pid;
    int         is_thumb;
};

struct PidEvent {
    uint64_t    time;
    int         rec_type;       // record type: fork, context switch, exit ...
    int         tgid;           // thread group id
    int         pid;            // for fork: child pid; for switch: next pid;
                                //   for exit: exit value
    uint32_t    vstart;         // virtual start address (only used with mmap)
    uint32_t    vend;           // virtual end address (only used with mmap)
    uint32_t    offset;         // virtual file offset (only used with mmap)

    // Dynamically allocated path to executable (or lib). In the case of
    // an mmapped dex file, the path is modified to be more useful for
    // comparing against the output of dexlist.  For example, instead of this:
    //   /data/dalvik-cache/system@app@TestHarness.apk@classes.dex
    // We convert to this:
    //   /system/app/TestHarness.apk
    char        *path;
    char        *mmap_path;     // unmodified mmap path
    int         argc;           // number of args
    char        **argv;         // dynamically allocated array of args
};

struct MethodRec {
    uint64_t    time;
    uint32_t    addr;
    int         pid;
    int         flags;
};

struct DexSym {
    uint32_t    addr;
    int         len;
    char        *name;
};

struct DexFileList {
    char        *path;
    int         nsymbols;
    DexSym      *symbols;
};

class TraceReaderBase {
  public:
    TraceReaderBase();
    virtual ~TraceReaderBase();

    friend class BBReader;

    void                Open(const char *filename);
    void                Close();
    void                WriteHeader(TraceHeader *header);
    inline bool         ReadBB(BBEvent *event);
    int                 ReadStatic(StaticRec *rec);
    int                 ReadStaticInsns(int num, uint32_t *insns);
    TraceHeader         *GetHeader()                { return header_; }
    inline uint64_t     ReadInsnTime(uint64_t min_time);
    void                TruncateLastBlock(uint32_t num_insns);
    inline bool         ReadAddr(uint64_t *time, uint32_t *addr, int *flags);
    inline bool         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);
    inline bool         ReadPidEvent(PidEvent *event);
    inline bool         ReadMethod(MethodRec *method_record);
    StaticBlock         *GetStaticBlock(uint64_t bb_num) { return &blocks_[bb_num]; }
    uint32_t            *GetInsns(uint64_t bb_num) { return blocks_[bb_num].insns; }
    uint32_t            GetBBAddr(uint64_t bb_num) {
        return blocks_[bb_num].rec.bb_addr & ~1;
    }
    int                 GetIsThumb(uint64_t bb_num) {
        return blocks_[bb_num].rec.bb_addr & 1;
    }
    void                SetPostProcessing(bool val) { post_processing_ = val; }

  protected:
    virtual int         FindCurrentPid(uint64_t time);
    int                 current_pid_;
    int                 next_pid_;
    uint64_t            next_pid_switch_time_;
    PidReader           *internal_pid_reader_;
    MethodReader        *internal_method_reader_;
    HashTable<DexFileList*> *dex_hash_;

  private:
    int          FindNumInsns(uint64_t bb_num, uint64_t bb_start_time);
    void         ReadTraceHeader(FILE *fstream, const char *filename,
                                const char *tracename, TraceHeader *header);
    PidEvent     *FindMmapDexFileEvent();
    void         ParseDexList(const char *filename);

    char         *static_filename_;
    FILE         *static_fstream_;
    TraceHeader  *header_;
    BBReader     *bb_reader_;
    InsnReader   *insn_reader_;
    AddrReader   *load_addr_reader_;
    AddrReader   *store_addr_reader_;
    ExcReader    *exc_reader_;
    PidReader    *pid_reader_;
    MethodReader *method_reader_;
    ExcReader    *internal_exc_reader_;
    StaticBlock  *blocks_;
    bool         exc_end_;
    uint64_t     bb_recnum_;
    uint64_t     exc_recnum_;
    uint64_t     exc_bb_num_;
    uint64_t     exc_time_;
    int          exc_num_insns_;
    bool         post_processing_;

    bool         load_eof_;
    uint64_t     load_time_;
    uint32_t     load_addr_;
    bool         store_eof_;
    uint64_t     store_time_;
    uint32_t     store_addr_;
};

class Decoder;

class BBReader {
  public:
    explicit BBReader(TraceReaderBase *trace);
    ~BBReader();
    void     Open(const char *filename);
    void     Close();
    bool     ReadBB(BBEvent *event);

  private:
    struct TimeRec {
        BBRec       bb_rec;
        uint64_t    next_time;
    };

    struct Future {
        Future      *next;
        TimeRec     bb;
    };

    inline Future   *AllocFuture();
    inline void     FreeFuture(Future *future);
    inline void     InsertFuture(Future *future);
    inline int      DecodeNextRec();

    TimeRec         nextrec_;
    Future          futures_[kMaxNumBasicBlocks];
    Future          *head_;
    Future          *free_;
    Decoder         *decoder_;
    bool            is_eof_;
    TraceReaderBase *trace_;
};

class InsnReader {
  public:
    InsnReader();
    ~InsnReader();

    void        Open(const char *filename);
    void        Close();
    uint64_t    ReadInsnTime(uint64_t min_time);

  private:
    Decoder     *decoder_;
    uint64_t    prev_time_;
    uint64_t    time_diff_;
    int         repeat_;
};

class AddrReader {
  public:
    AddrReader();
    ~AddrReader();

    bool        Open(const char *filename, const char *suffix);
    void        Close();
    bool        ReadAddr(uint64_t *time, uint32_t *addr);

  private:
    Decoder     *decoder_;
    uint32_t    prev_addr_;
    uint64_t    prev_time_;
    bool        opened_;        // true after file is opened
};

class ExcReader {
  public:
    ExcReader();
    ~ExcReader();

    void        Open(const char *filename);
    void        Close();
    bool        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);

  private:
    Decoder     *decoder_;
    uint64_t    prev_time_;
    uint64_t    prev_recnum_;
};

class PidReader {
  public:
    PidReader();
    ~PidReader();

    void        Open(const char *filename);
    void        Close();
    bool        ReadPidEvent(struct PidEvent *event);
    void        Dispose(struct PidEvent *event);

  private:
    Decoder     *decoder_;
    uint64_t    prev_time_;
};

class MethodReader {
  public:
    MethodReader();
    ~MethodReader();

    bool        Open(const char *filename);
    void        Close();
    bool        ReadMethod(MethodRec *method_record);

  private:
    Decoder     *decoder_;
    uint64_t    prev_time_;
    uint32_t    prev_addr_;
    int32_t     prev_pid_;
    bool        opened_;        // true after file is opened
};

// Reads the next dynamic basic block from the trace.
// Returns true on end-of-file.
inline bool TraceReaderBase::ReadBB(BBEvent *event)
{
    bb_recnum_ += 1;
    return bb_reader_->ReadBB(event);
}

inline uint64_t TraceReaderBase::ReadInsnTime(uint64_t min_time)
{
    return insn_reader_->ReadInsnTime(min_time);
}

inline bool TraceReaderBase::ReadAddr(uint64_t *time, uint32_t *addr, int *flags)
{
    if (load_eof_ && store_eof_)
        return true;

    if (store_eof_ || (!load_eof_ && load_time_ <= store_time_)) {
        *time = load_time_;
        *addr = load_addr_;
        *flags = 0;
        load_eof_ = load_addr_reader_->ReadAddr(&load_time_, &load_addr_);
    } else {
        *time = store_time_;
        *addr = store_addr_;
        *flags = 1;
        store_eof_ = store_addr_reader_->ReadAddr(&store_time_, &store_addr_);
    }
    return false;
}

inline bool TraceReaderBase::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)
{
    return exc_reader_->ReadExc(time, current_pc, recnum, target_pc, bb_num,
                                bb_start_time, num_insns);
}

inline bool TraceReaderBase::ReadPidEvent(PidEvent *event)
{
    return pid_reader_->ReadPidEvent(event);
}

inline bool TraceReaderBase::ReadMethod(MethodRec *method_record)
{
    return method_reader_->ReadMethod(method_record);
}

// Duplicates a string, allocating space using new[].
inline char * Strdup(const char *src) {
    int len = strlen(src);
    char *copy = new char[len + 1];
    strcpy(copy, src);
    return copy;
}

#endif /* TRACE_READER_BASE_H */