blob: 6602561186ce0d795900d9d267252b3943ed7a94 [file] [log] [blame]
Alexey Samsonov603c4be2012-06-04 13:55:19 +00001//===-- tsan_platform_linux.cc --------------------------------------------===//
Kostya Serebryany7ac41482012-05-10 13:48:04 +00002//
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 ThreadSanitizer (TSan), a race detector.
11//
Stephen Hines6d186232014-11-26 17:56:19 -080012// Linux- and FreeBSD-specific code.
Kostya Serebryany7ac41482012-05-10 13:48:04 +000013//===----------------------------------------------------------------------===//
14
Evgeniy Stepanov24e13722013-03-19 14:33:38 +000015
16#include "sanitizer_common/sanitizer_platform.h"
Stephen Hines6a211c52014-07-21 00:49:56 -070017#if SANITIZER_LINUX || SANITIZER_FREEBSD
Dmitry Vyukov3f24d632012-07-16 13:25:47 +000018
Alexey Samsonov84902c72012-06-18 09:42:39 +000019#include "sanitizer_common/sanitizer_common.h"
Alexey Samsonov48aee682012-06-05 06:19:00 +000020#include "sanitizer_common/sanitizer_libc.h"
Pirama Arumuga Nainar259f7062015-05-06 11:49:53 -070021#include "sanitizer_common/sanitizer_posix.h"
Alexey Samsonov84902c72012-06-18 09:42:39 +000022#include "sanitizer_common/sanitizer_procmaps.h"
Dmitry Vyukov92b54792013-10-03 17:14:35 +000023#include "sanitizer_common/sanitizer_stoptheworld.h"
Stephen Hines6d186232014-11-26 17:56:19 -080024#include "sanitizer_common/sanitizer_stackdepot.h"
Kostya Serebryany7ac41482012-05-10 13:48:04 +000025#include "tsan_platform.h"
26#include "tsan_rtl.h"
27#include "tsan_flags.h"
28
Kostya Serebryany7ac41482012-05-10 13:48:04 +000029#include <fcntl.h>
30#include <pthread.h>
31#include <signal.h>
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <stdarg.h>
36#include <sys/mman.h>
Kostya Serebryany7ac41482012-05-10 13:48:04 +000037#include <sys/syscall.h>
Stephen Hines2d1fdb22014-05-28 23:58:16 -070038#include <sys/socket.h>
Kostya Serebryany7ac41482012-05-10 13:48:04 +000039#include <sys/time.h>
40#include <sys/types.h>
41#include <sys/resource.h>
42#include <sys/stat.h>
43#include <unistd.h>
44#include <errno.h>
45#include <sched.h>
46#include <dlfcn.h>
Stephen Hines6a211c52014-07-21 00:49:56 -070047#if SANITIZER_LINUX
Dmitry Vyukov03f22482013-02-07 15:27:45 +000048#define __need_res_state
49#include <resolv.h>
Stephen Hines6a211c52014-07-21 00:49:56 -070050#endif
Kostya Serebryany7ac41482012-05-10 13:48:04 +000051
Dmitry Vyukov17b0a3c2013-10-15 13:03:06 +000052#ifdef sa_handler
53# undef sa_handler
54#endif
55
56#ifdef sa_sigaction
57# undef sa_sigaction
58#endif
59
Stephen Hines6a211c52014-07-21 00:49:56 -070060#if SANITIZER_FREEBSD
61extern "C" void *__libc_stack_end;
62void *__libc_stack_end = 0;
63#endif
Kostya Serebryany7ac41482012-05-10 13:48:04 +000064
65namespace __tsan {
66
Stephen Hines6d186232014-11-26 17:56:19 -080067static uptr g_data_start;
68static uptr g_data_end;
69
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -080070#ifdef TSAN_RUNTIME_VMA
71// Runtime detected VMA size.
72uptr vmaSize;
73#endif
74
Stephen Hines6a211c52014-07-21 00:49:56 -070075enum {
76 MemTotal = 0,
77 MemShadow = 1,
78 MemMeta = 2,
79 MemFile = 3,
80 MemMmap = 4,
81 MemTrace = 5,
82 MemHeap = 6,
83 MemOther = 7,
84 MemCount = 8,
85};
86
Stephen Hines6d186232014-11-26 17:56:19 -080087void FillProfileCallback(uptr p, uptr rss, bool file,
Alexander Potapenko2e13ca82013-09-03 11:09:16 +000088 uptr *mem, uptr stats_size) {
Stephen Hines6a211c52014-07-21 00:49:56 -070089 mem[MemTotal] += rss;
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -080090 if (p >= ShadowBeg() && p < ShadowEnd())
Stephen Hines6a211c52014-07-21 00:49:56 -070091 mem[MemShadow] += rss;
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -080092 else if (p >= MetaShadowBeg() && p < MetaShadowEnd())
Stephen Hines6a211c52014-07-21 00:49:56 -070093 mem[MemMeta] += rss;
Stephen Hines86277eb2015-03-23 12:06:32 -070094#ifndef SANITIZER_GO
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -080095 else if (p >= HeapMemBeg() && p < HeapMemEnd())
Stephen Hines6a211c52014-07-21 00:49:56 -070096 mem[MemHeap] += rss;
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -080097 else if (p >= LoAppMemBeg() && p < LoAppMemEnd())
Stephen Hines6d186232014-11-26 17:56:19 -080098 mem[file ? MemFile : MemMmap] += rss;
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -080099 else if (p >= HiAppMemBeg() && p < HiAppMemEnd())
Stephen Hines6d186232014-11-26 17:56:19 -0800100 mem[file ? MemFile : MemMmap] += rss;
101#else
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -0800102 else if (p >= AppMemBeg() && p < AppMemEnd())
Stephen Hines6d186232014-11-26 17:56:19 -0800103 mem[file ? MemFile : MemMmap] += rss;
104#endif
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -0800105 else if (p >= TraceMemBeg() && p < TraceMemEnd())
Stephen Hines6d186232014-11-26 17:56:19 -0800106 mem[MemTrace] += rss;
Stephen Hines6a211c52014-07-21 00:49:56 -0700107 else
108 mem[MemOther] += rss;
Dmitry Vyukov5d72fc72013-03-18 13:55:33 +0000109}
110
Stephen Hines6a211c52014-07-21 00:49:56 -0700111void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive) {
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -0800112 uptr mem[MemCount];
113 internal_memset(mem, 0, sizeof(mem[0]) * MemCount);
Alexander Potapenko2e13ca82013-09-03 11:09:16 +0000114 __sanitizer::GetMemoryProfile(FillProfileCallback, mem, 7);
Stephen Hines6d186232014-11-26 17:56:19 -0800115 StackDepotStats *stacks = StackDepotGetStats();
Stephen Hines6a211c52014-07-21 00:49:56 -0700116 internal_snprintf(buf, buf_size,
117 "RSS %zd MB: shadow:%zd meta:%zd file:%zd mmap:%zd"
Stephen Hines6d186232014-11-26 17:56:19 -0800118 " trace:%zd heap:%zd other:%zd stacks=%zd[%zd] nthr=%zd/%zd\n",
Stephen Hines6a211c52014-07-21 00:49:56 -0700119 mem[MemTotal] >> 20, mem[MemShadow] >> 20, mem[MemMeta] >> 20,
120 mem[MemFile] >> 20, mem[MemMmap] >> 20, mem[MemTrace] >> 20,
121 mem[MemHeap] >> 20, mem[MemOther] >> 20,
Stephen Hines6d186232014-11-26 17:56:19 -0800122 stacks->allocated >> 20, stacks->n_uniq_ids,
Stephen Hines6a211c52014-07-21 00:49:56 -0700123 nlive, nthread);
Dmitry Vyukov26127732012-05-22 11:33:03 +0000124}
125
Stephen Hines6a211c52014-07-21 00:49:56 -0700126#if SANITIZER_LINUX
Dmitry Vyukov92b54792013-10-03 17:14:35 +0000127void FlushShadowMemoryCallback(
128 const SuspendedThreadsList &suspended_threads_list,
129 void *argument) {
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -0800130 FlushUnneededShadowMemory(ShadowBeg(), ShadowEnd() - ShadowBeg());
Dmitry Vyukovadfb6502012-05-22 18:07:45 +0000131}
Stephen Hines6a211c52014-07-21 00:49:56 -0700132#endif
Dmitry Vyukovadfb6502012-05-22 18:07:45 +0000133
Dmitry Vyukov92b54792013-10-03 17:14:35 +0000134void FlushShadowMemory() {
Stephen Hines6a211c52014-07-21 00:49:56 -0700135#if SANITIZER_LINUX
Dmitry Vyukov92b54792013-10-03 17:14:35 +0000136 StopTheWorld(FlushShadowMemoryCallback, 0);
Stephen Hines6a211c52014-07-21 00:49:56 -0700137#endif
Dmitry Vyukov92b54792013-10-03 17:14:35 +0000138}
139
Stephen Hines86277eb2015-03-23 12:06:32 -0700140#ifndef SANITIZER_GO
Dmitry Vyukov82dbc512013-03-20 13:21:50 +0000141// Mark shadow for .rodata sections with the special kShadowRodata marker.
142// Accesses to .rodata can't race, so this saves time, memory and trace space.
143static void MapRodata() {
144 // First create temp file.
145 const char *tmpdir = GetEnv("TMPDIR");
146 if (tmpdir == 0)
147 tmpdir = GetEnv("TEST_TMPDIR");
148#ifdef P_tmpdir
149 if (tmpdir == 0)
150 tmpdir = P_tmpdir;
151#endif
152 if (tmpdir == 0)
153 return;
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700154 char name[256];
155 internal_snprintf(name, sizeof(name), "%s/tsan.rodata.%d",
Peter Collingbourne0b694fc2013-05-17 16:56:53 +0000156 tmpdir, (int)internal_getpid());
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700157 uptr openrv = internal_open(name, O_RDWR | O_CREAT | O_EXCL, 0600);
Peter Collingbourne9578a3e2013-05-08 14:43:49 +0000158 if (internal_iserror(openrv))
Dmitry Vyukov82dbc512013-03-20 13:21:50 +0000159 return;
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700160 internal_unlink(name); // Unlink it now, so that we can reuse the buffer.
Peter Collingbourne9578a3e2013-05-08 14:43:49 +0000161 fd_t fd = openrv;
Dmitry Vyukov82dbc512013-03-20 13:21:50 +0000162 // Fill the file with kShadowRodata.
163 const uptr kMarkerSize = 512 * 1024 / sizeof(u64);
164 InternalScopedBuffer<u64> marker(kMarkerSize);
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700165 // volatile to prevent insertion of memset
166 for (volatile u64 *p = marker.data(); p < marker.data() + kMarkerSize; p++)
Dmitry Vyukov82dbc512013-03-20 13:21:50 +0000167 *p = kShadowRodata;
168 internal_write(fd, marker.data(), marker.size());
169 // Map the file into memory.
Stephen Hines86277eb2015-03-23 12:06:32 -0700170 uptr page = internal_mmap(0, GetPageSizeCached(), PROT_READ | PROT_WRITE,
Peter Collingbourne9578a3e2013-05-08 14:43:49 +0000171 MAP_PRIVATE | MAP_ANONYMOUS, fd, 0);
172 if (internal_iserror(page)) {
Dmitry Vyukov82dbc512013-03-20 13:21:50 +0000173 internal_close(fd);
Dmitry Vyukov82dbc512013-03-20 13:21:50 +0000174 return;
175 }
176 // Map the file into shadow of .rodata sections.
Alexander Potapenko9ae28832013-03-26 10:34:37 +0000177 MemoryMappingLayout proc_maps(/*cache_enabled*/true);
Dmitry Vyukov82dbc512013-03-20 13:21:50 +0000178 uptr start, end, offset, prot;
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700179 // Reusing the buffer 'name'.
Dmitry Vyukov82dbc512013-03-20 13:21:50 +0000180 while (proc_maps.Next(&start, &end, &offset, name, ARRAY_SIZE(name), &prot)) {
181 if (name[0] != 0 && name[0] != '['
182 && (prot & MemoryMappingLayout::kProtectionRead)
183 && (prot & MemoryMappingLayout::kProtectionExecute)
184 && !(prot & MemoryMappingLayout::kProtectionWrite)
185 && IsAppMem(start)) {
186 // Assume it's .rodata
187 char *shadow_start = (char*)MemToShadow(start);
188 char *shadow_end = (char*)MemToShadow(end);
189 for (char *p = shadow_start; p < shadow_end; p += marker.size()) {
190 internal_mmap(p, Min<uptr>(marker.size(), shadow_end - p),
191 PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, 0);
192 }
193 }
194 }
195 internal_close(fd);
Dmitry Vyukov82dbc512013-03-20 13:21:50 +0000196}
197
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -0800198void InitializeShadowMemoryPlatform() {
Dmitry Vyukov82dbc512013-03-20 13:21:50 +0000199 MapRodata();
Kostya Serebryany7ac41482012-05-10 13:48:04 +0000200}
Kostya Serebryany7ac41482012-05-10 13:48:04 +0000201
Dmitry Vyukov0c2feef2012-09-07 18:08:02 +0000202static void InitDataSeg() {
Alexander Potapenko9ae28832013-03-26 10:34:37 +0000203 MemoryMappingLayout proc_maps(true);
Dmitry Vyukov0c2feef2012-09-07 18:08:02 +0000204 uptr start, end, offset;
205 char name[128];
Stephen Hines6d186232014-11-26 17:56:19 -0800206#if SANITIZER_FREEBSD
207 // On FreeBSD BSS is usually the last block allocated within the
208 // low range and heap is the last block allocated within the range
209 // 0x800000000-0x8ffffffff.
210 while (proc_maps.Next(&start, &end, &offset, name, ARRAY_SIZE(name),
211 /*protection*/ 0)) {
212 DPrintf("%p-%p %p %s\n", start, end, offset, name);
213 if ((start & 0xffff00000000ULL) == 0 && (end & 0xffff00000000ULL) == 0 &&
214 name[0] == '\0') {
215 g_data_start = start;
216 g_data_end = end;
217 }
218 }
219#else
Dmitry Vyukov0c2feef2012-09-07 18:08:02 +0000220 bool prev_is_data = false;
Alexey Samsonov45717c92013-03-13 06:51:02 +0000221 while (proc_maps.Next(&start, &end, &offset, name, ARRAY_SIZE(name),
222 /*protection*/ 0)) {
Dmitry Vyukov0c2feef2012-09-07 18:08:02 +0000223 DPrintf("%p-%p %p %s\n", start, end, offset, name);
224 bool is_data = offset != 0 && name[0] != 0;
Evgeniy Stepanov97dac9e2012-09-27 13:20:40 +0000225 // BSS may get merged with [heap] in /proc/self/maps. This is not very
226 // reliable.
227 bool is_bss = offset == 0 &&
228 (name[0] == 0 || internal_strcmp(name, "[heap]") == 0) && prev_is_data;
Dmitry Vyukov0c2feef2012-09-07 18:08:02 +0000229 if (g_data_start == 0 && is_data)
230 g_data_start = start;
231 if (is_bss)
232 g_data_end = end;
233 prev_is_data = is_data;
234 }
Stephen Hines6d186232014-11-26 17:56:19 -0800235#endif
Dmitry Vyukov0c2feef2012-09-07 18:08:02 +0000236 DPrintf("guessed data_start=%p data_end=%p\n", g_data_start, g_data_end);
237 CHECK_LT(g_data_start, g_data_end);
238 CHECK_GE((uptr)&g_data_start, g_data_start);
239 CHECK_LT((uptr)&g_data_start, g_data_end);
240}
Dmitry Vyukovb78caa62012-07-05 16:18:28 +0000241
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -0800242#endif // #ifndef SANITIZER_GO
243
244void InitializePlatformEarly() {
245#ifdef TSAN_RUNTIME_VMA
246 vmaSize =
247 (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1);
248#if defined(__aarch64__)
249 if (vmaSize != 39 && vmaSize != 42) {
250 Printf("FATAL: ThreadSanitizer: unsupported VMA range\n");
251 Printf("FATAL: Found %d - Supported 39 and 42\n", vmaSize);
Stephen Hines6d186232014-11-26 17:56:19 -0800252 Die();
253 }
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -0800254#elif defined(__powerpc64__)
255 if (vmaSize != 44 && vmaSize != 46) {
256 Printf("FATAL: ThreadSanitizer: unsupported VMA range\n");
257 Printf("FATAL: Found %d - Supported 44 and 46\n", vmaSize);
258 Die();
259 }
260#endif
261#endif
Stephen Hines6d186232014-11-26 17:56:19 -0800262}
Kostya Serebryany7ac41482012-05-10 13:48:04 +0000263
Stephen Hines6d186232014-11-26 17:56:19 -0800264void InitializePlatform() {
265 DisableCoreDumperIfNecessary();
Kostya Serebryany7ac41482012-05-10 13:48:04 +0000266
Dmitry Vyukov4de58642012-12-21 10:45:01 +0000267 // Go maps shadow memory lazily and works fine with limited address space.
268 // Unlimited stack is not a problem as well, because the executable
269 // is not compiled with -pie.
270 if (kCppMode) {
271 bool reexec = false;
272 // TSan doesn't play well with unlimited stack size (as stack
273 // overlaps with shadow memory). If we detect unlimited stack size,
274 // we re-exec the program with limited stack size as a best effort.
Stephen Hines6d186232014-11-26 17:56:19 -0800275 if (StackSizeIsUnlimited()) {
Dmitry Vyukov4de58642012-12-21 10:45:01 +0000276 const uptr kMaxStackSize = 32 * 1024 * 1024;
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700277 VReport(1, "Program is run with unlimited stack size, which wouldn't "
278 "work with ThreadSanitizer.\n"
279 "Re-execing with stack size limited to %zd bytes.\n",
280 kMaxStackSize);
Dmitry Vyukov4de58642012-12-21 10:45:01 +0000281 SetStackSizeLimitInBytes(kMaxStackSize);
282 reexec = true;
283 }
Dmitry Vyukovd698edc2012-11-28 12:19:50 +0000284
Stephen Hines6d186232014-11-26 17:56:19 -0800285 if (!AddressSpaceIsUnlimited()) {
Dmitry Vyukov4dc30822012-12-21 10:47:48 +0000286 Report("WARNING: Program is run with limited virtual address space,"
287 " which wouldn't work with ThreadSanitizer.\n");
Dmitry Vyukov4de58642012-12-21 10:45:01 +0000288 Report("Re-execing with unlimited virtual address space.\n");
Stephen Hines6d186232014-11-26 17:56:19 -0800289 SetAddressSpaceUnlimited();
Dmitry Vyukov4de58642012-12-21 10:45:01 +0000290 reexec = true;
291 }
292 if (reexec)
293 ReExec();
294 }
Dmitry Vyukovd698edc2012-11-28 12:19:50 +0000295
Stephen Hines86277eb2015-03-23 12:06:32 -0700296#ifndef SANITIZER_GO
Stephen Hines6d186232014-11-26 17:56:19 -0800297 CheckAndProtect();
Evgeniy Stepanovb114ed82013-03-13 08:19:53 +0000298 InitTlsSize();
Dmitry Vyukov0c2feef2012-09-07 18:08:02 +0000299 InitDataSeg();
Dmitry Vyukovb78caa62012-07-05 16:18:28 +0000300#endif
Kostya Serebryany7ac41482012-05-10 13:48:04 +0000301}
302
Dmitry Vyukov0c2feef2012-09-07 18:08:02 +0000303bool IsGlobalVar(uptr addr) {
304 return g_data_start && addr >= g_data_start && addr < g_data_end;
305}
Dmitry Vyukovb78caa62012-07-05 16:18:28 +0000306
Stephen Hines86277eb2015-03-23 12:06:32 -0700307#ifndef SANITIZER_GO
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700308// Extract file descriptors passed to glibc internal __res_iclose function.
309// This is required to properly "close" the fds, because we do not see internal
310// closes within glibc. The code is a pure hack.
Dmitry Vyukov03f22482013-02-07 15:27:45 +0000311int ExtractResolvFDs(void *state, int *fds, int nfd) {
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -0800312#if SANITIZER_LINUX && !SANITIZER_ANDROID
Dmitry Vyukov03f22482013-02-07 15:27:45 +0000313 int cnt = 0;
314 __res_state *statp = (__res_state*)state;
315 for (int i = 0; i < MAXNS && cnt < nfd; i++) {
316 if (statp->_u._ext.nsaddrs[i] && statp->_u._ext.nssocks[i] != -1)
317 fds[cnt++] = statp->_u._ext.nssocks[i];
318 }
319 return cnt;
Stephen Hines6a211c52014-07-21 00:49:56 -0700320#else
321 return 0;
322#endif
Dmitry Vyukov03f22482013-02-07 15:27:45 +0000323}
Dmitry Vyukov03f22482013-02-07 15:27:45 +0000324
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700325// Extract file descriptors passed via UNIX domain sockets.
326// This is requried to properly handle "open" of these fds.
327// see 'man recvmsg' and 'man 3 cmsg'.
328int ExtractRecvmsgFDs(void *msgp, int *fds, int nfd) {
329 int res = 0;
330 msghdr *msg = (msghdr*)msgp;
331 struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg);
332 for (; cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) {
333 if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS)
334 continue;
335 int n = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(fds[0]);
336 for (int i = 0; i < n; i++) {
337 fds[res++] = ((int*)CMSG_DATA(cmsg))[i];
338 if (res == nfd)
339 return res;
340 }
341 }
342 return res;
343}
344
Pirama Arumuga Nainar259f7062015-05-06 11:49:53 -0700345// Note: this function runs with async signals enabled,
346// so it must not touch any tsan state.
Stephen Hines2d1fdb22014-05-28 23:58:16 -0700347int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m,
348 void *abstime), void *c, void *m, void *abstime,
349 void(*cleanup)(void *arg), void *arg) {
350 // pthread_cleanup_push/pop are hardcore macros mess.
351 // We can't intercept nor call them w/o including pthread.h.
352 int res;
353 pthread_cleanup_push(cleanup, arg);
354 res = fn(c, m, abstime);
355 pthread_cleanup_pop(0);
356 return res;
357}
358#endif
Dmitry Vyukov03f22482013-02-07 15:27:45 +0000359
Pirama Arumuga Nainar799172d2016-03-03 15:50:30 -0800360#ifndef SANITIZER_GO
361void ReplaceSystemMalloc() { }
362#endif
363
Kostya Serebryany7ac41482012-05-10 13:48:04 +0000364} // namespace __tsan
Dmitry Vyukov3f24d632012-07-16 13:25:47 +0000365
Stephen Hines6d186232014-11-26 17:56:19 -0800366#endif // SANITIZER_LINUX || SANITIZER_FREEBSD