/* 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.
//--------- Head ------------------- {{{1
#ifndef THREAD_SANITIZER_H_
#define THREAD_SANITIZER_H_

#include "ts_util.h"
#include "ts_atomic.h"

//--------- Utils ------------------- {{{1

void Report(const char *format, ...);
void PcToStrings(uintptr_t pc, bool demangle,
                string *img_name, string *rtn_name,
                string *file_name, int *line_no);
string PcToRtnNameAndFilePos(uintptr_t pc);
string PcToRtnName(uintptr_t pc, bool demangle);
string Demangle(const char *str);


//--------- FLAGS ---------------------------------- {{{1
struct FLAGS {
  string           input_type; // for ts_offline.
                               // Possible values: str, bin, decode.
  bool             ignore_stack;
  intptr_t         verbosity;
  intptr_t         show_stats;  // 0 -- no stats; 1 -- some stats; 2 more stats.
  bool             trace_profile;
  bool             show_expected_races;
  uintptr_t        trace_addr;
  uintptr_t        segment_set_recycle_queue_size;
  uintptr_t        recent_segments_cache_size;
  vector<string>   file_prefix_to_cut;
  vector<string>   ignore;
  vector<string>   whitelist;
  bool             ignore_unknown_pcs;  // Ignore PCs with no debug info.
  vector<string>   cut_stack_below;
  string           summary_file;
  string           log_file;
  bool             offline;
  intptr_t         max_n_threads;
  bool             compress_cache_lines;
  bool             unlock_on_mutex_destroy;

  intptr_t         sample_events;
  intptr_t         sample_events_depth;

  intptr_t         num_callers;

  intptr_t    keep_history;
  bool        pure_happens_before;
  bool        free_is_write;
  bool        exit_after_main;
  bool        demangle;
  bool        announce_threads;
  bool        full_output;
  bool        show_states;
  bool        show_proc_self_status;
  bool        show_valgrind_context;  // debug-only
  bool        suggest_happens_before_arcs;
  bool        show_pc;
  bool        full_stack_frames;
  bool        color;  // Colorify terminal output.
  bool        html;  // Output in html format.
  bool        show_pid;

  intptr_t  debug_level;
  bool        save_ignore_context;  // print stack if ignore_end was forgotten.
  vector<string> debug_phase;
  intptr_t  trace_level;

  intptr_t     dry_run;
  intptr_t     max_sid;
  intptr_t     max_sid_before_flush;
  intptr_t     max_mem_in_mb;
  intptr_t     num_callers_in_history;
  intptr_t     flush_period;

  intptr_t     literace_sampling;
  bool         start_with_global_ignore_on;

  intptr_t     locking_scheme;  // Used for internal experiments with locking.

  bool         report_races;
  bool         thread_coverage;
  bool         atomicity;
  bool         call_coverage;
  string       dump_events;  // The name of log file. Debug mode only.
  bool         symbolize;
  bool         attach_mode;

  string       tsan_program_name;
  string       tsan_url;

  vector<string> suppressions;
  bool           generate_suppressions;

  intptr_t     error_exitcode;
  bool         trace_children;

  vector<string> race_verifier;
  vector<string> race_verifier_extra;
  intptr_t       race_verifier_sleep_ms;

  bool nacl_untrusted;

  bool threaded_analysis;

  bool sched_shake;
  bool api_ambush;

  bool enable_atomic;
};

extern FLAGS *G_flags;

extern bool g_race_verifier_active;

extern bool debug_expected_races;
extern bool debug_malloc;
extern bool debug_free;
extern bool debug_thread;
extern bool debug_rtn;
extern bool debug_wrap;
extern bool debug_ins;
extern bool debug_shadow_stack;
extern bool debug_race_verifier;

// -------- CallStack ------------- {{{1
const size_t kMaxCallStackSize = 1 << 12;

struct CallStackPod {
  uintptr_t *end_;
  uintptr_t pcs_[kMaxCallStackSize];
};

struct CallStack: public CallStackPod {

  CallStack() { Clear(); }

  size_t size() { return (size_t)(end_ - pcs_); }
  uintptr_t *pcs() { return pcs_; }

  bool empty() { return end_ == pcs_; }

  uintptr_t &back() {
    DCHECK(!empty());
    return *(end_ - 1);
  }

  void pop_back() {
    DCHECK(!empty());
    end_--;
  }

  void push_back(uintptr_t pc) {
    DCHECK(size() < kMaxCallStackSize);
    *end_ = pc;
    end_++;
  }

  void Clear() {
    end_ = pcs_;
  }

  uintptr_t &operator[] (size_t i) {
    DCHECK(i < size());
    return pcs_[i];
  }

};

//--------- TS Exports ----------------- {{{1
#include "ts_events.h"
#include "ts_trace_info.h"

struct TSanThread;
void ThreadSanitizerInit();
void ThreadSanitizerFini();
// TODO(glider): this is a temporary solution to avoid deadlocks after fork().
#ifdef TS_LLVM
void ThreadSanitizerLockAcquire();
void ThreadSanitizerLockRelease();
#endif
void ThreadSanitizerHandleOneEvent(Event *event);
TSanThread *ThreadSanitizerGetThreadByTid(int32_t tid);
void ThreadSanitizerHandleTrace(int32_t tid, TraceInfo *trace_info,
                                       uintptr_t *tleb);
void ThreadSanitizerHandleTrace(TSanThread *thr, TraceInfo *trace_info,
                                       uintptr_t *tleb);
void ThreadSanitizerHandleOneMemoryAccess(TSanThread *thr, MopInfo mop,
                                                 uintptr_t addr);
void ThreadSanitizerParseFlags(vector<string>* args);
bool ThreadSanitizerWantToInstrumentSblock(uintptr_t pc);
bool ThreadSanitizerWantToCreateSegmentsOnSblockEntry(uintptr_t pc);
bool ThreadSanitizerIgnoreAccessesBelowFunction(uintptr_t pc);

typedef int (*ThreadSanitizerUnwindCallback)(uintptr_t* stack, int size, uintptr_t pc);
void ThreadSanitizerSetUnwindCallback(ThreadSanitizerUnwindCallback cb);

/** Atomic operation handler.
 *  @param tid ID of a thread that issues the operation.
 *  @param pc Program counter that should be associated with the operation.
 *  @param op Type of the operation (load, store, etc).
 *  @param mo Memory ordering associated with the operation
 *      (relaxed, acquire, release, etc). NB there are some restrictions on
 *      what memory orderings can be used with what types of operations.
 *      E.g. a store can't have an acquire semantics
 *      (see C++0x standard draft for details).
 *  @param fail_mo Memory ordering the operation has if it fails,
 *      applicable only to compare_exchange oprations.
 *  @param size Size of the memory access in bytes (1, 2, 4 or 8).
 *  @param a Address of the memory access.
 *  @param v Operand for the operation (e.g. a value to store).
 *  @param cmp Comparand for compare_exchange oprations.
 *  @return Result of the operation (e.g. loaded value).
 */
