aboutsummaryrefslogtreecommitdiff
path: root/libsanitizer/tsan/tsan_sync.h
blob: 67d3c0b5e7dd7bd6fe73ad7bbec4e03eef043968 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//===-- tsan_sync.h ---------------------------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file is a part of ThreadSanitizer (TSan), a race detector.
//
//===----------------------------------------------------------------------===//
#ifndef TSAN_SYNC_H
#define TSAN_SYNC_H

#include "sanitizer_common/sanitizer_atomic.h"
#include "sanitizer_common/sanitizer_common.h"
#include "sanitizer_common/sanitizer_deadlock_detector_interface.h"
#include "tsan_defs.h"
#include "tsan_dense_alloc.h"
#include "tsan_shadow.h"
#include "tsan_vector_clock.h"

namespace __tsan {

// These need to match __tsan_mutex_* flags defined in tsan_interface.h.
// See documentation there as well.
enum MutexFlags {
  MutexFlagLinkerInit          = 1 << 0, // __tsan_mutex_linker_init
  MutexFlagWriteReentrant      = 1 << 1, // __tsan_mutex_write_reentrant
  MutexFlagReadReentrant       = 1 << 2, // __tsan_mutex_read_reentrant
  MutexFlagReadLock            = 1 << 3, // __tsan_mutex_read_lock
  MutexFlagTryLock             = 1 << 4, // __tsan_mutex_try_lock
  MutexFlagTryLockFailed       = 1 << 5, // __tsan_mutex_try_lock_failed
  MutexFlagRecursiveLock       = 1 << 6, // __tsan_mutex_recursive_lock
  MutexFlagRecursiveUnlock     = 1 << 7, // __tsan_mutex_recursive_unlock
  MutexFlagNotStatic           = 1 << 8, // __tsan_mutex_not_static

  // The following flags are runtime private.
  // Mutex API misuse was detected, so don't report any more.
  MutexFlagBroken              = 1 << 30,
  // We did not intercept pre lock event, so handle it on post lock.
  MutexFlagDoPreLockOnPostLock = 1 << 29,
  // Must list all mutex creation flags.
  MutexCreationFlagMask        = MutexFlagLinkerInit |
                                 MutexFlagWriteReentrant |
                                 MutexFlagReadReentrant |
                                 MutexFlagNotStatic,
};

// SyncVar is a descriptor of a user synchronization object
// (mutex or an atomic variable).
struct SyncVar {
  SyncVar();

  uptr addr;  // overwritten by DenseSlabAlloc freelist
  Mutex mtx;
  StackID creation_stack_id;
  Tid owner_tid;  // Set only by exclusive owners.
  FastState last_lock;
  int recursion;
  atomic_uint32_t flags;
  u32 next;  // in MetaMap
  DDMutex dd;
  VectorClock *read_clock;  // Used for rw mutexes only.
  VectorClock *clock;

  void Init(ThreadState *thr, uptr pc, uptr addr, bool save_stack);
  void Reset();

  bool IsFlagSet(u32 f) const {
    return atomic_load_relaxed(&flags) & f;
  }

  void SetFlags(u32 f) {
    atomic_store_relaxed(&flags, atomic_load_relaxed(&flags) | f);
  }

  void UpdateFlags(u32 flagz) {
    // Filter out operation flags.
    if (!(flagz & MutexCreationFlagMask))
      return;
    u32 current = atomic_load_relaxed(&flags);
    if (current & MutexCreationFlagMask)
      return;
    // Note: this can be called from MutexPostReadLock which holds only read
    // lock on the SyncVar.
    atomic_store_relaxed(&flags, current | (flagz & MutexCreationFlagMask));
  }
};

// MetaMap maps app addresses to heap block (MBlock) and sync var (SyncVar)
// descriptors. It uses 1/2 direct shadow, see tsan_platform.h for the mapping.
class MetaMap {
 public:
  MetaMap();

  void AllocBlock(ThreadState *thr, uptr pc, uptr p, uptr sz);

  // FreeBlock resets all sync objects in the range if reset=true and must not
  // run concurrently with ResetClocks which resets all sync objects
  // w/o any synchronization (as part of DoReset).
  // If we don't have a thread slot (very early/late in thread lifetime or
  // Go/Java callbacks) or the slot is not locked, then reset must be set to
  // false. In such case sync object clocks will be reset later (when it's
  // reused or during the next ResetClocks).
  uptr FreeBlock(Processor *proc, uptr p, bool reset);
  bool FreeRange(Processor *proc, uptr p, uptr sz, bool reset);
  void ResetRange(Processor *proc, uptr p, uptr sz, bool reset);
  // Reset vector clocks of all sync objects.
  // Must be called when no other threads access sync objects.
  void ResetClocks();
  MBlock* GetBlock(uptr p);

  SyncVar *GetSyncOrCreate(ThreadState *thr, uptr pc, uptr addr,
                           bool save_stack) {
    return GetSync(thr, pc, addr, true, save_stack);
  }
  SyncVar *GetSyncIfExists(uptr addr) {
    return GetSync(nullptr, 0, addr, false, false);
  }

  void MoveMemory(uptr src, uptr dst, uptr sz);

  void OnProcIdle(Processor *proc);

  struct MemoryStats {
    uptr mem_block;
    uptr sync_obj;
  };

  MemoryStats GetMemoryStats() const;

 private:
  static const u32 kFlagMask  = 3u << 30;
  static const u32 kFlagBlock = 1u << 30;
  static const u32 kFlagSync  = 2u << 30;
  typedef DenseSlabAlloc<MBlock, 1 << 18, 1 << 12, kFlagMask> BlockAlloc;
  typedef DenseSlabAlloc<SyncVar, 1 << 20, 1 << 10, kFlagMask> SyncAlloc;
  BlockAlloc block_alloc_;
  SyncAlloc sync_alloc_;

  SyncVar *GetSync(ThreadState *thr, uptr pc, uptr addr, bool create,
                   bool save_stack);
};

}  // namespace __tsan

#endif  // TSAN_SYNC_H