1//===-- asan_thread_registry.cc -------------------------------------------===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file is a part of AddressSanitizer, an address sanity checker. 11// 12// AsanThreadRegistry-related code. AsanThreadRegistry is a container 13// for summaries of all created threads. 14//===----------------------------------------------------------------------===// 15 16#include "asan_stack.h" 17#include "asan_thread.h" 18#include "asan_thread_registry.h" 19#include "sanitizer_common/sanitizer_common.h" 20 21namespace __asan { 22 23static AsanThreadRegistry asan_thread_registry(LINKER_INITIALIZED); 24 25AsanThreadRegistry &asanThreadRegistry() { 26 return asan_thread_registry; 27} 28 29AsanThreadRegistry::AsanThreadRegistry(LinkerInitialized x) 30 : main_thread_(x), 31 main_thread_summary_(x), 32 accumulated_stats_(x), 33 max_malloced_memory_(x), 34 mu_(x) { } 35 36void AsanThreadRegistry::Init() { 37 AsanTSDInit(AsanThreadSummary::TSDDtor); 38 main_thread_.set_summary(&main_thread_summary_); 39 main_thread_summary_.set_thread(&main_thread_); 40 RegisterThread(&main_thread_); 41 SetCurrent(&main_thread_); 42 // At this point only one thread exists. 43 inited_ = true; 44} 45 46void AsanThreadRegistry::RegisterThread(AsanThread *thread) { 47 BlockingMutexLock lock(&mu_); 48 u32 tid = n_threads_; 49 n_threads_++; 50 CHECK(n_threads_ < kMaxNumberOfThreads); 51 52 AsanThreadSummary *summary = thread->summary(); 53 CHECK(summary != 0); 54 summary->set_tid(tid); 55 thread_summaries_[tid] = summary; 56} 57 58void AsanThreadRegistry::UnregisterThread(AsanThread *thread) { 59 BlockingMutexLock lock(&mu_); 60 FlushToAccumulatedStatsUnlocked(&thread->stats()); 61 AsanThreadSummary *summary = thread->summary(); 62 CHECK(summary); 63 summary->set_thread(0); 64} 65 66AsanThread *AsanThreadRegistry::GetMain() { 67 return &main_thread_; 68} 69 70AsanThread *AsanThreadRegistry::GetCurrent() { 71 AsanThreadSummary *summary = (AsanThreadSummary *)AsanTSDGet(); 72 if (!summary) { 73#if ASAN_ANDROID 74 // On Android, libc constructor is called _after_ asan_init, and cleans up 75 // TSD. Try to figure out if this is still the main thread by the stack 76 // address. We are not entirely sure that we have correct main thread 77 // limits, so only do this magic on Android, and only if the found thread is 78 // the main thread. 79 AsanThread* thread = FindThreadByStackAddress((uptr)&summary); 80 if (thread && thread->tid() == 0) { 81 SetCurrent(thread); 82 return thread; 83 } 84#endif 85 return 0; 86 } 87 return summary->thread(); 88} 89 90void AsanThreadRegistry::SetCurrent(AsanThread *t) { 91 CHECK(t->summary()); 92 if (flags()->verbosity >= 2) { 93 Report("SetCurrent: %p for thread %p\n", 94 t->summary(), (void*)GetThreadSelf()); 95 } 96 // Make sure we do not reset the current AsanThread. 97 CHECK(AsanTSDGet() == 0); 98 AsanTSDSet(t->summary()); 99 CHECK(AsanTSDGet() == t->summary()); 100} 101 102AsanStats &AsanThreadRegistry::GetCurrentThreadStats() { 103 AsanThread *t = GetCurrent(); 104 return (t) ? t->stats() : main_thread_.stats(); 105} 106 107void AsanThreadRegistry::GetAccumulatedStats(AsanStats *stats) { 108 BlockingMutexLock lock(&mu_); 109 UpdateAccumulatedStatsUnlocked(); 110 internal_memcpy(stats, &accumulated_stats_, sizeof(accumulated_stats_)); 111} 112 113uptr AsanThreadRegistry::GetCurrentAllocatedBytes() { 114 BlockingMutexLock lock(&mu_); 115 UpdateAccumulatedStatsUnlocked(); 116 uptr malloced = accumulated_stats_.malloced; 117 uptr freed = accumulated_stats_.freed; 118 // Return sane value if malloced < freed due to racy 119 // way we update accumulated stats. 120 return (malloced > freed) ? malloced - freed : 1; 121} 122 123uptr AsanThreadRegistry::GetHeapSize() { 124 BlockingMutexLock lock(&mu_); 125 UpdateAccumulatedStatsUnlocked(); 126 return accumulated_stats_.mmaped - accumulated_stats_.munmaped; 127} 128 129uptr AsanThreadRegistry::GetFreeBytes() { 130 BlockingMutexLock lock(&mu_); 131 UpdateAccumulatedStatsUnlocked(); 132 uptr total_free = accumulated_stats_.mmaped 133 - accumulated_stats_.munmaped 134 + accumulated_stats_.really_freed 135 + accumulated_stats_.really_freed_redzones; 136 uptr total_used = accumulated_stats_.malloced 137 + accumulated_stats_.malloced_redzones; 138 // Return sane value if total_free < total_used due to racy 139 // way we update accumulated stats. 140 return (total_free > total_used) ? total_free - total_used : 1; 141} 142 143// Return several stats counters with a single call to 144// UpdateAccumulatedStatsUnlocked(). 145void AsanThreadRegistry::FillMallocStatistics(AsanMallocStats *malloc_stats) { 146 BlockingMutexLock lock(&mu_); 147 UpdateAccumulatedStatsUnlocked(); 148 malloc_stats->blocks_in_use = accumulated_stats_.mallocs; 149 malloc_stats->size_in_use = accumulated_stats_.malloced; 150 malloc_stats->max_size_in_use = max_malloced_memory_; 151 malloc_stats->size_allocated = accumulated_stats_.mmaped; 152} 153 154AsanThreadSummary *AsanThreadRegistry::FindByTid(u32 tid) { 155 CHECK(tid < n_threads_); 156 CHECK(thread_summaries_[tid]); 157 return thread_summaries_[tid]; 158} 159 160AsanThread *AsanThreadRegistry::FindThreadByStackAddress(uptr addr) { 161 BlockingMutexLock lock(&mu_); 162 for (u32 tid = 0; tid < n_threads_; tid++) { 163 AsanThread *t = thread_summaries_[tid]->thread(); 164 if (!t || !(t->fake_stack().StackSize())) continue; 165 if (t->fake_stack().AddrIsInFakeStack(addr) || t->AddrIsInStack(addr)) { 166 return t; 167 } 168 } 169 return 0; 170} 171 172void AsanThreadRegistry::UpdateAccumulatedStatsUnlocked() { 173 for (u32 tid = 0; tid < n_threads_; tid++) { 174 AsanThread *t = thread_summaries_[tid]->thread(); 175 if (t != 0) { 176 FlushToAccumulatedStatsUnlocked(&t->stats()); 177 } 178 } 179 // This is not very accurate: we may miss allocation peaks that happen 180 // between two updates of accumulated_stats_. For more accurate bookkeeping 181 // the maximum should be updated on every malloc(), which is unacceptable. 182 if (max_malloced_memory_ < accumulated_stats_.malloced) { 183 max_malloced_memory_ = accumulated_stats_.malloced; 184 } 185} 186 187void AsanThreadRegistry::FlushToAccumulatedStatsUnlocked(AsanStats *stats) { 188 // AsanStats consists of variables of type uptr only. 189 uptr *dst = (uptr*)&accumulated_stats_; 190 uptr *src = (uptr*)stats; 191 uptr num_fields = sizeof(AsanStats) / sizeof(uptr); 192 for (uptr i = 0; i < num_fields; i++) { 193 dst[i] += src[i]; 194 src[i] = 0; 195 } 196} 197 198} // namespace __asan 199