Evgeniy Stepanov | bec2f0e | 2011-03-05 12:50:33 +0300 | [diff] [blame] | 1 | /* Copyright (c) 2008-2010, Google Inc. |
| 2 | * All rights reserved. |
| 3 | * |
| 4 | * Redistribution and use in source and binary forms, with or without |
| 5 | * modification, are permitted provided that the following conditions are |
| 6 | * met: |
| 7 | * |
| 8 | * * Redistributions of source code must retain the above copyright |
| 9 | * notice, this list of conditions and the following disclaimer. |
| 10 | * * Neither the name of Google Inc. nor the names of its |
| 11 | * contributors may be used to endorse or promote products derived from |
| 12 | * this software without specific prior written permission. |
| 13 | * |
| 14 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| 15 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| 16 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| 17 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| 18 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 19 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| 20 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| 21 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| 22 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| 24 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 25 | */ |
| 26 | |
| 27 | // This file is part of ThreadSanitizer, a dynamic data race detector. |
| 28 | // Author: Konstantin Serebryany. |
| 29 | // Information about one TRACE (single-entry-multiple-exit region of code). |
| 30 | #ifndef TS_TRACE_INFO_ |
| 31 | #define TS_TRACE_INFO_ |
| 32 | |
| 33 | #include "ts_util.h" |
| 34 | // Information about one Memory Operation. |
| 35 | // |
| 36 | // A memory access is represented by mop[idx] = {pc,size,is_write} |
| 37 | // which is computed at instrumentation time and {actual_address} computed |
| 38 | // at run-time. The instrumentation insn looks like |
| 39 | // tleb[idx] = actual_address |
| 40 | // The create_sblock field tells if we want to remember the stack trace |
| 41 | // which corresponds to this Mop (i.e. create an SBLOCK). |
| 42 | struct MopInfo { |
| 43 | public: |
| 44 | MopInfo(uintptr_t pc, size_t size, bool is_write, bool create_sblock) { |
| 45 | DCHECK(sizeof(*this) == 8); |
| 46 | pc_ = pc; |
| 47 | if (size > 16) size = 16; // some instructions access more than 16 bytes. |
| 48 | size_minus1_ = size - 1; |
| 49 | is_write_ = is_write; |
| 50 | create_sblock_ = create_sblock; |
| 51 | |
| 52 | DCHECK(size != 0); |
| 53 | DCHECK(this->size() == size); |
| 54 | DCHECK(this->is_write() == is_write); |
| 55 | DCHECK(this->create_sblock() == create_sblock); |
| 56 | } |
| 57 | |
| 58 | MopInfo() { |
| 59 | DCHECK(sizeof(*this) == 8); |
| 60 | memset(this, 0, sizeof(*this)); |
| 61 | } |
| 62 | |
| 63 | uintptr_t pc() { return pc_; }; |
| 64 | size_t size() { return size_minus1_ + 1; } |
| 65 | bool is_write() { return is_write_; } |
| 66 | bool create_sblock() { return create_sblock_; } |
| 67 | |
| 68 | private: |
| 69 | uint64_t pc_ :58; // 48 bits is enough for pc, even on x86-64. |
| 70 | uintptr_t create_sblock_ :1; |
| 71 | uintptr_t is_write_ :1; |
| 72 | uintptr_t size_minus1_ :4; // 0..15 |
| 73 | }; |
| 74 | |
| 75 | // ---------------- Lite Race ------------------ |
| 76 | // Experimental! |
| 77 | // |
| 78 | // The idea was first introduced in LiteRace: |
| 79 | // http://www.cs.ucla.edu/~dlmarino/pubs/pldi09.pdf |
| 80 | // Instead of analyzing all memory accesses, we do sampling. |
| 81 | // For each trace (single-enry muliple-exit region) we maintain a counter of |
| 82 | // executions. If a trace has been executed more than a certain threshold, we |
| 83 | // start skipping this trace sometimes. |
| 84 | // The LiteRace paper suggests several strategies for sampling, including |
| 85 | // thread-local counters. Having thread local counters for all threads is too |
| 86 | // expensive, so we have kLiteRaceNumTids arrays of counters and use |
| 87 | // the array (tid % 8). |
| 88 | // |
| 89 | // sampling_rate indicates the level of sampling. |
| 90 | // 0 means no sampling. |
| 91 | // 1 means handle *almost* all accesses. |
| 92 | // ... |
| 93 | // 31 means very aggressive sampling (skip a lot of accesses). |
| 94 | |
| 95 | // |
| 96 | // Note: ANNOTATE_PUBLISH_MEMORY() does not work with sampling... :( |
| 97 | |
| 98 | struct LiteRaceCounters { |
| 99 | uint32_t counter; |
| 100 | int32_t num_to_skip; |
| 101 | }; |
| 102 | |
| 103 | typedef LiteRaceCounters LiteRaceStorage[8][8]; |
| 104 | |
| 105 | struct TraceInfoPOD { |
| 106 | enum { kLiteRaceNumTids = 8 }; |
| 107 | enum { kLiteRaceStorageSize = 8 }; |
| 108 | size_t n_mops_; |
| 109 | size_t pc_; |
| 110 | size_t counter_; |
| 111 | uint32_t literace_counters[kLiteRaceNumTids]; |
| 112 | int32_t literace_num_to_skip[kLiteRaceNumTids]; |
| 113 | // [kLiteRaceNumTids]x[kLiteRaceStorageSize] |
| 114 | LiteRaceStorage *literace_storage; |
| 115 | int32_t storage_index; |
| 116 | MopInfo mops_[1]; |
| 117 | }; |
| 118 | |
| 119 | // An instance of this class is created for each TRACE (SEME region) |
| 120 | // during instrumentation. |
| 121 | class TraceInfo : public TraceInfoPOD { |
| 122 | public: |
| 123 | static TraceInfo *NewTraceInfo(size_t n_mops, uintptr_t pc); |
| 124 | void DeleteTraceInfo(TraceInfo *trace_info) { |
| 125 | delete [] (uintptr_t*)trace_info; |
| 126 | } |
| 127 | MopInfo *GetMop(size_t i) { |
| 128 | DCHECK(i < n_mops_); |
| 129 | return &mops_[i]; |
| 130 | } |
| 131 | |
| 132 | size_t n_mops() const { return n_mops_; } |
| 133 | size_t pc() const { return pc_; } |
| 134 | size_t &counter() { return counter_; } |
| 135 | MopInfo *mops() { return mops_; } |
| 136 | |
| 137 | static void PrintTraceProfile(); |
| 138 | |
| 139 | INLINE bool LiteRaceSkipTraceQuickCheck(uintptr_t tid_modulo_num) { |
| 140 | DCHECK(tid_modulo_num < kLiteRaceNumTids); |
| 141 | // Check how may accesses are left to skip. |
| 142 | // Racey, but ok. |
| 143 | int32_t num_to_skip = --(literace_num_to_skip[tid_modulo_num]); |
| 144 | if (num_to_skip > 0) { |
| 145 | return true; |
| 146 | } |
| 147 | return false; |
| 148 | } |
| 149 | |
| 150 | INLINE void LiteRaceUpdate(uintptr_t tid_modulo_num, uint32_t sampling_rate) { |
| 151 | DCHECK(sampling_rate < 32); |
| 152 | DCHECK(sampling_rate > 0); |
| 153 | uint32_t cur_counter = literace_counters[tid_modulo_num]; |
| 154 | // The bigger the counter the bigger the number of skipped accesses. |
| 155 | int32_t next_num_to_skip = (cur_counter >> (32 - sampling_rate)) + 1; |
| 156 | //if (id() == 2861) |
| 157 | // Printf("T%d id=%ld take s=%d c=%u\n", tid_modulo_num, id(), |
| 158 | // next_num_to_skip, cur_counter); |
| 159 | literace_num_to_skip[tid_modulo_num] = next_num_to_skip; |
| 160 | literace_counters[tid_modulo_num] = cur_counter + next_num_to_skip; |
| 161 | } |
| 162 | |
| 163 | INLINE void LLVMLiteRaceUpdate(uintptr_t tid_modulo_num, |
| 164 | uint32_t sampling_rate) { |
| 165 | DCHECK(sampling_rate < 32); |
| 166 | DCHECK(sampling_rate > 0); |
| 167 | LiteRaceCounters *counters = |
| 168 | &((*literace_storage)[tid_modulo_num][storage_index]); |
| 169 | uint32_t cur_counter = counters->counter; |
| 170 | // The bigger the counter the bigger the number of skipped accesses. |
| 171 | int32_t next_num_to_skip = (cur_counter >> (32 - sampling_rate)) + 1; |
| 172 | counters->num_to_skip = next_num_to_skip; |
| 173 | counters->counter = cur_counter + next_num_to_skip; |
| 174 | } |
| 175 | |
| 176 | // This is all racey, but ok. |
| 177 | INLINE bool LiteRaceSkipTrace(uint32_t tid_modulo_num, |
| 178 | uint32_t sampling_rate) { |
| 179 | if (LiteRaceSkipTraceQuickCheck(tid_modulo_num)) return true; |
| 180 | LiteRaceUpdate(tid_modulo_num, sampling_rate); |
| 181 | return false; |
| 182 | } |
| 183 | |
| 184 | INLINE bool LiteRaceSkipTraceRealTid(uint32_t tid, uint32_t sampling_rate) { |
| 185 | return LiteRaceSkipTrace(tid % kLiteRaceNumTids, sampling_rate); |
| 186 | } |
| 187 | |
| 188 | private: |
| 189 | static size_t id_counter_; |
| 190 | static vector<TraceInfo*> *g_all_traces; |
| 191 | |
| 192 | TraceInfo() : TraceInfoPOD() { } |
| 193 | }; |
| 194 | |
| 195 | // end. {{{1 |
| 196 | #endif // TS_TRACE_INFO_ |
| 197 | // vim:shiftwidth=2:softtabstop=2:expandtab:tw=80 |