/* Copyright (c) 2008-2010, Google Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *
 *     * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 *     * Neither the name of Google Inc. nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

// This file is part of ThreadSanitizer, a dynamic data race detector.
// Author: Konstantin Serebryany.
// Information about one TRACE (single-entry-multiple-exit region of code).
#ifndef TS_TRACE_INFO_
#define TS_TRACE_INFO_

#include "ts_util.h"
// Information about one Memory Operation.
//
// A memory access is represented by mop[idx] = {pc,size,is_write}
// which is computed at instrumentation time and {actual_address} computed
// at run-time. The instrumentation insn looks like
//  tleb[idx] = actual_address
// The create_sblock field tells if we want to remember the stack trace
// which corresponds to this Mop (i.e. create an SBLOCK).
struct MopInfo {
 public:
  MopInfo(uintptr_t pc, size_t size, bool is_write, bool create_sblock) {
    DCHECK(sizeof(*this) == 8);
    pc_ = pc;
    if (size > 16) size = 16; // some instructions access more than 16 bytes.
    size_minus1_ = size - 1;
    is_write_ = is_write;
    create_sblock_ = create_sblock;

    DCHECK(size != 0);
    DCHECK(this->size() == size);
    DCHECK(this->is_write() == is_write);
    DCHECK(this->create_sblock() == create_sblock);
  }

  MopInfo() {
    DCHECK(sizeof(*this) == 8);
    memset(this, 0, sizeof(*this));
  }

  uintptr_t pc()            { return pc_; };
  size_t    size()          { return size_minus1_ + 1; }
  bool      is_write()      { return is_write_; }
  bool      create_sblock() { return create_sblock_; }

 private:
  uint64_t  pc_           :58;  // 48 bits is enough for pc, even on x86-64.
  uint64_t  create_sblock_ :1;
  uint64_t  is_write_      :1;
  uint64_t  size_minus1_   :4;  // 0..15
};

// ---------------- Lite Race ------------------
// Experimental!
//
// The idea was first introduced in LiteRace:
// http://www.cs.ucla.edu/~dlmarino/pubs/pldi09.pdf
// Instead of analyzing all memory accesses, we do sampling.
// For each trace (single-enry muliple-exit region) we maintain a counter of
// executions. If a trace has been executed more than a certain threshold, we
// start skipping this trace sometimes.
// The LiteRace paper suggests several strategies for sampling, including
// thread-local counters. Having thread local counters for all threads is too
// expensive, so we have kLiteRaceNumTids arrays of counters and use
// the array (tid % 8).
//
// sampling_rate indicates the level of sampling.
// 0 means no sampling.
// 1 means handle *almost* all accesses.
// ...
// 31 means very aggressive sampling (skip a lot of accesses).

//
// Note: ANNOTATE_PUBLISH_MEMORY() does not work with sampling... :(

struct LiteRaceCounters {
  uint32_t counter;
  int32_t num_to_skip;
};

struct TraceInfoPOD {
  enum { kLiteRaceNumTids = 8 };
  enum { kLiteRaceStorageSize = 8 };
  typedef LiteRaceCounters LiteRaceStorage[kLiteRaceNumTids][kLiteRaceStorageSize];

  size_t n_mops_;
  size_t pc_;
  size_t counter_;
  LiteRaceStorage *literace_storage;
  int32_t storage_index;
  MopInfo mops_[1];
};

// An instance of this class is created for each TRACE (SEME region)
// during instrumentation.
class TraceInfo : public TraceInfoPOD {
 public:
  static TraceInfo *NewTraceInfo(size_t n_mops, uintptr_t pc);
  void DeleteTraceInfo(TraceInfo *trace_info) {
    delete [] (uintptr_t*)trace_info;
  }
  MopInfo *GetMop(size_t i) {
    DCHECK(i < n_mops_);
    return &mops_[i];
  }

  size_t n_mops() const { return n_mops_; }
  size_t pc()     const { return pc_; }
  size_t &counter()     { return counter_; }
  MopInfo *mops()       { return mops_; }

  static void PrintTraceProfile();

  INLINE bool LiteRaceSkipTraceQuickCheck(uintptr_t tid_modulo_num) {
    DCHECK(tid_modulo_num < kLiteRaceNumTids);
    // Check how may accesses are left to skip. Racey, but ok.
    LiteRaceCounters *counters =
        &((*literace_storage)[tid_modulo_num][storage_index]);
    int32_t num_to_skip = --counters->num_to_skip;
    if (num_to_skip > 0) {
      return true;
    }
    return false;
  }

  INLINE void LiteRaceUpdate(uintptr_t tid_modulo_num, uint32_t sampling_rate) {
    DCHECK(sampling_rate < 32);
    DCHECK(sampling_rate > 0);
    LiteRaceCounters *counters =
        &((*literace_storage)[tid_modulo_num][storage_index]);
    uint32_t cur_counter = counters->counter;
    // The bigger the counter the bigger the number of skipped accesses.
    int32_t next_num_to_skip = (cur_counter >> (32 - sampling_rate)) + 1;
    counters->num_to_skip = next_num_to_skip;
    counters->counter = cur_counter + next_num_to_skip;

  }

  // TODO(glider): get rid of this.
  INLINE void LLVMLiteRaceUpdate(uintptr_t tid_modulo_num,
                                 uint32_t sampling_rate) {
    LiteRaceUpdate(tid_modulo_num, sampling_rate);
  }

  // This is all racey, but ok.
  INLINE bool LiteRaceSkipTrace(uint32_t tid_modulo_num,
                                uint32_t sampling_rate) {
    if (LiteRaceSkipTraceQuickCheck(tid_modulo_num)) return true;
    LiteRaceUpdate(tid_modulo_num, sampling_rate);
    return false;
  }

  INLINE bool LiteRaceSkipTraceRealTid(uint32_t tid, uint32_t sampling_rate) {
    return LiteRaceSkipTrace(tid % kLiteRaceNumTids, sampling_rate);
  }

 private:
  static size_t id_counter_;
  static vector<TraceInfo*> *g_all_traces;

  TraceInfo() : TraceInfoPOD() { }
};

// end. {{{1
#endif  // TS_TRACE_INFO_
// vim:shiftwidth=2:softtabstop=2:expandtab:tw=80