1 //===-- asan_thread_registry.cc -------------------------------------------===//
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
6 //===----------------------------------------------------------------------===//
8 // This file is a part of AddressSanitizer, an address sanity checker.
10 // AsanThreadRegistry-related code. AsanThreadRegistry is a container
11 // for summaries of all created threads.
12 //===----------------------------------------------------------------------===//
14 #include "asan_stack.h"
15 #include "asan_thread.h"
16 #include "asan_thread_registry.h"
17 #include "sanitizer_common/sanitizer_common.h"
21 static AsanThreadRegistry
asan_thread_registry(LINKER_INITIALIZED
);
23 AsanThreadRegistry
&asanThreadRegistry() {
24 return asan_thread_registry
;
27 AsanThreadRegistry::AsanThreadRegistry(LinkerInitialized x
)
29 main_thread_summary_(x
),
30 accumulated_stats_(x
),
31 max_malloced_memory_(x
),
34 void AsanThreadRegistry::Init() {
35 AsanTSDInit(AsanThreadSummary::TSDDtor
);
36 main_thread_
.set_summary(&main_thread_summary_
);
37 main_thread_summary_
.set_thread(&main_thread_
);
38 RegisterThread(&main_thread_
);
39 SetCurrent(&main_thread_
);
40 // At this point only one thread exists.
44 void AsanThreadRegistry::RegisterThread(AsanThread
*thread
) {
45 ScopedLock
lock(&mu_
);
48 CHECK(n_threads_
< kMaxNumberOfThreads
);
50 AsanThreadSummary
*summary
= thread
->summary();
52 summary
->set_tid(tid
);
53 thread_summaries_
[tid
] = summary
;
56 void AsanThreadRegistry::UnregisterThread(AsanThread
*thread
) {
57 ScopedLock
lock(&mu_
);
58 FlushToAccumulatedStatsUnlocked(&thread
->stats());
59 AsanThreadSummary
*summary
= thread
->summary();
61 summary
->set_thread(0);
64 AsanThread
*AsanThreadRegistry::GetMain() {
68 AsanThread
*AsanThreadRegistry::GetCurrent() {
69 AsanThreadSummary
*summary
= (AsanThreadSummary
*)AsanTSDGet();
72 // On Android, libc constructor is called _after_ asan_init, and cleans up
73 // TSD. Try to figure out if this is still the main thread by the stack
74 // address. We are not entirely sure that we have correct main thread
75 // limits, so only do this magic on Android, and only if the found thread is
77 AsanThread
* thread
= FindThreadByStackAddress((uptr
)&summary
);
78 if (thread
&& thread
->tid() == 0) {
85 return summary
->thread();
88 void AsanThreadRegistry::SetCurrent(AsanThread
*t
) {
90 if (flags()->verbosity
>= 2) {
91 Report("SetCurrent: %p for thread %p\n",
92 t
->summary(), (void*)GetThreadSelf());
94 // Make sure we do not reset the current AsanThread.
95 CHECK(AsanTSDGet() == 0);
96 AsanTSDSet(t
->summary());
97 CHECK(AsanTSDGet() == t
->summary());
100 AsanStats
&AsanThreadRegistry::GetCurrentThreadStats() {
101 AsanThread
*t
= GetCurrent();
102 return (t
) ? t
->stats() : main_thread_
.stats();
105 void AsanThreadRegistry::GetAccumulatedStats(AsanStats
*stats
) {
106 ScopedLock
lock(&mu_
);
107 UpdateAccumulatedStatsUnlocked();
108 internal_memcpy(stats
, &accumulated_stats_
, sizeof(accumulated_stats_
));
111 uptr
AsanThreadRegistry::GetCurrentAllocatedBytes() {
112 ScopedLock
lock(&mu_
);
113 UpdateAccumulatedStatsUnlocked();
114 uptr malloced
= accumulated_stats_
.malloced
;
115 uptr freed
= accumulated_stats_
.freed
;
116 // Return sane value if malloced < freed due to racy
117 // way we update accumulated stats.
118 return (malloced
> freed
) ? malloced
- freed
: 1;
121 uptr
AsanThreadRegistry::GetHeapSize() {
122 ScopedLock
lock(&mu_
);
123 UpdateAccumulatedStatsUnlocked();
124 return accumulated_stats_
.mmaped
;
127 uptr
AsanThreadRegistry::GetFreeBytes() {
128 ScopedLock
lock(&mu_
);
129 UpdateAccumulatedStatsUnlocked();
130 uptr total_free
= accumulated_stats_
.mmaped
131 + accumulated_stats_
.really_freed
132 + accumulated_stats_
.really_freed_redzones
;
133 uptr total_used
= accumulated_stats_
.malloced
134 + accumulated_stats_
.malloced_redzones
;
135 // Return sane value if total_free < total_used due to racy
136 // way we update accumulated stats.
137 return (total_free
> total_used
) ? total_free
- total_used
: 1;
140 // Return several stats counters with a single call to
141 // UpdateAccumulatedStatsUnlocked().
142 void AsanThreadRegistry::FillMallocStatistics(AsanMallocStats
*malloc_stats
) {
143 ScopedLock
lock(&mu_
);
144 UpdateAccumulatedStatsUnlocked();
145 malloc_stats
->blocks_in_use
= accumulated_stats_
.mallocs
;
146 malloc_stats
->size_in_use
= accumulated_stats_
.malloced
;
147 malloc_stats
->max_size_in_use
= max_malloced_memory_
;
148 malloc_stats
->size_allocated
= accumulated_stats_
.mmaped
;
151 AsanThreadSummary
*AsanThreadRegistry::FindByTid(u32 tid
) {
152 CHECK(tid
< n_threads_
);
153 CHECK(thread_summaries_
[tid
]);
154 return thread_summaries_
[tid
];
157 AsanThread
*AsanThreadRegistry::FindThreadByStackAddress(uptr addr
) {
158 ScopedLock
lock(&mu_
);
159 for (u32 tid
= 0; tid
< n_threads_
; tid
++) {
160 AsanThread
*t
= thread_summaries_
[tid
]->thread();
161 if (!t
|| !(t
->fake_stack().StackSize())) continue;
162 if (t
->fake_stack().AddrIsInFakeStack(addr
) || t
->AddrIsInStack(addr
)) {
169 void AsanThreadRegistry::UpdateAccumulatedStatsUnlocked() {
170 for (u32 tid
= 0; tid
< n_threads_
; tid
++) {
171 AsanThread
*t
= thread_summaries_
[tid
]->thread();
173 FlushToAccumulatedStatsUnlocked(&t
->stats());
176 // This is not very accurate: we may miss allocation peaks that happen
177 // between two updates of accumulated_stats_. For more accurate bookkeeping
178 // the maximum should be updated on every malloc(), which is unacceptable.
179 if (max_malloced_memory_
< accumulated_stats_
.malloced
) {
180 max_malloced_memory_
= accumulated_stats_
.malloced
;
184 void AsanThreadRegistry::FlushToAccumulatedStatsUnlocked(AsanStats
*stats
) {
185 // AsanStats consists of variables of type uptr only.
186 uptr
*dst
= (uptr
*)&accumulated_stats_
;
187 uptr
*src
= (uptr
*)stats
;
188 uptr num_fields
= sizeof(AsanStats
) / sizeof(uptr
);
189 for (uptr i
= 0; i
< num_fields
; i
++) {
195 } // namespace __asan