// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

/**
 * @fileoverview Parses Mali DDK/kernel events in the Linux event trace format.
 */
base.require('importer.linux_perf.parser');
base.exportTo('tracing.importer.linux_perf', function() {

  var Parser = tracing.importer.linux_perf.Parser;

  /**
   * Parses Mali DDK/kernel trace events.
   * @constructor
   */
  function MaliParser(importer) {
    Parser.call(this, importer);

    // kernel DVFS events
    importer.registerEventHandler('mali_dvfs_event',
        MaliParser.prototype.dvfsEventEvent.bind(this));
    importer.registerEventHandler('mali_dvfs_set_clock',
        MaliParser.prototype.dvfsSetClockEvent.bind(this));
    importer.registerEventHandler('mali_dvfs_set_voltage',
        MaliParser.prototype.dvfsSetVoltageEvent.bind(this));

    // kernel Mali hw counter events
    this.addJMCounter('mali_hwc_MESSAGES_SENT', 'Messages Sent');
    this.addJMCounter('mali_hwc_MESSAGES_RECEIVED', 'Messages Received');
    this.addJMCycles('mali_hwc_GPU_ACTIVE', 'GPU Active');
    this.addJMCycles('mali_hwc_IRQ_ACTIVE', 'IRQ Active');

    for (var i = 0; i < 7; i++) {
      var jobStr = 'JS' + i;
      var jobHWCStr = 'mali_hwc_' + jobStr;
      this.addJMCounter(jobHWCStr + '_JOBS', jobStr + ' Jobs');
      this.addJMCounter(jobHWCStr + '_TASKS', jobStr + ' Tasks');
      this.addJMCycles(jobHWCStr + '_ACTIVE', jobStr + ' Active');
      this.addJMCycles(jobHWCStr + '_WAIT_READ', jobStr + ' Wait Read');
      this.addJMCycles(jobHWCStr + '_WAIT_ISSUE', jobStr + ' Wait Issue');
      this.addJMCycles(jobHWCStr + '_WAIT_DEPEND', jobStr + ' Wait Depend');
      this.addJMCycles(jobHWCStr + '_WAIT_FINISH', jobStr + ' Wait Finish');
    }

    this.addTilerCounter('mali_hwc_TRIANGLES', 'Triangles');
    this.addTilerCounter('mali_hwc_QUADS', 'Quads');
    this.addTilerCounter('mali_hwc_POLYGONS', 'Polygons');
    this.addTilerCounter('mali_hwc_POINTS', 'Points');
    this.addTilerCounter('mali_hwc_LINES', 'Lines');
    this.addTilerCounter('mali_hwc_VCACHE_HIT', 'VCache Hit');
    this.addTilerCounter('mali_hwc_VCACHE_MISS', 'VCache Miss');
    this.addTilerCounter('mali_hwc_FRONT_FACING', 'Front Facing');
    this.addTilerCounter('mali_hwc_BACK_FACING', 'Back Facing');
    this.addTilerCounter('mali_hwc_PRIM_VISIBLE', 'Prim Visible');
    this.addTilerCounter('mali_hwc_PRIM_CULLED', 'Prim Culled');
    this.addTilerCounter('mali_hwc_PRIM_CLIPPED', 'Prim Clipped');

    this.addTilerCounter('mali_hwc_WRBUF_HIT', 'Wrbuf Hit');
    this.addTilerCounter('mali_hwc_WRBUF_MISS', 'Wrbuf Miss');
    this.addTilerCounter('mali_hwc_WRBUF_LINE', 'Wrbuf Line');
    this.addTilerCounter('mali_hwc_WRBUF_PARTIAL', 'Wrbuf Partial');
    this.addTilerCounter('mali_hwc_WRBUF_STALL', 'Wrbuf Stall');

    this.addTilerCycles('mali_hwc_ACTIVE', 'Tiler Active');
    this.addTilerCycles('mali_hwc_INDEX_WAIT', 'Index Wait');
    this.addTilerCycles('mali_hwc_INDEX_RANGE_WAIT', 'Index Range Wait');
    this.addTilerCycles('mali_hwc_VERTEX_WAIT', 'Vertex Wait');
    this.addTilerCycles('mali_hwc_PCACHE_WAIT', 'Pcache Wait');
    this.addTilerCycles('mali_hwc_WRBUF_WAIT', 'Wrbuf Wait');
    this.addTilerCycles('mali_hwc_BUS_READ', 'Bus Read');
    this.addTilerCycles('mali_hwc_BUS_WRITE', 'Bus Write');

    this.addTilerCycles('mali_hwc_TILER_UTLB_STALL', 'Tiler UTLB Stall');
    this.addTilerCycles('mali_hwc_TILER_UTLB_HIT', 'Tiler UTLB Hit');

    this.addFragCycles('mali_hwc_FRAG_ACTIVE', 'Active');
    /* NB: don't propagate spelling mistakes to labels */
    this.addFragCounter('mali_hwc_FRAG_PRIMATIVES', 'Primitives');
    this.addFragCounter('mali_hwc_FRAG_PRIMATIVES_DROPPED',
        'Primitives Dropped');
    this.addFragCycles('mali_hwc_FRAG_CYCLE_DESC', 'Descriptor Processing');
    this.addFragCycles('mali_hwc_FRAG_CYCLES_PLR', 'PLR Processing??');
    this.addFragCycles('mali_hwc_FRAG_CYCLES_VERT', 'Vertex Processing');
    this.addFragCycles('mali_hwc_FRAG_CYCLES_TRISETUP', 'Triangle Setup');
    this.addFragCycles('mali_hwc_FRAG_CYCLES_RAST', 'Rasterization???');
    this.addFragCounter('mali_hwc_FRAG_THREADS', 'Threads');
    this.addFragCounter('mali_hwc_FRAG_DUMMY_THREADS', 'Dummy Threads');
    this.addFragCounter('mali_hwc_FRAG_QUADS_RAST', 'Quads Rast');
    this.addFragCounter('mali_hwc_FRAG_QUADS_EZS_TEST', 'Quads EZS Test');
    this.addFragCounter('mali_hwc_FRAG_QUADS_EZS_KILLED', 'Quads EZS Killed');
    this.addFragCounter('mali_hwc_FRAG_QUADS_LZS_TEST', 'Quads LZS Test');
    this.addFragCounter('mali_hwc_FRAG_QUADS_LZS_KILLED', 'Quads LZS Killed');
    this.addFragCycles('mali_hwc_FRAG_CYCLE_NO_TILE', 'No Tiles');
    this.addFragCounter('mali_hwc_FRAG_NUM_TILES', 'Tiles');
    this.addFragCounter('mali_hwc_FRAG_TRANS_ELIM', 'Transactions Eliminated');

    this.addComputeCycles('mali_hwc_COMPUTE_ACTIVE', 'Active');
    this.addComputeCounter('mali_hwc_COMPUTE_TASKS', 'Tasks');
    this.addComputeCounter('mali_hwc_COMPUTE_THREADS', 'Threads Started');
    this.addComputeCycles('mali_hwc_COMPUTE_CYCLES_DESC',
        'Waiting for Descriptors');

    this.addTripipeCycles('mali_hwc_TRIPIPE_ACTIVE', 'Active');

    this.addArithCounter('mali_hwc_ARITH_WORDS', 'Instructions (/Pipes)');
    this.addArithCycles('mali_hwc_ARITH_CYCLES_REG',
        'Reg scheduling stalls (/Pipes)');
    this.addArithCycles('mali_hwc_ARITH_CYCLES_L0',
        'L0 cache miss stalls (/Pipes)');
    this.addArithCounter('mali_hwc_ARITH_FRAG_DEPEND',
        'Frag dep check failures (/Pipes)');

    this.addLSCounter('mali_hwc_LS_WORDS', 'Instruction Words Completed');
    this.addLSCounter('mali_hwc_LS_ISSUES', 'Full Pipeline Issues');
    this.addLSCounter('mali_hwc_LS_RESTARTS', 'Restarts (unpairable insts)');
    this.addLSCounter('mali_hwc_LS_REISSUES_MISS',
        'Pipeline reissue (cache miss/uTLB)');
    this.addLSCounter('mali_hwc_LS_REISSUES_VD',
        'Pipeline reissue (varying data)');
    /* TODO(sleffler) fix kernel event typo */
    this.addLSCounter('mali_hwc_LS_REISSUE_ATTRIB_MISS',
        'Pipeline reissue (attribute cache miss)');
    this.addLSCounter('mali_hwc_LS_REISSUE_NO_WB', 'Writeback not used');

    this.addTexCounter('mali_hwc_TEX_WORDS', 'Words');
    this.addTexCounter('mali_hwc_TEX_BUBBLES', 'Bubbles');
    this.addTexCounter('mali_hwc_TEX_WORDS_L0', 'Words L0');
    this.addTexCounter('mali_hwc_TEX_WORDS_DESC', 'Words Desc');
    this.addTexCounter('mali_hwc_TEX_THREADS', 'Threads');
    this.addTexCounter('mali_hwc_TEX_RECIRC_FMISS', 'Recirc due to Full Miss');
    this.addTexCounter('mali_hwc_TEX_RECIRC_DESC', 'Recirc due to Desc Miss');
    this.addTexCounter('mali_hwc_TEX_RECIRC_MULTI', 'Recirc due to Multipass');
    this.addTexCounter('mali_hwc_TEX_RECIRC_PMISS',
        'Recirc due to Partial Cache Miss');
    this.addTexCounter('mali_hwc_TEX_RECIRC_CONF',
        'Recirc due to Cache Conflict');

    this.addLSCCounter('mali_hwc_LSC_READ_HITS', 'Read Hits');
    this.addLSCCounter('mali_hwc_LSC_READ_MISSES', 'Read Misses');
    this.addLSCCounter('mali_hwc_LSC_WRITE_HITS', 'Write Hits');
    this.addLSCCounter('mali_hwc_LSC_WRITE_MISSES', 'Write Misses');
    this.addLSCCounter('mali_hwc_LSC_ATOMIC_HITS', 'Atomic Hits');
    this.addLSCCounter('mali_hwc_LSC_ATOMIC_MISSES', 'Atomic Misses');
    this.addLSCCounter('mali_hwc_LSC_LINE_FETCHES', 'Line Fetches');
    this.addLSCCounter('mali_hwc_LSC_DIRTY_LINE', 'Dirty Lines');
    this.addLSCCounter('mali_hwc_LSC_SNOOPS', 'Snoops');

    this.addAXICounter('mali_hwc_AXI_TLB_STALL', 'Address channel stall');
    this.addAXICounter('mali_hwc_AXI_TLB_MISS', 'Cache Miss');
    this.addAXICounter('mali_hwc_AXI_TLB_TRANSACTION', 'Transactions');
    this.addAXICounter('mali_hwc_LS_TLB_MISS', 'LS Cache Miss');
    this.addAXICounter('mali_hwc_LS_TLB_HIT', 'LS Cache Hit');
    this.addAXICounter('mali_hwc_AXI_BEATS_READ', 'Read Beats');
    this.addAXICounter('mali_hwc_AXI_BEATS_WRITE', 'Write Beats');

    this.addMMUCounter('mali_hwc_MMU_TABLE_WALK', 'Page Table Walks');
    this.addMMUCounter('mali_hwc_MMU_REPLAY_MISS',
        'Cache Miss from Replay Buffer');
    this.addMMUCounter('mali_hwc_MMU_REPLAY_FULL', 'Replay Buffer Full');
    this.addMMUCounter('mali_hwc_MMU_NEW_MISS', 'Cache Miss on New Request');
    this.addMMUCounter('mali_hwc_MMU_HIT', 'Cache Hit');

    this.addMMUCycles('mali_hwc_UTLB_STALL', 'UTLB Stalled');
    this.addMMUCycles('mali_hwc_UTLB_REPLAY_MISS', 'UTLB Replay Miss');
    this.addMMUCycles('mali_hwc_UTLB_REPLAY_FULL', 'UTLB Replay Full');
    this.addMMUCycles('mali_hwc_UTLB_NEW_MISS', 'UTLB New Miss');
    this.addMMUCycles('mali_hwc_UTLB_HIT', 'UTLB Hit');

    this.addL2Counter('mali_hwc_L2_READ_BEATS', 'Read Beats');
    this.addL2Counter('mali_hwc_L2_WRITE_BEATS', 'Write Beats');
    this.addL2Counter('mali_hwc_L2_ANY_LOOKUP', 'Any Lookup');
    this.addL2Counter('mali_hwc_L2_READ_LOOKUP', 'Read Lookup');
    this.addL2Counter('mali_hwc_L2_SREAD_LOOKUP', 'Shareable Read Lookup');
    this.addL2Counter('mali_hwc_L2_READ_REPLAY', 'Read Replayed');
    this.addL2Counter('mali_hwc_L2_READ_SNOOP', 'Read Snoop');
    this.addL2Counter('mali_hwc_L2_READ_HIT', 'Read Cache Hit');
    this.addL2Counter('mali_hwc_L2_CLEAN_MISS', 'CleanUnique Miss');
    this.addL2Counter('mali_hwc_L2_WRITE_LOOKUP', 'Write Lookup');
    this.addL2Counter('mali_hwc_L2_SWRITE_LOOKUP', 'Shareable Write Lookup');
    this.addL2Counter('mali_hwc_L2_WRITE_REPLAY', 'Write Replayed');
    this.addL2Counter('mali_hwc_L2_WRITE_SNOOP', 'Write Snoop');
    this.addL2Counter('mali_hwc_L2_WRITE_HIT', 'Write Cache Hit');
    this.addL2Counter('mali_hwc_L2_EXT_READ_FULL', 'ExtRD with BIU Full');
    this.addL2Counter('mali_hwc_L2_EXT_READ_HALF', 'ExtRD with BIU >1/2 Full');
    this.addL2Counter('mali_hwc_L2_EXT_WRITE_FULL', 'ExtWR with BIU Full');
    this.addL2Counter('mali_hwc_L2_EXT_WRITE_HALF', 'ExtWR with BIU >1/2 Full');

    this.addL2Counter('mali_hwc_L2_EXT_READ', 'External Read (ExtRD)');
    this.addL2Counter('mali_hwc_L2_EXT_READ_LINE', 'ExtRD (linefill)');
    this.addL2Counter('mali_hwc_L2_EXT_WRITE', 'External Write (ExtWR)');
    this.addL2Counter('mali_hwc_L2_EXT_WRITE_LINE', 'ExtWR (linefill)');
    this.addL2Counter('mali_hwc_L2_EXT_WRITE_SMALL', 'ExtWR (burst size <64B)');
    this.addL2Counter('mali_hwc_L2_EXT_BARRIER', 'External Barrier');
    this.addL2Counter('mali_hwc_L2_EXT_AR_STALL', 'Address Read stalls');
    this.addL2Counter('mali_hwc_L2_EXT_R_BUF_FULL',
        'Response Buffer full stalls');
    this.addL2Counter('mali_hwc_L2_EXT_RD_BUF_FULL',
        'Read Data Buffer full stalls');
    this.addL2Counter('mali_hwc_L2_EXT_R_RAW', 'RAW hazard stalls');
    this.addL2Counter('mali_hwc_L2_EXT_W_STALL', 'Write Data stalls');
    this.addL2Counter('mali_hwc_L2_EXT_W_BUF_FULL', 'Write Data Buffer full');
    this.addL2Counter('mali_hwc_L2_EXT_R_W_HAZARD', 'WAW or WAR hazard stalls');
    this.addL2Counter('mali_hwc_L2_TAG_HAZARD', 'Tag hazard replays');
    this.addL2Cycles('mali_hwc_L2_SNOOP_FULL', 'Snoop buffer full');
    this.addL2Cycles('mali_hwc_L2_REPLAY_FULL', 'Replay buffer full');

    // DDK events (from X server)
    importer.registerEventHandler('tracing_mark_write:mali_driver',
        MaliParser.prototype.maliDDKEvent.bind(this));

    this.model_ = importer.model_;
  }

  MaliParser.prototype = {
    __proto__: Parser.prototype,

    maliDDKOpenSlice: function(pid, tid, ts, func, blockinfo) {
      var thread = this.importer.model_.getOrCreateProcess(pid)
        .getOrCreateThread(tid);
      var funcArgs = /^([\w\d_]*)(?:\(\))?:?\s*(.*)$/.exec(func);
      thread.beginSlice('gpu-driver', funcArgs[1], ts,
          { 'args': funcArgs[2],
            'blockinfo': blockinfo });
    },

    maliDDKCloseSlice: function(pid, tid, ts, args, blockinfo) {
      var thread = this.importer.model_.getOrCreateProcess(pid)
        .getOrCreateThread(tid);
      if (!thread.openSliceCount) {
        // Discard unmatched ends.
        return;
      }
      thread.endSlice(ts);
    },

    /**
     * Deduce the format of Mali perf events.
     *
     * @return {RegExp} the regular expression for parsing data when the format
     * is recognized; otherwise null.
     */
    autoDetectLineRE: function(line) {
      // Matches Mali perf events with thread info
      var lineREWithThread =
        /^\s*\(([\w\-]*)\)\s*(\w+):\s*([\w\\\/\.\-]*@\d*):?\s*(.*)$/;
      if (lineREWithThread.test(line))
        return lineREWithThread;

      // Matches old-style Mali perf events
      var lineRENoThread = /^s*()(\w+):\s*([\w\\\/.\-]*):?\s*(.*)$/;
      if (lineRENoThread.test(line))
        return lineRENoThread;
      return null;
    },

    lineRE: null,

    /**
     * Parses maliDDK events and sets up state in the importer.
     * events will come in pairs with a cros_trace_print_enter
     * like this (line broken here for formatting):
     *
     * tracing_mark_write: mali_driver: (mali-012345) cros_trace_print_enter: \
     *   gles/src/texture/mali_gles_texture_slave.c@1505: gles2_texturep_upload
     *
     * and a cros_trace_print_exit like this:
     *
     * tracing_mark_write: mali_driver: (mali-012345) cros_trace_print_exit: \
     *   gles/src/texture/mali_gles_texture_slave.c@1505:
     */
    maliDDKEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
      if (this.lineRE == null) {
        this.lineRE = this.autoDetectLineRE(eventBase.details);
        if (this.lineRE == null)
          return false;
      }
      var maliEvent = this.lineRE.exec(eventBase.details);
      // Old-style Mali perf events have no thread id, so make one.
      var tid = (maliEvent[1] === '' ? 'mali' : maliEvent[1]);
      switch (maliEvent[2]) {
        case 'cros_trace_print_enter':
          this.maliDDKOpenSlice(pid, tid, ts, maliEvent[4],
              maliEvent[3]);
          break;
        case 'cros_trace_print_exit':
          this.maliDDKCloseSlice(pid, tid, ts, [], maliEvent[3]);
      }
      return true;
    },

    /*
     * Kernel event support.
     */

    dvfsSample: function(counterName, seriesName, ts, s) {
      var value = parseInt(s);
      var counter = this.model_.getOrCreateProcess(0).
          getOrCreateCounter('DVFS', counterName);
      if (counter.numSeries == 0) {
        counter.seriesNames.push(seriesName);
        counter.seriesColors.push(tracing.getStringColorId(counter.name));
      }
      counter.timestamps.push(ts);
      counter.samples.push(value);
    },

    dvfsEventEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
      var event = /utilization=(\d+)/.exec(eventBase.details);
      if (!event)
        return false;

      this.dvfsSample('DVFS Utilization', 'utilization', ts, event[1]);
      return true;
    },

    dvfsSetClockEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
      var event = /frequency=(\d+)/.exec(eventBase.details);
      if (!event)
        return false;

      this.dvfsSample('DVFS Frequency', 'frequency', ts, event[1]);
      return true;
    },

    dvfsSetVoltageEvent: function(eventName, cpuNumber, pid, ts, eventBase) {
      var event = /voltage=(\d+)/.exec(eventBase.details);
      if (!event)
        return false;

      this.dvfsSample('DVFS Voltage', 'voltage', ts, event[1]);
      return true;
    },

    hwcSample: function(cat, counterName, seriesName, ts, eventBase) {
      var event = /val=(\d+)/.exec(eventBase.details);
      if (!event)
        return false;
      var value = parseInt(event[1]);

      var counter = this.model_.getOrCreateProcess(0).
          getOrCreateCounter(cat, counterName);
      if (counter.numSeries == 0) {
        counter.seriesNames.push(seriesName);
        counter.seriesColors.push(tracing.getStringColorId(counter.name));
      }
      counter.timestamps.push(ts);
      counter.samples.push(value);
      return true;
    },

    /*
     * Job Manager block counters.
     */
    jmSample: function(ctrName, seriesName, ts, eventBase) {
      return this.hwcSample('mali:jm', 'JM: ' + ctrName, seriesName, ts,
          eventBase);
    },
    addJMCounter: function(hwcEventName, hwcTitle) {
      function handler(eventName, cpuNumber, pid, ts, eventBase) {
        return this.jmSample(hwcTitle, 'count', ts, eventBase);
      }
      this.importer.registerEventHandler(hwcEventName, handler.bind(this));
    },
    addJMCycles: function(hwcEventName, hwcTitle) {
      function handler(eventName, cpuNumber, pid, ts, eventBase) {
        return this.jmSample(hwcTitle, 'cycles', ts, eventBase);
      }
      this.importer.registerEventHandler(hwcEventName, handler.bind(this));
    },

    /*
     * Tiler block counters.
     */
    tilerSample: function(ctrName, seriesName, ts, eventBase) {
      return this.hwcSample('mali:tiler', 'Tiler: ' + ctrName, seriesName,
          ts, eventBase);
    },
    addTilerCounter: function(hwcEventName, hwcTitle) {
      function handler(eventName, cpuNumber, pid, ts, eventBase) {
        return this.tilerSample(hwcTitle, 'count', ts, eventBase);
      }
      this.importer.registerEventHandler(hwcEventName, handler.bind(this));
    },
    addTilerCycles: function(hwcEventName, hwcTitle) {
      function handler(eventName, cpuNumber, pid, ts, eventBase) {
        return this.tilerSample(hwcTitle, 'cycles', ts, eventBase);
      }
      this.importer.registerEventHandler(hwcEventName, handler.bind(this));
    },

    /*
     * Fragment counters.
     */
    fragSample: function(ctrName, seriesName, ts, eventBase) {
      return this.hwcSample('mali:fragment', 'Fragment: ' + ctrName,
          seriesName, ts, eventBase);
    },
    addFragCounter: function(hwcEventName, hwcTitle) {
      function handler(eventName, cpuNumber, pid, ts, eventBase) {
        return this.fragSample(hwcTitle, 'count', ts, eventBase);
      }
      this.importer.registerEventHandler(hwcEventName, handler.bind(this));
    },
    addFragCycles: function(hwcEventName, hwcTitle) {
      function handler(eventName, cpuNumber, pid, ts, eventBase) {
        return this.fragSample(hwcTitle, 'cycles', ts, eventBase);
      }
      this.importer.registerEventHandler(hwcEventName, handler.bind(this));
    },

    /*
     * Compute counters.
     */
    computeSample: function(ctrName, seriesName, ts, eventBase) {
      return this.hwcSample('mali:compute', 'Compute: ' + ctrName,
          seriesName, ts, eventBase);
    },
    addComputeCounter: function(hwcEventName, hwcTitle) {
      function handler(eventName, cpuNumber, pid, ts, eventBase) {
        return this.computeSample(hwcTitle, 'count', ts, eventBase);
      }
      this.importer.registerEventHandler(hwcEventName, handler.bind(this));
    },
    addComputeCycles: function(hwcEventName, hwcTitle) {
      function handler(eventName, cpuNumber, pid, ts, eventBase) {
        return this.computeSample(hwcTitle, 'cycles', ts, eventBase);
      }
      this.importer.registerEventHandler(hwcEventName, handler.bind(this));
    },

    /*
     * Tripipe counters.
     */
    addTripipeCycles: function(hwcEventName, hwcTitle) {
      function handler(eventName, cpuNumber, pid, ts, eventBase) {
        return this.hwcSample('mali:shader', 'Tripipe: ' + hwcTitle, 'cycles',
            ts, eventBase);
      }
      this.importer.registerEventHandler(hwcEventName, handler.bind(this));
    },

    /*
     * Arith counters.
     */
    arithSample: function(ctrName, seriesName, ts, eventBase) {
      return this.hwcSample('mali:arith', 'Arith: ' + ctrName, seriesName, ts,
          eventBase);
    },
    addArithCounter: function(hwcEventName, hwcTitle) {
      function handler(eventName, cpuNumber, pid, ts, eventBase) {
        return this.arithSample(hwcTitle, 'count', ts, eventBase);
      }
      this.importer.registerEventHandler(hwcEventName, handler.bind(this));
    },
    addArithCycles: function(hwcEventName, hwcTitle) {
      function handler(eventName, cpuNumber, pid, ts, eventBase) {
        return this.arithSample(hwcTitle, 'cycles', ts, eventBase);
      }
      this.importer.registerEventHandler(hwcEventName, handler.bind(this));
    },

    /*
     * Load/Store counters.
     */
    addLSCounter: function(hwcEventName, hwcTitle) {
      function handler(eventName, cpuNumber, pid, ts, eventBase) {
        return this.hwcSample('mali:ls', 'LS: ' + hwcTitle, 'count', ts,
            eventBase);
      }
      this.importer.registerEventHandler(hwcEventName, handler.bind(this));
    },

    /*
     * Texture counters.
     */
    textureSample: function(ctrName, seriesName, ts, eventBase) {
      return this.hwcSample('mali:texture', 'Texture: ' + ctrName,
          seriesName, ts, eventBase);
    },
    addTexCounter: function(hwcEventName, hwcTitle) {
      function handler(eventName, cpuNumber, pid, ts, eventBase) {
        return this.textureSample(hwcTitle, 'count', ts, eventBase);
      }
      this.importer.registerEventHandler(hwcEventName, handler.bind(this));
    },

    /*
     * LSC counters.
     */
    addLSCCounter: function(hwcEventName, hwcTitle) {
      function handler(eventName, cpuNumber, pid, ts, eventBase) {
        return this.hwcSample('mali:lsc', 'LSC: ' + hwcTitle, 'count', ts,
            eventBase);
      }
      this.importer.registerEventHandler(hwcEventName, handler.bind(this));
    },

    /*
     * TLB counters.
     */
    addAXICounter: function(hwcEventName, hwcTitle) {
      function handler(eventName, cpuNumber, pid, ts, eventBase) {
        return this.hwcSample('mali:axi', 'AXI: ' + hwcTitle, 'count', ts,
            eventBase);
      }
      this.importer.registerEventHandler(hwcEventName, handler.bind(this));
    },

    /*
     * MMU counters.
     */
    mmuSample: function(ctrName, seriesName, ts, eventBase) {
      return this.hwcSample('mali:mmu', 'MMU: ' + ctrName, seriesName, ts,
          eventBase);
    },
    addMMUCounter: function(hwcEventName, hwcTitle) {
      function handler(eventName, cpuNumber, pid, ts, eventBase) {
        return this.mmuSample(hwcTitle, 'count', ts, eventBase);
      }
      this.importer.registerEventHandler(hwcEventName, handler.bind(this));
    },
    addMMUCycles: function(hwcEventName, hwcTitle) {
      function handler(eventName, cpuNumber, pid, ts, eventBase) {
        return this.mmuSample(hwcTitle, 'cycles', ts, eventBase);
      }
      this.importer.registerEventHandler(hwcEventName, handler.bind(this));
    },

    /*
     * L2 counters.
     */
    l2Sample: function(ctrName, seriesName, ts, eventBase) {
      return this.hwcSample('mali:l2', 'L2: ' + ctrName, seriesName, ts,
          eventBase);
    },
    addL2Counter: function(hwcEventName, hwcTitle) {
      function handler(eventName, cpuNumber, pid, ts, eventBase) {
        return this.l2Sample(hwcTitle, 'count', ts, eventBase);
      }
      this.importer.registerEventHandler(hwcEventName, handler.bind(this));
    },
    addL2Cycles: function(hwcEventName, hwcTitle) {
      function handler(eventName, cpuNumber, pid, ts, eventBase) {
        return this.l2Sample(hwcTitle, 'cycles', ts, eventBase);
      }
      this.importer.registerEventHandler(hwcEventName, handler.bind(this));
    }
  };

  Parser.registerSubtype(MaliParser);

  return {
    MaliParser: MaliParser
  };
});