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 -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-8/lib/clang/8.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-8~svn345461/build-llvm/projects/compiler-rt/lib/xray -I /build/llvm-toolchain-snapshot-8~svn345461/projects/compiler-rt/lib/xray -I /build/llvm-toolchain-snapshot-8~svn345461/build-llvm/include -I /build/llvm-toolchain-snapshot-8~svn345461/include -I /build/llvm-toolchain-snapshot-8~svn345461/projects/compiler-rt/lib/xray/.. -I /build/llvm-toolchain-snapshot-8~svn345461/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/8.0.0/include/ -internal-isystem /usr/local/include -internal-isystem /usr/lib/llvm-8/lib/clang/8.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-8~svn345461/build-llvm/projects/compiler-rt/lib/xray -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-2018-10-27-211344-32123-1 -x c++ /build/llvm-toolchain-snapshot-8~svn345461/projects/compiler-rt/lib/xray/xray_profile_collector.cc -faddrsig
1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | #include "xray_profile_collector.h" |
16 | #include "sanitizer_common/sanitizer_common.h" |
17 | #include "xray_allocator.h" |
18 | #include "xray_defs.h" |
19 | #include "xray_profiling_flags.h" |
20 | #include "xray_segmented_array.h" |
21 | #include <memory> |
22 | #include <pthread.h> |
23 | #include <utility> |
24 | |
25 | namespace __xray { |
26 | namespace profileCollectorService { |
27 | |
28 | namespace { |
29 | |
30 | SpinMutex GlobalMutex; |
31 | struct ThreadTrie { |
32 | tid_t TId; |
33 | typename std::aligned_storage<sizeof(FunctionCallTrie)>::type TrieStorage; |
34 | }; |
35 | |
36 | struct ProfileBuffer { |
37 | void *Data; |
38 | size_t Size; |
39 | }; |
40 | |
41 | |
42 | constexpr u64 XRayProfilingVersion = 0x20180424; |
43 | |
44 | |
45 | constexpr u64 XRayMagicBytes = 0x7872617970726f66; |
46 | |
47 | struct XRayProfilingFileHeader { |
48 | const u64 MagicBytes = XRayMagicBytes; |
49 | const u64 Version = XRayProfilingVersion; |
50 | u64 Timestamp = 0; |
51 | u64 PID = 0; |
52 | }; |
53 | |
54 | struct BlockHeader { |
55 | u32 BlockSize; |
56 | u32 BlockNum; |
57 | u64 ThreadId; |
58 | }; |
59 | |
60 | using ThreadTriesArray = Array<ThreadTrie>; |
61 | using ProfileBufferArray = Array<ProfileBuffer>; |
62 | using ThreadTriesArrayAllocator = typename ThreadTriesArray::AllocatorType; |
63 | using ProfileBufferArrayAllocator = typename ProfileBufferArray::AllocatorType; |
64 | |
65 | |
66 | |
67 | |
68 | static typename std::aligned_storage<sizeof(FunctionCallTrie::Allocators)>::type |
69 | AllocatorStorage; |
70 | static typename std::aligned_storage<sizeof(ThreadTriesArray)>::type |
71 | ThreadTriesStorage; |
72 | static typename std::aligned_storage<sizeof(ProfileBufferArray)>::type |
73 | ProfileBuffersStorage; |
74 | static typename std::aligned_storage<sizeof(ThreadTriesArrayAllocator)>::type |
75 | ThreadTriesArrayAllocatorStorage; |
76 | static typename std::aligned_storage<sizeof(ProfileBufferArrayAllocator)>::type |
77 | ProfileBufferArrayAllocatorStorage; |
78 | |
79 | static ThreadTriesArray *ThreadTries = nullptr; |
80 | static ThreadTriesArrayAllocator *ThreadTriesAllocator = nullptr; |
81 | static ProfileBufferArray *ProfileBuffers = nullptr; |
82 | static ProfileBufferArrayAllocator *ProfileBuffersAllocator = nullptr; |
83 | static FunctionCallTrie::Allocators *GlobalAllocators = nullptr; |
84 | |
85 | } |
86 | |
87 | void post(const FunctionCallTrie &T, tid_t TId) XRAY_NEVER_INSTRUMENT { |
88 | static pthread_once_t Once = PTHREAD_ONCE_INIT0; |
89 | pthread_once(&Once, +[] { reset(); }); |
90 | |
91 | ThreadTrie *Item = nullptr; |
92 | { |
93 | SpinMutexLock Lock(&GlobalMutex); |
94 | if (GlobalAllocators == nullptr || ThreadTries == nullptr) |
95 | return; |
96 | |
97 | Item = ThreadTries->Append({}); |
98 | Item->TId = TId; |
99 | auto Trie = reinterpret_cast<FunctionCallTrie *>(&Item->TrieStorage); |
100 | new (Trie) FunctionCallTrie(*GlobalAllocators); |
101 | } |
102 | |
103 | auto Trie = reinterpret_cast<FunctionCallTrie *>(&Item->TrieStorage); |
104 | T.deepCopyInto(*Trie); |
105 | } |
106 | |
107 | |
108 | |
109 | |
110 | using PathArray = Array<int32_t>; |
111 | |
112 | struct ProfileRecord { |
113 | using PathAllocator = typename PathArray::AllocatorType; |
114 | |
115 | |
116 | |
117 | PathArray Path; |
118 | const FunctionCallTrie::Node *Node = nullptr; |
119 | |
120 | |
121 | ProfileRecord(PathAllocator &A, |
122 | const FunctionCallTrie::Node *N) XRAY_NEVER_INSTRUMENT |
123 | : Path(A), |
124 | Node(N) {} |
125 | }; |
126 | |
127 | namespace { |
128 | |
129 | using ProfileRecordArray = Array<ProfileRecord>; |
130 | |
131 | |
132 | |
133 | static void |
134 | populateRecords(ProfileRecordArray &PRs, ProfileRecord::PathAllocator &PA, |
135 | const FunctionCallTrie &Trie) XRAY_NEVER_INSTRUMENT { |
136 | using StackArray = Array<const FunctionCallTrie::Node *>; |
137 | using StackAllocator = typename StackArray::AllocatorType; |
138 | StackAllocator StackAlloc(profilingFlags()->stack_allocator_max); |
139 | StackArray DFSStack(StackAlloc); |
140 | for (const auto R : Trie.getRoots()) { |
141 | DFSStack.Append(R); |
142 | while (!DFSStack.empty()) { |
| 10 | | Assuming the condition is true | |
|
| 11 | | Loop condition is true. Entering loop body | |
|
143 | auto Node = DFSStack.back(); |
144 | DFSStack.trim(1); |
145 | auto Record = PRs.AppendEmplace(PA, Node); |
| 12 | | Value assigned to 'Node' | |
|
146 | if (Record == nullptr) |
| 13 | | Assuming the condition is false | |
|
| |
147 | return; |
148 | DCHECK_NE(Record, nullptr); |
149 | |
150 | |
151 | |
152 | for (auto N = Node; N != nullptr; N = N->Parent) |
| 15 | | Assuming pointer value is null | |
|
| 16 | | Loop condition is false. Execution continues on line 156 | |
|
153 | Record->Path.Append(N->FId); |
154 | DCHECK(!Record->Path.empty()); |
155 | |
156 | for (const auto C : Node->Callees) |
| 17 | | Access to field 'Callees' results in a dereference of a null pointer (loaded from variable 'Node') |
|
157 | DFSStack.Append(C.NodePtr); |
158 | } |
159 | } |
160 | } |
161 | |
162 | static void serializeRecords(ProfileBuffer *Buffer, const BlockHeader &Header, |
163 | const ProfileRecordArray &ProfileRecords) |
164 | XRAY_NEVER_INSTRUMENT { |
165 | auto NextPtr = static_cast<uint8_t *>( |
166 | internal_memcpy(Buffer->Data, &Header, sizeof(Header))) + |
167 | sizeof(Header); |
168 | for (const auto &Record : ProfileRecords) { |
169 | |
170 | for (const auto FId : Record.Path) |
171 | NextPtr = |
172 | static_cast<uint8_t *>(internal_memcpy(NextPtr, &FId, sizeof(FId))) + |
173 | sizeof(FId); |
174 | |
175 | |
176 | constexpr int32_t SentinelFId = 0; |
177 | NextPtr = static_cast<uint8_t *>( |
178 | internal_memset(NextPtr, SentinelFId, sizeof(SentinelFId))) + |
179 | sizeof(SentinelFId); |
180 | |
181 | |
182 | NextPtr = |
183 | static_cast<uint8_t *>(internal_memcpy( |
184 | NextPtr, &Record.Node->CallCount, sizeof(Record.Node->CallCount))) + |
185 | sizeof(Record.Node->CallCount); |
186 | NextPtr = static_cast<uint8_t *>( |
187 | internal_memcpy(NextPtr, &Record.Node->CumulativeLocalTime, |
188 | sizeof(Record.Node->CumulativeLocalTime))) + |
189 | sizeof(Record.Node->CumulativeLocalTime); |
190 | } |
191 | |
192 | DCHECK_EQ(NextPtr - static_cast<uint8_t *>(Buffer->Data), Buffer->Size); |
193 | } |
194 | |
195 | } |
196 | |
197 | void serialize() XRAY_NEVER_INSTRUMENT { |
198 | SpinMutexLock Lock(&GlobalMutex); |
199 | |
200 | if (GlobalAllocators == nullptr || ThreadTries == nullptr || |
| 1 | Assuming the condition is false | |
|
| 2 | | Assuming the condition is false | |
|
| |
201 | ProfileBuffers == nullptr) |
| 3 | | Assuming the condition is false | |
|
202 | return; |
203 | |
204 | |
205 | for (auto &B : *ProfileBuffers) |
206 | deallocateBuffer(reinterpret_cast<uint8_t *>(B.Data), B.Size); |
207 | ProfileBuffers->trim(ProfileBuffers->size()); |
208 | |
209 | if (ThreadTries->empty()) |
| 5 | | Assuming the condition is false | |
|
| |
210 | return; |
211 | |
212 | |
213 | u32 I = 0; |
214 | for (const auto &ThreadTrie : *ThreadTries) { |
215 | using ProfileRecordAllocator = typename ProfileRecordArray::AllocatorType; |
216 | ProfileRecordAllocator PRAlloc(profilingFlags()->global_allocator_max); |
217 | ProfileRecord::PathAllocator PathAlloc( |
218 | profilingFlags()->global_allocator_max); |
219 | ProfileRecordArray ProfileRecords(PRAlloc); |
220 | |
221 | |
222 | |
223 | |
224 | |
225 | const auto &Trie = |
226 | *reinterpret_cast<const FunctionCallTrie *>(&(ThreadTrie.TrieStorage)); |
227 | if (Trie.getRoots().empty()) |
| 7 | | Assuming the condition is false | |
|
| |
228 | continue; |
229 | |
230 | populateRecords(ProfileRecords, PathAlloc, Trie); |
| 9 | | Calling 'populateRecords' | |
|
231 | DCHECK(!Trie.getRoots().empty()); |
232 | DCHECK(!ProfileRecords.empty()); |
233 | |
234 | |
235 | |
236 | |
237 | |
238 | |
239 | |
240 | |
241 | |
242 | |
243 | u32 CumulativeSizes = 0; |
244 | for (const auto &Record : ProfileRecords) |
245 | CumulativeSizes += 20 + (4 * Record.Path.size()); |
246 | |
247 | BlockHeader Header{16 + CumulativeSizes, I++, ThreadTrie.TId}; |
248 | auto Buffer = ProfileBuffers->Append({}); |
249 | Buffer->Size = sizeof(Header) + CumulativeSizes; |
250 | Buffer->Data = allocateBuffer(Buffer->Size); |
251 | DCHECK_NE(Buffer->Data, nullptr); |
252 | serializeRecords(Buffer, Header, ProfileRecords); |
253 | } |
254 | } |
255 | |
256 | void reset() XRAY_NEVER_INSTRUMENT { |
257 | SpinMutexLock Lock(&GlobalMutex); |
258 | |
259 | if (ProfileBuffers != nullptr) { |
260 | |
261 | for (auto &B : *ProfileBuffers) |
262 | deallocateBuffer(reinterpret_cast<uint8_t *>(B.Data), B.Size); |
263 | ProfileBuffers->trim(ProfileBuffers->size()); |
264 | } |
265 | |
266 | if (ThreadTries != nullptr) { |
267 | |
268 | for (auto &T : *ThreadTries) { |
269 | auto Trie = reinterpret_cast<FunctionCallTrie *>(&T.TrieStorage); |
270 | Trie->~FunctionCallTrie(); |
271 | } |
272 | ThreadTries->trim(ThreadTries->size()); |
273 | } |
274 | |
275 | |
276 | if (GlobalAllocators != nullptr) |
277 | GlobalAllocators->~Allocators(); |
278 | |
279 | GlobalAllocators = |
280 | reinterpret_cast<FunctionCallTrie::Allocators *>(&AllocatorStorage); |
281 | new (GlobalAllocators) FunctionCallTrie::Allocators(); |
282 | *GlobalAllocators = FunctionCallTrie::InitAllocators(); |
283 | |
284 | if (ThreadTriesAllocator != nullptr) |
285 | ThreadTriesAllocator->~ThreadTriesArrayAllocator(); |
286 | |
287 | ThreadTriesAllocator = reinterpret_cast<ThreadTriesArrayAllocator *>( |
288 | &ThreadTriesArrayAllocatorStorage); |
289 | new (ThreadTriesAllocator) |
290 | ThreadTriesArrayAllocator(profilingFlags()->global_allocator_max); |
291 | ThreadTries = reinterpret_cast<ThreadTriesArray *>(&ThreadTriesStorage); |
292 | new (ThreadTries) ThreadTriesArray(*ThreadTriesAllocator); |
293 | |
294 | if (ProfileBuffersAllocator != nullptr) |
295 | ProfileBuffersAllocator->~ProfileBufferArrayAllocator(); |
296 | |
297 | ProfileBuffersAllocator = reinterpret_cast<ProfileBufferArrayAllocator *>( |
298 | &ProfileBufferArrayAllocatorStorage); |
299 | new (ProfileBuffersAllocator) |
300 | ProfileBufferArrayAllocator(profilingFlags()->global_allocator_max); |
301 | ProfileBuffers = |
302 | reinterpret_cast<ProfileBufferArray *>(&ProfileBuffersStorage); |
303 | new (ProfileBuffers) ProfileBufferArray(*ProfileBuffersAllocator); |
304 | } |
305 | |
306 | XRayBuffer nextBuffer(XRayBuffer B) XRAY_NEVER_INSTRUMENT { |
307 | SpinMutexLock Lock(&GlobalMutex); |
308 | |
309 | if (ProfileBuffers == nullptr || ProfileBuffers->size() == 0) |
310 | return {nullptr, 0}; |
311 | |
312 | static pthread_once_t Once = PTHREAD_ONCE_INIT0; |
313 | static typename std::aligned_storage<sizeof(XRayProfilingFileHeader)>::type |
314 | FileHeaderStorage; |
315 | pthread_once(&Once, |
316 | +[] { new (&FileHeaderStorage) XRayProfilingFileHeader{}; }); |
317 | |
318 | if (UNLIKELY(B.Data == nullptr)__builtin_expect(!!(B.Data == nullptr), 0)) { |
319 | |
320 | auto &FileHeader = |
321 | *reinterpret_cast<XRayProfilingFileHeader *>(&FileHeaderStorage); |
322 | FileHeader.Timestamp = NanoTime(); |
323 | FileHeader.PID = internal_getpid(); |
324 | return {&FileHeaderStorage, sizeof(XRayProfilingFileHeader)}; |
325 | } |
326 | |
327 | if (UNLIKELY(B.Data == &FileHeaderStorage)__builtin_expect(!!(B.Data == &FileHeaderStorage), 0)) |
328 | return {(*ProfileBuffers)[0].Data, (*ProfileBuffers)[0].Size}; |
329 | |
330 | BlockHeader Header; |
331 | internal_memcpy(&Header, B.Data, sizeof(BlockHeader)); |
332 | auto NextBlock = Header.BlockNum + 1; |
333 | if (NextBlock < ProfileBuffers->size()) |
334 | return {(*ProfileBuffers)[NextBlock].Data, |
335 | (*ProfileBuffers)[NextBlock].Size}; |
336 | return {nullptr, 0}; |
337 | } |
338 | |
339 | } |
340 | } |