Bug Summary

File:projects/compiler-rt/lib/xray/xray_profile_collector.cc
Warning:line 156, column 27
Access to field 'Callees' results in a dereference of a null pointer (loaded from variable 'Node')

Annotated Source Code

Press '?' to see keyboard shortcuts

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//===-- xray_profile_collector.cc ------------------------------*- C++ -*-===//
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 XRay, a dynamic runtime instrumentation system.
11//
12// This implements the interface for the profileCollectorService.
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
25namespace __xray {
26namespace profileCollectorService {
27
28namespace {
29
30SpinMutex GlobalMutex;
31struct ThreadTrie {
32 tid_t TId;
33 typename std::aligned_storage<sizeof(FunctionCallTrie)>::type TrieStorage;
34};
35
36struct ProfileBuffer {
37 void *Data;
38 size_t Size;
39};
40
41// Current version of the profile format.
42constexpr u64 XRayProfilingVersion = 0x20180424;
43
44// Identifier for XRay profiling files 'xrayprof' in hex.
45constexpr u64 XRayMagicBytes = 0x7872617970726f66;
46
47struct XRayProfilingFileHeader {
48 const u64 MagicBytes = XRayMagicBytes;
49 const u64 Version = XRayProfilingVersion;
50 u64 Timestamp = 0; // System time in nanoseconds.
51 u64 PID = 0; // Process ID.
52};
53
54struct BlockHeader {
55 u32 BlockSize;
56 u32 BlockNum;
57 u64 ThreadId;
58};
59
60using ThreadTriesArray = Array<ThreadTrie>;
61using ProfileBufferArray = Array<ProfileBuffer>;
62using ThreadTriesArrayAllocator = typename ThreadTriesArray::AllocatorType;
63using ProfileBufferArrayAllocator = typename ProfileBufferArray::AllocatorType;
64
65// These need to be global aligned storage to avoid dynamic initialization. We
66// need these to be aligned to allow us to placement new objects into the
67// storage, and have pointers to those objects be appropriately aligned.
68static typename std::aligned_storage<sizeof(FunctionCallTrie::Allocators)>::type
69 AllocatorStorage;
70static typename std::aligned_storage<sizeof(ThreadTriesArray)>::type
71 ThreadTriesStorage;
72static typename std::aligned_storage<sizeof(ProfileBufferArray)>::type
73 ProfileBuffersStorage;
74static typename std::aligned_storage<sizeof(ThreadTriesArrayAllocator)>::type
75 ThreadTriesArrayAllocatorStorage;
76static typename std::aligned_storage<sizeof(ProfileBufferArrayAllocator)>::type
77 ProfileBufferArrayAllocatorStorage;
78
79static ThreadTriesArray *ThreadTries = nullptr;
80static ThreadTriesArrayAllocator *ThreadTriesAllocator = nullptr;
81static ProfileBufferArray *ProfileBuffers = nullptr;
82static ProfileBufferArrayAllocator *ProfileBuffersAllocator = nullptr;
83static FunctionCallTrie::Allocators *GlobalAllocators = nullptr;
84
85} // namespace
86
87void 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// A PathArray represents the function id's representing a stack trace. In this
108// context a path is almost always represented from the leaf function in a call
109// stack to a root of the call trie.
110using PathArray = Array<int32_t>;
111
112struct ProfileRecord {
113 using PathAllocator = typename PathArray::AllocatorType;
114
115 // The Path in this record is the function id's from the leaf to the root of
116 // the function call stack as represented from a FunctionCallTrie.
117 PathArray Path;
118 const FunctionCallTrie::Node *Node = nullptr;
119
120 // Constructor for in-place construction.
121 ProfileRecord(PathAllocator &A,
122 const FunctionCallTrie::Node *N) XRAY_NEVER_INSTRUMENT
123 : Path(A),
124 Node(N) {}
125};
126
127namespace {
128
129using ProfileRecordArray = Array<ProfileRecord>;
130
131// Walk a depth-first traversal of each root of the FunctionCallTrie to generate
132// the path(s) and the data associated with the path.
133static void
134populateRecords(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
14
Taking false branch
147 return;
148 DCHECK_NE(Record, nullptr);
149
150 // Traverse the Node's parents and as we're doing so, get the FIds in
151 // the order they appear.
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
162static 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 // List of IDs follow:
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 // Add the sentinel here.
176 constexpr int32_t SentinelFId = 0;
177 NextPtr = static_cast<uint8_t *>(
178 internal_memset(NextPtr, SentinelFId, sizeof(SentinelFId))) +
179 sizeof(SentinelFId);
180
181 // Add the node data here.
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} // namespace
196
197void 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
4
Taking false branch
201 ProfileBuffers == nullptr)
3
Assuming the condition is false
202 return;
203
204 // Clear out the global ProfileBuffers, if it's not empty.
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
6
Taking false branch
210 return;
211
212 // Then repopulate the global ProfileBuffers.
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 // First, we want to compute the amount of space we're going to need. We'll
222 // use a local allocator and an __xray::Array<...> to store the intermediary
223 // data, then compute the size as we're going along. Then we'll allocate the
224 // contiguous space to contain the thread buffer data.
225 const auto &Trie =
226 *reinterpret_cast<const FunctionCallTrie *>(&(ThreadTrie.TrieStorage));
227 if (Trie.getRoots().empty())
7
Assuming the condition is false
8
Taking false branch
228 continue;
229
230 populateRecords(ProfileRecords, PathAlloc, Trie);
9
Calling 'populateRecords'
231 DCHECK(!Trie.getRoots().empty());
232 DCHECK(!ProfileRecords.empty());
233
234 // Go through each record, to compute the sizes.
235 //
236 // header size = block size (4 bytes)
237 // + block number (4 bytes)
238 // + thread id (8 bytes)
239 // record size = path ids (4 bytes * number of ids + sentinel 4 bytes)
240 // + call count (8 bytes)
241 // + local time (8 bytes)
242 // + end of record (8 bytes)
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
256void reset() XRAY_NEVER_INSTRUMENT {
257 SpinMutexLock Lock(&GlobalMutex);
258
259 if (ProfileBuffers != nullptr) {
260 // Clear out the profile buffers that have been serialized.
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 // Clear out the function call tries per thread.
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 // Reset the global allocators.
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
306XRayBuffer 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 // The first buffer should always contain the file header information.
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} // namespace profileCollectorService
340} // namespace __xray