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