// Copyright 2006 The Android Open Source Project

#ifndef PARSE_OPTIONS_INL_H
#define PARSE_OPTIONS_INL_H

// Define a typedef for TraceReaderType and include "parse_options.h"
// before including this header file in a C++ source file.
//
// For example:
// 
// struct symbol {
//   int  elapsed;
// };
// 
// typedef TraceReader<symbol> TraceReaderType;


typedef TraceReaderType::symbol_type symbol_type;
typedef TraceReaderType::region_type region_type;
typedef TraceReaderType::ProcessState ProcessState;

symbol_type *kernel_sym;
symbol_type *library_sym;

// Returns true if the given event is included (or not excluded)
// from the list of valid events specified by the user on the
// command line.
inline bool IsValidEvent(BBEvent *event, symbol_type *sym)
{
  if (include_some_pids && pid_include_vector.GetBit(event->pid) == 0)
      return false;
  if (exclude_some_pids && pid_exclude_vector.GetBit(event->pid))
      return false;
  if (include_some_procedures) {
    if (sym == NULL || included_procedures.Find(sym->name) == 0)
      return false;
  }
  if (exclude_some_procedures) {
    if (sym == NULL || excluded_procedures.Find(sym->name))
      return false;
  }
  return true;
}

inline bool IsValidPid(int pid) {
  if (include_some_pids && pid_include_vector.GetBit(pid) == 0)
    return false;
  if (exclude_some_pids && pid_exclude_vector.GetBit(pid))
    return false;
  return true;
}

inline symbol_type *GetSymbol(TraceReaderType *trace, int pid, uint32_t addr,
                              uint64_t time)
{
  symbol_type *sym = trace->LookupFunction(pid, addr, time);

  if (lump_kernel && (sym->region->flags & region_type::kIsKernelRegion)) {
    if (kernel_sym == NULL) {
      kernel_sym = sym;
      sym->name = ":kernel";
    } else {
      sym = kernel_sym;
    }
  }

  if (lump_libraries && (sym->region->flags & region_type::kIsLibraryRegion)) {
    if (library_sym == NULL) {
      library_sym = sym;
      sym->name = ":libs";
    } else {
      sym = library_sym;
    }
  }

  return sym;
}

inline bool IsIncludedProcedure(symbol_type *sym)
{
  if (include_kernel_syms && (sym->region->flags & region_type::kIsKernelRegion))
    return true;
  if (include_library_syms && (sym->region->flags & region_type::kIsLibraryRegion))
    return true;
  return included_procedures.Find(sym->name);
}

inline bool IsExcludedProcedure(symbol_type *sym)
{
  if (exclude_kernel_syms && (sym->region->flags & region_type::kIsKernelRegion))
    return true;
  if (exclude_library_syms && (sym->region->flags & region_type::kIsLibraryRegion))
    return true;
  return excluded_procedures.Find(sym->name);
}

// Returns true on end-of-file.
inline bool GetNextValidEvent(TraceReaderType *trace,
                              BBEvent *event,
                              BBEvent *first_ignored_event,
                              symbol_type **sym_ptr)
{
  symbol_type *sym = NULL;
  first_ignored_event->time = 0;
  if (trace->ReadBB(event))
    return true;
  bool recheck = true;
  while (recheck) {
    recheck = false;
    if (include_some_pids) {
      while (pid_include_vector.GetBit(event->pid) == 0) {
        if (first_ignored_event->time == 0)
          *first_ignored_event = *event;
        if (trace->ReadBB(event))
          return true;
      }
    } else if (exclude_some_pids) {
      while (pid_exclude_vector.GetBit(event->pid)) {
        if (first_ignored_event->time == 0)
          *first_ignored_event = *event;
        if (trace->ReadBB(event))
          return true;
      }
    }

    if (include_some_procedures) {
      sym = GetSymbol(trace, event->pid, event->bb_addr, event->time);
      while (!IsIncludedProcedure(sym)) {
        if (first_ignored_event->time == 0)
          *first_ignored_event = *event;
        if (trace->ReadBB(event))
          return true;
        recheck = true;
        sym = GetSymbol(trace, event->pid, event->bb_addr, event->time);
      }
    } else if (exclude_some_procedures) {
      sym = GetSymbol(trace, event->pid, event->bb_addr, event->time);
      while (IsExcludedProcedure(sym)) {
        if (first_ignored_event->time == 0)
          *first_ignored_event = *event;
        if (trace->ReadBB(event))
          return true;
        recheck = true;
        sym = GetSymbol(trace, event->pid, event->bb_addr, event->time);
      }
    }
  }
  if (sym == NULL)
    sym = GetSymbol(trace, event->pid, event->bb_addr, event->time);

  *sym_ptr = sym;
  return false;
}

#endif  // PARSE_OPTIONS_INL_H