clang -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name xray_profile_collector.cc -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -mthread-model posix -fmath-errno -masm-verbose -mconstructor-aliases -munwind-tables -fuse-init-array -target-cpu x86-64 -dwarf-column-info -debugger-tuning=gdb -momit-leaf-frame-pointer -ffunction-sections -fdata-sections -resource-dir /usr/lib/llvm-9/lib/clang/9.0.0 -D XRAY_HAS_EXCEPTIONS=1 -D _DEBUG -D _GNU_SOURCE -D __STDC_CONSTANT_MACROS -D __STDC_FORMAT_MACROS -D __STDC_LIMIT_MACROS -I /build/llvm-toolchain-snapshot-9~svn362543/build-llvm/projects/compiler-rt/lib/xray -I /build/llvm-toolchain-snapshot-9~svn362543/projects/compiler-rt/lib/xray -I /build/llvm-toolchain-snapshot-9~svn362543/build-llvm/include -I /build/llvm-toolchain-snapshot-9~svn362543/include -I /build/llvm-toolchain-snapshot-9~svn362543/projects/compiler-rt/lib/xray/.. -I /build/llvm-toolchain-snapshot-9~svn362543/projects/compiler-rt/lib/xray/../../include -U NDEBUG -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/x86_64-linux-gnu/c++/6.3.0 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/x86_64-linux-gnu/c++/6.3.0 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/6.3.0/../../../../include/c++/6.3.0/backward -internal-isystem /usr/include/clang/9.0.0/include/ -internal-isystem /usr/local/include -internal-isystem /usr/lib/llvm-9/lib/clang/9.0.0/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O3 -Wno-unused-parameter -Wwrite-strings -Wno-missing-field-initializers -Wno-long-long -Wno-maybe-uninitialized -Wno-comment -Wno-unused-parameter -Wno-variadic-macros -Wno-non-virtual-dtor -std=c++11 -fdeprecated-macro -fdebug-compilation-dir /build/llvm-toolchain-snapshot-9~svn362543/build-llvm/projects/compiler-rt/lib/xray -fdebug-prefix-map=/build/llvm-toolchain-snapshot-9~svn362543=. -ferror-limit 19 -fmessage-length 0 -fvisibility hidden -fvisibility-inlines-hidden -fno-builtin -fno-rtti -fobjc-runtime=gcc -fdiagnostics-show-option -vectorize-loops -vectorize-slp -analyzer-output=html -analyzer-config stable-report-filename=true -o /tmp/scan-build-2019-06-05-060531-1271-1 -x c++ /build/llvm-toolchain-snapshot-9~svn362543/projects/compiler-rt/lib/xray/xray_profile_collector.cc -faddrsig
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | #include "xray_profile_collector.h" |
15 | #include "sanitizer_common/sanitizer_common.h" |
16 | #include "xray_allocator.h" |
17 | #include "xray_defs.h" |
18 | #include "xray_profiling_flags.h" |
19 | #include "xray_segmented_array.h" |
20 | #include <memory> |
21 | #include <pthread.h> |
22 | #include <utility> |
23 | |
24 | namespace __xray { |
25 | namespace profileCollectorService { |
26 | |
27 | namespace { |
28 | |
29 | SpinMutex GlobalMutex; |
30 | struct ThreadTrie { |
31 | tid_t TId; |
32 | typename std::aligned_storage<sizeof(FunctionCallTrie)>::type TrieStorage; |
33 | }; |
34 | |
35 | struct ProfileBuffer { |
36 | void *Data; |
37 | size_t Size; |
38 | }; |
39 | |
40 | |
41 | constexpr u64 XRayProfilingVersion = 0x20180424; |
42 | |
43 | |
44 | constexpr u64 XRayMagicBytes = 0x7872617970726f66; |
45 | |
46 | struct XRayProfilingFileHeader { |
47 | const u64 MagicBytes = XRayMagicBytes; |
48 | const u64 Version = XRayProfilingVersion; |
49 | u64 Timestamp = 0; |
50 | u64 PID = 0; |
51 | }; |
52 | |
53 | struct BlockHeader { |
54 | u32 BlockSize; |
55 | u32 BlockNum; |
56 | u64 ThreadId; |
57 | }; |
58 | |
59 | struct ThreadData { |
60 | BufferQueue *BQ; |
61 | FunctionCallTrie::Allocators::Buffers Buffers; |
62 | FunctionCallTrie::Allocators Allocators; |
63 | FunctionCallTrie FCT; |
64 | tid_t TId; |
65 | }; |
66 | |
67 | using ThreadDataArray = Array<ThreadData>; |
68 | using ThreadDataAllocator = ThreadDataArray::AllocatorType; |
69 | |
70 | |
71 | |
72 | |
73 | |
74 | static typename std::aligned_storage< |
75 | sizeof(BufferQueue), alignof(BufferQueue)>::type BufferQueueStorage; |
76 | static BufferQueue *BQ = nullptr; |
77 | static BufferQueue::Buffer Buffer; |
78 | static typename std::aligned_storage<sizeof(ThreadDataAllocator), |
79 | alignof(ThreadDataAllocator)>::type |
80 | ThreadDataAllocatorStorage; |
81 | static typename std::aligned_storage<sizeof(ThreadDataArray), |
82 | alignof(ThreadDataArray)>::type |
83 | ThreadDataArrayStorage; |
84 | |
85 | static ThreadDataAllocator *TDAllocator = nullptr; |
86 | static ThreadDataArray *TDArray = nullptr; |
87 | |
88 | using ProfileBufferArray = Array<ProfileBuffer>; |
89 | using ProfileBufferArrayAllocator = typename ProfileBufferArray::AllocatorType; |
90 | |
91 | |
92 | |
93 | |
94 | static typename std::aligned_storage<sizeof(ProfileBufferArray)>::type |
95 | ProfileBuffersStorage; |
96 | static typename std::aligned_storage<sizeof(ProfileBufferArrayAllocator)>::type |
97 | ProfileBufferArrayAllocatorStorage; |
98 | |
99 | static ProfileBufferArrayAllocator *ProfileBuffersAllocator = nullptr; |
100 | static ProfileBufferArray *ProfileBuffers = nullptr; |
101 | |
102 | |
103 | |
104 | static atomic_uint8_t CollectorInitialized{0}; |
105 | |
106 | } |
107 | |
108 | void post(BufferQueue *Q, FunctionCallTrie &&T, |
109 | FunctionCallTrie::Allocators &&A, |
110 | FunctionCallTrie::Allocators::Buffers &&B, |
111 | tid_t TId) XRAY_NEVER_INSTRUMENT { |
112 | DCHECK_NE(Q, nullptr); |
113 | |
114 | |
115 | if (!atomic_load(&CollectorInitialized, memory_order_acquire)) { |
116 | T.~FunctionCallTrie(); |
117 | A.~Allocators(); |
118 | Q->releaseBuffer(B.NodeBuffer); |
119 | Q->releaseBuffer(B.RootsBuffer); |
120 | Q->releaseBuffer(B.ShadowStackBuffer); |
121 | Q->releaseBuffer(B.NodeIdPairBuffer); |
122 | B.~Buffers(); |
123 | return; |
124 | } |
125 | |
126 | { |
127 | SpinMutexLock Lock(&GlobalMutex); |
128 | DCHECK_NE(TDAllocator, nullptr); |
129 | DCHECK_NE(TDArray, nullptr); |
130 | |
131 | if (TDArray->AppendEmplace(Q, std::move(B), std::move(A), std::move(T), |
132 | TId) == nullptr) { |
133 | |
134 | |
135 | T.~FunctionCallTrie(); |
136 | A.~Allocators(); |
137 | Q->releaseBuffer(B.NodeBuffer); |
138 | Q->releaseBuffer(B.RootsBuffer); |
139 | Q->releaseBuffer(B.ShadowStackBuffer); |
140 | Q->releaseBuffer(B.NodeIdPairBuffer); |
141 | B.~Buffers(); |
142 | } |
143 | } |
144 | } |
145 | |
146 | |
147 | |
148 | |
149 | using PathArray = Array<int32_t>; |
150 | |
151 | struct ProfileRecord { |
152 | using PathAllocator = typename PathArray::AllocatorType; |
153 | |
154 | |
155 | |
156 | PathArray Path; |
157 | const FunctionCallTrie::Node *Node; |
158 | }; |
159 | |
160 | namespace { |
161 | |
162 | using ProfileRecordArray = Array<ProfileRecord>; |
163 | |
164 | |
165 | |
166 | static void |
167 | populateRecords(ProfileRecordArray &PRs, ProfileRecord::PathAllocator &PA, |
168 | const FunctionCallTrie &Trie) XRAY_NEVER_INSTRUMENT { |
169 | using StackArray = Array<const FunctionCallTrie::Node *>; |
170 | using StackAllocator = typename StackArray::AllocatorType; |
171 | StackAllocator StackAlloc(profilingFlags()->stack_allocator_max); |
172 | StackArray DFSStack(StackAlloc); |
173 | for (const auto *R : Trie.getRoots()) { |
174 | DFSStack.Append(R); |
175 | while (!DFSStack.empty()) { |
| 12 | | Assuming the condition is true | |
|
| 13 | | Loop condition is true. Entering loop body | |
|
176 | auto *Node = DFSStack.back(); |
177 | DFSStack.trim(1); |
178 | if (Node == nullptr) |
| 14 | | Assuming the condition is false | |
|
| |
179 | continue; |
180 | auto Record = PRs.AppendEmplace(PathArray{PA}, Node); |
| 16 | | Value assigned to 'Node' | |
|
181 | if (Record == nullptr) |
| 17 | | Assuming the condition is false | |
|
| |
182 | return; |
183 | DCHECK_NE(Record, nullptr); |
184 | |
185 | |
186 | |
187 | for (auto N = Node; N != nullptr; N = N->Parent) |
| 19 | | Assuming pointer value is null | |
|
| 20 | | Loop condition is false. Execution continues on line 191 | |
|
188 | Record->Path.Append(N->FId); |
189 | DCHECK(!Record->Path.empty()); |
190 | |
191 | for (const auto C : Node->Callees) |
| 21 | | Access to field 'Callees' results in a dereference of a null pointer (loaded from variable 'Node') |
|
192 | DFSStack.Append(C.NodePtr); |
193 | } |
194 | } |
195 | } |
196 | |
197 | static void serializeRecords(ProfileBuffer *Buffer, const BlockHeader &Header, |
198 | const ProfileRecordArray &ProfileRecords) |
199 | XRAY_NEVER_INSTRUMENT { |
200 | auto NextPtr = static_cast<uint8_t *>( |
201 | internal_memcpy(Buffer->Data, &Header, sizeof(Header))) + |
202 | sizeof(Header); |
203 | for (const auto &Record : ProfileRecords) { |
204 | |
205 | for (const auto FId : Record.Path) |
206 | NextPtr = |
207 | static_cast<uint8_t *>(internal_memcpy(NextPtr, &FId, sizeof(FId))) + |
208 | sizeof(FId); |
209 | |
210 | |
211 | constexpr int32_t SentinelFId = 0; |
212 | NextPtr = static_cast<uint8_t *>( |
213 | internal_memset(NextPtr, SentinelFId, sizeof(SentinelFId))) + |
214 | sizeof(SentinelFId); |
215 | |
216 | |
217 | NextPtr = |
218 | static_cast<uint8_t *>(internal_memcpy( |
219 | NextPtr, &Record.Node->CallCount, sizeof(Record.Node->CallCount))) + |
220 | sizeof(Record.Node->CallCount); |
221 | NextPtr = static_cast<uint8_t *>( |
222 | internal_memcpy(NextPtr, &Record.Node->CumulativeLocalTime, |
223 | sizeof(Record.Node->CumulativeLocalTime))) + |
224 | sizeof(Record.Node->CumulativeLocalTime); |
225 | } |
226 | |
227 | DCHECK_EQ(NextPtr - static_cast<uint8_t *>(Buffer->Data), Buffer->Size); |
228 | } |
229 | |
230 | } |
231 | |
232 | void serialize() XRAY_NEVER_INSTRUMENT { |
233 | if (!atomic_load(&CollectorInitialized, memory_order_acquire)) |
| 1 | Assuming the condition is false | |
|
| |
234 | return; |
235 | |
236 | SpinMutexLock Lock(&GlobalMutex); |
237 | |
238 | |
239 | for (auto &B : *ProfileBuffers) |
240 | deallocateBuffer(reinterpret_cast<unsigned char *>(B.Data), B.Size); |
241 | ProfileBuffers->trim(ProfileBuffers->size()); |
242 | |
243 | DCHECK_NE(TDArray, nullptr); |
244 | if (TDArray->empty()) |
| 3 | | Assuming the condition is false | |
|
| |
245 | return; |
246 | |
247 | |
248 | u32 I = 0; |
249 | auto MaxSize = profilingFlags()->global_allocator_max; |
250 | auto ProfileArena = allocateBuffer(MaxSize); |
251 | if (ProfileArena == nullptr) |
| 5 | | Assuming the condition is false | |
|
| |
252 | return; |
253 | |
254 | auto ProfileArenaCleanup = at_scope_exit( |
255 | [&]() XRAY_NEVER_INSTRUMENT { deallocateBuffer(ProfileArena, MaxSize); }); |
256 | |
257 | auto PathArena = allocateBuffer(profilingFlags()->global_allocator_max); |
258 | if (PathArena == nullptr) |
| 7 | | Assuming the condition is false | |
|
| |
259 | return; |
260 | |
261 | auto PathArenaCleanup = at_scope_exit( |
262 | [&]() XRAY_NEVER_INSTRUMENT { deallocateBuffer(PathArena, MaxSize); }); |
263 | |
264 | for (const auto &ThreadTrie : *TDArray) { |
265 | using ProfileRecordAllocator = typename ProfileRecordArray::AllocatorType; |
266 | ProfileRecordAllocator PRAlloc(ProfileArena, |
267 | profilingFlags()->global_allocator_max); |
268 | ProfileRecord::PathAllocator PathAlloc( |
269 | PathArena, profilingFlags()->global_allocator_max); |
270 | ProfileRecordArray ProfileRecords(PRAlloc); |
271 | |
272 | |
273 | |
274 | |
275 | |
276 | if (ThreadTrie.FCT.getRoots().empty()) |
| 9 | | Assuming the condition is false | |
|
| |
277 | continue; |
278 | |
279 | populateRecords(ProfileRecords, PathAlloc, ThreadTrie.FCT); |
| 11 | | Calling 'populateRecords' | |
|
280 | DCHECK(!ThreadTrie.FCT.getRoots().empty()); |
281 | DCHECK(!ProfileRecords.empty()); |
282 | |
283 | |
284 | |
285 | |
286 | |
287 | |
288 | |
289 | |
290 | |
291 | |
292 | u32 CumulativeSizes = 0; |
293 | for (const auto &Record : ProfileRecords) |
294 | CumulativeSizes += 20 + (4 * Record.Path.size()); |
295 | |
296 | BlockHeader Header{16 + CumulativeSizes, I++, ThreadTrie.TId}; |
297 | auto B = ProfileBuffers->Append({}); |
298 | B->Size = sizeof(Header) + CumulativeSizes; |
299 | B->Data = allocateBuffer(B->Size); |
300 | DCHECK_NE(B->Data, nullptr); |
301 | serializeRecords(B, Header, ProfileRecords); |
302 | } |
303 | } |
304 | |
305 | void reset() XRAY_NEVER_INSTRUMENT { |
306 | atomic_store(&CollectorInitialized, 0, memory_order_release); |
307 | SpinMutexLock Lock(&GlobalMutex); |
308 | |
309 | if (ProfileBuffers != nullptr) { |
310 | |
311 | for (auto &B : *ProfileBuffers) |
312 | deallocateBuffer(reinterpret_cast<uint8_t *>(B.Data), B.Size); |
313 | ProfileBuffers->trim(ProfileBuffers->size()); |
314 | ProfileBuffers = nullptr; |
315 | } |
316 | |
317 | if (TDArray != nullptr) { |
318 | |
319 | for (auto &TD : *TDArray) { |
320 | TD.BQ->releaseBuffer(TD.Buffers.NodeBuffer); |
321 | TD.BQ->releaseBuffer(TD.Buffers.RootsBuffer); |
322 | TD.BQ->releaseBuffer(TD.Buffers.ShadowStackBuffer); |
323 | TD.BQ->releaseBuffer(TD.Buffers.NodeIdPairBuffer); |
324 | } |
325 | |
326 | |
327 | |
328 | |
329 | TDArray = nullptr; |
330 | } |
331 | |
332 | if (TDAllocator != nullptr) { |
333 | TDAllocator->~Allocator(); |
334 | TDAllocator = nullptr; |
335 | } |
336 | |
337 | if (Buffer.Data != nullptr) { |
338 | BQ->releaseBuffer(Buffer); |
339 | } |
340 | |
341 | if (BQ == nullptr) { |
342 | bool Success = false; |
343 | new (&BufferQueueStorage) |
344 | BufferQueue(profilingFlags()->global_allocator_max, 1, Success); |
345 | if (!Success) |
346 | return; |
347 | BQ = reinterpret_cast<BufferQueue *>(&BufferQueueStorage); |
348 | } else { |
349 | BQ->finalize(); |
350 | |
351 | if (BQ->init(profilingFlags()->global_allocator_max, 1) != |
352 | BufferQueue::ErrorCode::Ok) |
353 | return; |
354 | } |
355 | |
356 | if (BQ->getBuffer(Buffer) != BufferQueue::ErrorCode::Ok) |
357 | return; |
358 | |
359 | new (&ProfileBufferArrayAllocatorStorage) |
360 | ProfileBufferArrayAllocator(profilingFlags()->global_allocator_max); |
361 | ProfileBuffersAllocator = reinterpret_cast<ProfileBufferArrayAllocator *>( |
362 | &ProfileBufferArrayAllocatorStorage); |
363 | |
364 | new (&ProfileBuffersStorage) ProfileBufferArray(*ProfileBuffersAllocator); |
365 | ProfileBuffers = |
366 | reinterpret_cast<ProfileBufferArray *>(&ProfileBuffersStorage); |
367 | |
368 | new (&ThreadDataAllocatorStorage) |
369 | ThreadDataAllocator(Buffer.Data, Buffer.Size); |
370 | TDAllocator = |
371 | reinterpret_cast<ThreadDataAllocator *>(&ThreadDataAllocatorStorage); |
372 | new (&ThreadDataArrayStorage) ThreadDataArray(*TDAllocator); |
373 | TDArray = reinterpret_cast<ThreadDataArray *>(&ThreadDataArrayStorage); |
374 | |
375 | atomic_store(&CollectorInitialized, 1, memory_order_release); |
376 | } |
377 | |
378 | XRayBuffer nextBuffer(XRayBuffer B) XRAY_NEVER_INSTRUMENT { |
379 | SpinMutexLock Lock(&GlobalMutex); |
380 | |
381 | if (ProfileBuffers == nullptr || ProfileBuffers->size() == 0) |
382 | return {nullptr, 0}; |
383 | |
384 | static pthread_once_t Once = PTHREAD_ONCE_INIT0; |
385 | static typename std::aligned_storage<sizeof(XRayProfilingFileHeader)>::type |
386 | FileHeaderStorage; |
387 | pthread_once( |
388 | &Once, +[]() XRAY_NEVER_INSTRUMENT { |
389 | new (&FileHeaderStorage) XRayProfilingFileHeader{}; |
390 | }); |
391 | |
392 | if (UNLIKELY(B.Data == nullptr)__builtin_expect(!!(B.Data == nullptr), 0)) { |
393 | |
394 | auto &FileHeader = |
395 | *reinterpret_cast<XRayProfilingFileHeader *>(&FileHeaderStorage); |
396 | FileHeader.Timestamp = NanoTime(); |
397 | FileHeader.PID = internal_getpid(); |
398 | return {&FileHeaderStorage, sizeof(XRayProfilingFileHeader)}; |
399 | } |
400 | |
401 | if (UNLIKELY(B.Data == &FileHeaderStorage)__builtin_expect(!!(B.Data == &FileHeaderStorage), 0)) |
402 | return {(*ProfileBuffers)[0].Data, (*ProfileBuffers)[0].Size}; |
403 | |
404 | BlockHeader Header; |
405 | internal_memcpy(&Header, B.Data, sizeof(BlockHeader)); |
406 | auto NextBlock = Header.BlockNum + 1; |
407 | if (NextBlock < ProfileBuffers->size()) |
408 | return {(*ProfileBuffers)[NextBlock].Data, |
409 | (*ProfileBuffers)[NextBlock].Size}; |
410 | return {nullptr, 0}; |
411 | } |
412 | |
413 | } |
414 | } |