uint64_t ThreadSanitizerHandleAtomicOp(int32_t tid,
                                       uintptr_t pc,
                                       tsan_atomic_op op,
                                       tsan_memory_order mo,
                                       tsan_memory_order fail_mo,
                                       size_t size,
                                       void volatile* a,
                                       uint64_t v,
                                       uint64_t cmp);

enum IGNORE_BELOW_RTN {
  IGNORE_BELOW_RTN_UNKNOWN,
  IGNORE_BELOW_RTN_NO,
  IGNORE_BELOW_RTN_YES
};

void ThreadSanitizerHandleRtnCall(int32_t tid, uintptr_t call_pc,
                                         uintptr_t target_pc,
                                         IGNORE_BELOW_RTN ignore_below);

void ThreadSanitizerHandleRtnExit(int32_t tid);

void ThreadSanitizerPrintUsage();
extern "C" const char *ThreadSanitizerQuery(const char *query);
bool PhaseDebugIsOn(const char *phase_name);

extern bool g_has_entered_main;
extern bool g_has_exited_main;

// -------- Stats ------------------- {{{1
#include "ts_stats.h"
extern Stats *G_stats;

// -------- Expected Race ---------------------- {{{1
// Information about expected races.
struct ExpectedRace {
  uintptr_t   ptr;
  uintptr_t   size;
  bool        is_verifiable;
  bool        is_nacl_untrusted;
  int         count;
  const char *description;
  uintptr_t   pc;
};

ExpectedRace* ThreadSanitizerFindExpectedRace(uintptr_t addr);

// Tell ThreadSanitizer about the location of NaCl untrusted region.
void ThreadSanitizerNaclUntrustedRegion(uintptr_t mem_start, uintptr_t mem_end);

// Returns true if accesses and locks at the given address should be ignored
// according to the current NaCl flags (--nacl-untrusted). Always false if not a
// NaCl program.
bool ThreadSanitizerIgnoreForNacl(uintptr_t addr);

// end. {{{1
#endif  //  THREAD_SANITIZER_H_

// vim:shiftwidth=2:softtabstop=2:expandtab