1 /*
2  * Platform.cpp
3  *
4  * This source file is part of the FoundationDB open source project
5  *
6  * Copyright 2013-2018 Apple Inc. and the FoundationDB project authors
7  *
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *     http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  */
20 
21 #ifdef _WIN32
22 // This has to come as the first include on Win32 for rand_s() to be found
23 #define _CRT_RAND_S
24 #include <stdlib.h>
25 #include <math.h> // For _set_FMA3_enable workaround in platformInit
26 #endif
27 
28 #include "flow/Platform.h"
29 #include "flow/Arena.h"
30 
31 #include "flow/Trace.h"
32 #include "flow/Error.h"
33 
34 #include "flow/Knobs.h"
35 
36 #include <iostream>
37 #include <fstream>
38 #include <sstream>
39 #include <cstring>
40 #include <algorithm>
41 
42 #include <sys/types.h>
43 #include <time.h>
44 #include <sys/stat.h>
45 #include <fcntl.h>
46 #include "flow/UnitTest.h"
47 #include "flow/FaultInjection.h"
48 
49 #ifdef _WIN32
50 #define NOMINMAX
51 #include <windows.h>
52 #include <winioctl.h>
53 #include <io.h>
54 #include <psapi.h>
55 #include <stdio.h>
56 #include <conio.h>
57 #include <direct.h>
58 #include <pdh.h>
59 #include <pdhmsg.h>
60 #pragma comment(lib, "pdh.lib")
61 
62 // for SHGetFolderPath
63 #include <ShlObj.h>
64 #pragma comment(lib, "Shell32.lib")
65 
66 #define CANONICAL_PATH_SEPARATOR '\\'
67 #define PATH_MAX MAX_PATH
68 #endif
69 
70 #ifdef __unixish__
71 #define CANONICAL_PATH_SEPARATOR '/'
72 
73 #include <dirent.h>
74 #include <sys/time.h>
75 #include <sys/mman.h>
76 #include <unistd.h>
77 #include <ftw.h>
78 #include <pwd.h>
79 #include <sched.h>
80 
81 /* Needed for disk capacity */
82 #include <sys/statvfs.h>
83 
84 /* getifaddrs */
85 #include <sys/socket.h>
86 #include <ifaddrs.h>
87 #include <arpa/inet.h>
88 
89 #include "flow/stacktrace.h"
90 
91 #ifdef __linux__
92 /* Needed for memory allocation */
93 #include <linux/mman.h>
94 /* Needed for processor affinity */
95 #include <sched.h>
96 /* Needed for getProcessorTime* and setpriority */
97 #include <sys/syscall.h>
98 /* Needed for setpriority */
99 #include <sys/resource.h>
100 /* Needed for crash handler */
101 #include <signal.h>
102 /* Needed for gnu_dev_{major,minor} */
103 #include <sys/sysmacros.h>
104 #endif
105 
106 #ifdef __APPLE__
107 #include <sys/uio.h>
108 #include <sys/syslimits.h>
109 #include <mach/mach.h>
110 #include <sys/param.h>
111 #include <sys/mount.h>
112 #include <sys/sysctl.h>
113 #include <netinet/in.h>
114 #include <net/if.h>
115 #include <net/if_dl.h>
116 #include <net/route.h>
117 
118 #include <CoreFoundation/CoreFoundation.h>
119 #include <IOKit/IOKitLib.h>
120 #include <IOKit/storage/IOBlockStorageDriver.h>
121 #include <IOKit/storage/IOMedia.h>
122 #include <IOKit/IOBSD.h>
123 #endif
124 
125 #endif
126 
removeWhitespace(const std::string & t)127 std::string removeWhitespace(const std::string &t)
128 {
129 	static const std::string ws(" \t\r");
130 	std::string str = t;
131 	size_t found = str.find_last_not_of(ws);
132 	if (found != std::string::npos)
133 		str.erase(found + 1);
134 	else
135 		str.clear();			// str is all whitespace
136 	found = str.find_first_not_of(ws);
137 	if (found != std::string::npos)
138 		str.erase(0, found);
139 	else
140 		str.clear();			// str is all whitespace
141 
142 	return str;
143 }
144 
145 #ifdef _WIN32
146 #define ALLOC_FAIL NULL
147 #elif defined(__unixish__)
148 #define ALLOC_FAIL MAP_FAILED
149 #else
150 #error What platform is this?
151 #endif
152 
153 using std::cout;
154 using std::endl;
155 
156 #if defined(_WIN32)
FiletimeAsInt64(FILETIME & t)157 __int64 FiletimeAsInt64 (FILETIME &t){
158 	return *(__int64*)&t;
159 }
160 #endif
161 
162 #ifdef _WIN32
handlePdhStatus(const PDH_STATUS & status,std::string message)163 bool handlePdhStatus(const PDH_STATUS& status, std::string message) {
164 	if (status != ERROR_SUCCESS) {
165 		TraceEvent(SevWarnAlways, message.c_str()).GetLastError().detail("Status", status);
166 		return false;
167 	}
168 	return true;
169 }
170 
setPdhString(int id,std::string & out)171 bool setPdhString(int id, std::string &out) {
172 	char buf[512];
173 	DWORD sz = 512;
174 	if (!handlePdhStatus(PdhLookupPerfNameByIndex(NULL, id, buf, &sz), "PdhLookupPerfByNameIndex"))
175 		return false;
176 	out = buf;
177 	return true;
178 }
179 #endif
180 
181 #ifdef __unixish__
getProcessorTimeGeneric(int who)182 static double getProcessorTimeGeneric(int who) {
183 	struct rusage r_usage;
184 
185 	if (getrusage(who, &r_usage)) {
186 		TraceEvent(SevError, "GetCPUTime").detail("Who", who).GetLastError();
187 		throw platform_error();
188 	}
189 
190 	return (r_usage.ru_utime.tv_sec + (r_usage.ru_utime.tv_usec / double(1e6)) +
191 			r_usage.ru_stime.tv_sec + (r_usage.ru_stime.tv_usec / double(1e6)));
192 }
193 #endif
194 
getProcessorTimeThread()195 double getProcessorTimeThread() {
196 	INJECT_FAULT( platform_error, "getProcessorTimeThread" );
197 #if defined(_WIN32)
198 	FILETIME ftCreate, ftExit, ftKernel, ftUser;
199 	if (!GetThreadTimes(GetCurrentThread(), &ftCreate, &ftExit, &ftKernel, &ftUser)) {
200 		TraceEvent(SevError, "GetThreadCPUTime").GetLastError();
201 		throw platform_error();
202 	}
203 	return FiletimeAsInt64(ftKernel) / double(1e7) + FiletimeAsInt64(ftUser) / double(1e7);
204 #elif defined(__linux__)
205 	return getProcessorTimeGeneric(RUSAGE_THREAD);
206 #elif defined(__APPLE__)
207 	/* No RUSAGE_THREAD so we use the lower level interface */
208 	struct thread_basic_info info;
209 	mach_msg_type_number_t info_count = THREAD_BASIC_INFO_COUNT;
210 	if (KERN_SUCCESS != thread_info(mach_thread_self(), THREAD_BASIC_INFO, (thread_info_t)&info, &info_count)) {
211 		TraceEvent(SevError, "GetThreadCPUTime").GetLastError();
212 		throw platform_error();
213 	}
214 	return (info.user_time.seconds + (info.user_time.microseconds / double(1e6)) +
215 			info.system_time.seconds + (info.system_time.microseconds / double(1e6)));
216 #else
217 	#warning getProcessorTimeThread unimplemented on this platform
218 	return 0.0;
219 #endif
220 }
221 
getProcessorTimeProcess()222 double getProcessorTimeProcess() {
223 	INJECT_FAULT( platform_error, "getProcessorTimeProcess" );
224 #if defined(_WIN32)
225 	FILETIME ftCreate, ftExit, ftKernel, ftUser;
226 	if (!GetProcessTimes(GetCurrentProcess(), &ftCreate, &ftExit, &ftKernel, &ftUser)) {
227 		TraceEvent(SevError, "GetProcessCPUTime").GetLastError();
228 		throw platform_error();
229 	}
230 	return FiletimeAsInt64(ftKernel) / double(1e7) + FiletimeAsInt64(ftUser) / double(1e7);
231 #elif defined(__unixish__)
232 	return getProcessorTimeGeneric(RUSAGE_SELF);
233 #else
234 	#warning getProcessorTimeProcess unimplemented on this platform
235 	return 0.0;
236 #endif
237 }
238 
getResidentMemoryUsage()239 uint64_t getResidentMemoryUsage() {
240 #if defined(__linux__)
241 	uint64_t rssize = 0;
242 
243 	std::ifstream stat_stream("/proc/self/statm", std::ifstream::in);
244 	std::string ignore;
245 
246 	if(!stat_stream.good()) {
247 		TraceEvent(SevError, "GetResidentMemoryUsage").GetLastError();
248 		throw platform_error();
249 	}
250 
251 	stat_stream >> ignore;
252 	stat_stream >> rssize;
253 
254 	rssize *= sysconf(_SC_PAGESIZE);
255 
256 	return rssize;
257 #elif defined(_WIN32)
258 	PROCESS_MEMORY_COUNTERS_EX pmc;
259 	if(!GetProcessMemoryInfo(GetCurrentProcess(), (PPROCESS_MEMORY_COUNTERS)&pmc, sizeof(pmc))) {
260 		TraceEvent(SevError, "GetResidentMemoryUsage").GetLastError();
261 		throw platform_error();
262 	}
263 	return pmc.WorkingSetSize;
264 #elif defined(__APPLE__)
265 	struct task_basic_info info;
266 	mach_msg_type_number_t info_count = TASK_BASIC_INFO_COUNT;
267 	if (KERN_SUCCESS != task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &info_count)) {
268 		TraceEvent(SevError, "GetResidentMemoryUsage").GetLastError();
269 		throw platform_error();
270 	}
271 	return info.resident_size;
272 #else
273 	#warning getMemoryUsage unimplemented on this platform
274 	return 0;
275 #endif
276 }
277 
getMemoryUsage()278 uint64_t getMemoryUsage() {
279 #if defined(__linux__)
280 	uint64_t vmsize = 0;
281 
282 	std::ifstream stat_stream("/proc/self/statm", std::ifstream::in);
283 
284 	if(!stat_stream.good()) {
285 		TraceEvent(SevError, "GetMemoryUsage").GetLastError();
286 		throw platform_error();
287 	}
288 
289 	stat_stream >> vmsize;
290 
291 	vmsize *= sysconf(_SC_PAGESIZE);
292 
293 	return vmsize;
294 #elif defined(_WIN32)
295 	PROCESS_MEMORY_COUNTERS_EX pmc;
296 	if(!GetProcessMemoryInfo(GetCurrentProcess(), (PPROCESS_MEMORY_COUNTERS)&pmc, sizeof(pmc))) {
297 		TraceEvent(SevError, "GetMemoryUsage").GetLastError();
298 		throw platform_error();
299 	}
300 	return pmc.PagefileUsage;
301 #elif defined(__APPLE__)
302 	struct task_basic_info info;
303 	mach_msg_type_number_t info_count = TASK_BASIC_INFO_COUNT;
304 	if (KERN_SUCCESS != task_info(mach_task_self(), TASK_BASIC_INFO, (task_info_t)&info, &info_count)) {
305 		TraceEvent(SevError, "GetMemoryUsage").GetLastError();
306 		throw platform_error();
307 	}
308 	return info.virtual_size;
309 #else
310 	#warning getMemoryUsage unimplemented on this platform
311 	return 0;
312 #endif
313 }
314 
315 #if defined(__linux__)
getMemoryInfo(std::map<StringRef,int64_t> & request,std::stringstream & memInfoStream)316 void getMemoryInfo(std::map<StringRef, int64_t>& request, std::stringstream& memInfoStream) {
317 	size_t count = request.size();
318 	if (count == 0)
319 		return;
320 
321 	while (count > 0 && !memInfoStream.eof()) {
322 		std::string key;
323 
324 		memInfoStream >> key;
325 		auto item = request.find(StringRef(key));
326 		if (item != request.end()){
327 			int64_t value;
328 			memInfoStream >> value;
329 			item->second = value;
330 			count--;
331 		}
332 		memInfoStream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
333 	}
334 }
335 
getLowWatermark(std::stringstream & zoneInfoStream)336 int64_t getLowWatermark(std::stringstream& zoneInfoStream) {
337 	int64_t lowWatermark = 0;
338 	while(!zoneInfoStream.eof()) {
339 		std::string key;
340 		zoneInfoStream >> key;
341 
342 		if(key == "low") {
343 			int64_t value;
344 			zoneInfoStream >> value;
345 			lowWatermark += value;
346 		}
347 
348 		zoneInfoStream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
349 	}
350 
351 	return lowWatermark;
352 }
353 #endif
354 
getMachineRAMInfo(MachineRAMInfo & memInfo)355 void getMachineRAMInfo(MachineRAMInfo& memInfo) {
356 #if defined(__linux__)
357 	std::ifstream zoneInfoFileStream("/proc/zoneinfo", std::ifstream::in);
358 	int64_t lowWatermark = 0;
359 	if(!zoneInfoFileStream.good()) {
360 		TraceEvent(SevWarnAlways, "GetMachineZoneInfo").GetLastError();
361 	}
362 	else {
363 		std::stringstream zoneInfoStream;
364 		zoneInfoStream << zoneInfoFileStream.rdbuf();
365 		lowWatermark = getLowWatermark(zoneInfoStream) * 4; // Convert from 4K pages to KB
366 	}
367 
368 	std::ifstream fileStream("/proc/meminfo", std::ifstream::in);
369 	if (!fileStream.good()) {
370 		TraceEvent(SevError, "GetMachineMemInfo").GetLastError();
371 		throw platform_error();
372 	}
373 
374 	std::map<StringRef, int64_t> request = {
375 		{ LiteralStringRef("MemTotal:"), 0 },
376 		{ LiteralStringRef("MemFree:"), 0 },
377 		{ LiteralStringRef("MemAvailable:"), -1 },
378 		{ LiteralStringRef("Active(file):"), 0 },
379 		{ LiteralStringRef("Inactive(file):"), 0 },
380 		{ LiteralStringRef("SwapTotal:"), 0 },
381 		{ LiteralStringRef("SwapFree:"), 0 },
382 		{ LiteralStringRef("SReclaimable:"), 0 },
383 	};
384 
385 	std::stringstream memInfoStream;
386 	memInfoStream << fileStream.rdbuf();
387 	getMemoryInfo( request, memInfoStream );
388 
389 	int64_t memFree = request[LiteralStringRef("MemFree:")];
390 	int64_t pageCache = request[LiteralStringRef("Active(file):")] + request[LiteralStringRef("Inactive(file):")];
391 	int64_t slabReclaimable = request[LiteralStringRef("SReclaimable:")];
392 	int64_t usedSwap = request[LiteralStringRef("SwapTotal:")] - request[LiteralStringRef("SwapFree:")];
393 
394 	memInfo.total = 1024 * request[LiteralStringRef("MemTotal:")];
395 	if(request[LiteralStringRef("MemAvailable:")] != -1) {
396 		memInfo.available = 1024 * (request[LiteralStringRef("MemAvailable:")] - usedSwap);
397 	}
398 	else {
399 		memInfo.available = 1024 * (std::max<int64_t>(0, (memFree-lowWatermark) + std::max(pageCache-lowWatermark, pageCache/2) + std::max(slabReclaimable-lowWatermark, slabReclaimable/2)) - usedSwap);
400 	}
401 
402 	memInfo.committed = memInfo.total - memInfo.available;
403 #elif defined(_WIN32)
404 	MEMORYSTATUSEX mem_status;
405 	mem_status.dwLength = sizeof(mem_status);
406 	if (!GlobalMemoryStatusEx(&mem_status)) {
407 		TraceEvent(SevError, "WindowsGetMemStatus").GetLastError();
408 		throw platform_error();
409 	}
410 
411 	PERFORMACE_INFORMATION perf;
412 	if (!GetPerformanceInfo(&perf, sizeof(perf))) {
413 		TraceEvent(SevError, "WindowsGetMemPerformanceInfo").GetLastError();
414 		throw platform_error();
415 	}
416 
417 	memInfo.total = mem_status.ullTotalPhys;
418 	memInfo.committed = perf.PageSize*perf.CommitTotal;
419 	memInfo.available = memInfo.total - memInfo.committed;
420 #elif defined(__APPLE__)
421 	vm_statistics_data_t vm_stat;
422 	vm_size_t pagesize;
423 	mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
424 	if (KERN_SUCCESS != host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&vm_stat, &host_size)) {
425 		TraceEvent(SevError, "GetMachineMemInfo").GetLastError();
426 		throw platform_error();
427 	}
428 	host_page_size(mach_host_self(), &pagesize);
429 
430 	memInfo.total = pagesize * (vm_stat.free_count + vm_stat.active_count + vm_stat.inactive_count + vm_stat.wire_count);
431 	memInfo.available = pagesize * vm_stat.free_count;
432 	memInfo.committed = memInfo.total - memInfo.available;
433 #else
434 	#warning getMachineRAMInfo unimplemented on this platform
435 #endif
436 }
437 
systemErrorCodeToError()438 Error systemErrorCodeToError() {
439 #if defined(_WIN32)
440 	if(GetLastError() == ERROR_IO_DEVICE) {
441 		return io_error();
442 	}
443 #elif defined(__unixish__)
444 	if(errno == EIO || errno == EROFS) {
445 		return io_error();
446 	}
447 #else
448 	#error Port me!
449 #endif
450 
451 	return platform_error();
452 }
453 
getDiskBytes(std::string const & directory,int64_t & free,int64_t & total)454 void getDiskBytes(std::string const& directory, int64_t& free, int64_t& total) {
455 	INJECT_FAULT( platform_error, "getDiskBytes" );
456 #if defined(__unixish__)
457 #ifdef __linux__
458 	struct statvfs buf;
459 	if (statvfs(directory.c_str(), &buf)) {
460 		Error e = systemErrorCodeToError();
461 		TraceEvent(SevError, "GetDiskBytesStatvfsError").detail("Directory", directory).GetLastError().error(e);
462 		throw e;
463 	}
464 
465 	uint64_t blockSize = buf.f_frsize;
466 #elif defined(__APPLE__)
467 	struct statfs buf;
468 	if (statfs(directory.c_str(), &buf)) {
469 		Error e = systemErrorCodeToError();
470 		TraceEvent(SevError, "GetDiskBytesStatfsError").detail("Directory", directory).GetLastError().error(e);
471 		throw e;
472 	}
473 
474 	uint64_t blockSize = buf.f_bsize;
475 #else
476 #error Unknown unix
477 #endif
478 
479 	free = std::min( (uint64_t) std::numeric_limits<int64_t>::max(), buf.f_bavail * blockSize );
480 	total = std::min( (uint64_t) std::numeric_limits<int64_t>::max(), buf.f_blocks * blockSize );
481 
482 #elif defined(_WIN32)
483 	std::string fullPath = abspath(directory);
484 	//TraceEvent("FullDiskPath").detail("Path", fullPath).detail("Disk", (char)toupper(fullPath[0]));
485 
486 	ULARGE_INTEGER freeSpace;
487 	ULARGE_INTEGER totalSpace;
488 	ULARGE_INTEGER totalFreeSpace;
489 	if( !GetDiskFreeSpaceEx( fullPath.c_str(), &freeSpace, &totalSpace, &totalFreeSpace ) ) {
490 		Error e = systemErrorCodeToError();
491 		TraceEvent(SevError, "DiskFreeError").detail("Path", fullPath).GetLastError().error(e);
492 		throw e;
493 	}
494 	total = std::min( (uint64_t) std::numeric_limits<int64_t>::max(), totalSpace.QuadPart );
495 	free = std::min( (uint64_t) std::numeric_limits<int64_t>::max(), freeSpace.QuadPart );
496 #else
497 	#warning getDiskBytes unimplemented on this platform
498 	free = 1LL<<50;
499 	total = 1LL<<50;
500 #endif
501 }
502 
503 #ifdef __unixish__
getInterfaceName(const IPAddress & _ip)504 const char* getInterfaceName(const IPAddress& _ip) {
505 	INJECT_FAULT( platform_error, "getInterfaceName" );
506 	static char iname[20];
507 
508 	struct ifaddrs* interfaces = NULL;
509 	const char* ifa_name = NULL;
510 
511 	if (getifaddrs(&interfaces)) {
512 		TraceEvent(SevWarnAlways, "GetInterfaceAddrs").GetLastError();
513 		throw platform_error();
514 	}
515 
516 	for (struct ifaddrs* iter = interfaces; iter; iter = iter->ifa_next) {
517 		if(!iter->ifa_addr)
518 			continue;
519 		if (iter->ifa_addr->sa_family == AF_INET && _ip.isV4()) {
520 			uint32_t ip = ntohl(((struct sockaddr_in*)iter->ifa_addr)->sin_addr.s_addr);
521 			if (ip == _ip.toV4()) {
522 				ifa_name = iter->ifa_name;
523 				break;
524 			}
525 		} else if (iter->ifa_addr->sa_family == AF_INET6 && _ip.isV6()) {
526 			struct sockaddr_in6* ifa_addr = (struct sockaddr_in6*)iter->ifa_addr;
527 			if (memcmp(_ip.toV6().data(), &ifa_addr->sin6_addr, 16) == 0) {
528 				ifa_name = iter->ifa_name;
529 				break;
530 			}
531 		}
532 	}
533 
534 	if (ifa_name) {
535 		strncpy(iname, ifa_name, 19);
536 		iname[19] = 0;
537 	}
538 
539 	freeifaddrs(interfaces);
540 
541 	if (ifa_name)
542 		return iname;
543 	else
544 		return NULL;
545 }
546 #endif
547 
548 #if defined(__linux__)
getNetworkTraffic(const IPAddress & ip,uint64_t & bytesSent,uint64_t & bytesReceived,uint64_t & outSegs,uint64_t & retransSegs)549 void getNetworkTraffic(const IPAddress& ip, uint64_t& bytesSent, uint64_t& bytesReceived, uint64_t& outSegs,
550                        uint64_t& retransSegs) {
551 	INJECT_FAULT( platform_error, "getNetworkTraffic" ); // Even though this function doesn't throw errors, the equivalents for other platforms do, and since all of our simulation testing is on Linux...
552 	const char* ifa_name = nullptr;
553 	try {
554 		ifa_name = getInterfaceName(ip);
555 	}
556 	catch(Error &e) {
557 		if(e.code() != error_code_platform_error) {
558 			throw;
559 		}
560 	}
561 
562 	if (!ifa_name)
563 		return;
564 
565 	std::ifstream dev_stream("/proc/net/dev", std::ifstream::in);
566 	dev_stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
567 	dev_stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
568 
569 	std::string iface;
570 	std::string ignore;
571 
572 	uint64_t bytesSentSum = 0;
573 	uint64_t bytesReceivedSum = 0;
574 
575 	while (dev_stream.good()) {
576 		dev_stream >> iface;
577 		if (dev_stream.eof()) break;
578 		if (!strncmp(iface.c_str(), ifa_name, strlen(ifa_name))) {
579 			uint64_t sent = 0, received = 0;
580 
581 			dev_stream >> received;
582 			for (int i = 0; i < 7; i++) dev_stream >> ignore;
583 			dev_stream >> sent;
584 
585 			bytesSentSum += sent;
586 			bytesReceivedSum += received;
587 
588 			dev_stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
589 		}
590 	}
591 
592 	if(bytesSentSum > 0) {
593 		bytesSent = bytesSentSum;
594 	}
595 	if(bytesReceivedSum > 0) {
596 		bytesReceived = bytesReceivedSum;
597 	}
598 
599 	std::ifstream snmp_stream("/proc/net/snmp", std::ifstream::in);
600 
601 	std::string label;
602 
603 	while (snmp_stream.good()) {
604 		snmp_stream >> label;
605 		snmp_stream.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
606 		if (label == "Tcp:")
607 			break;
608 	}
609 
610 	/* Ignore the first 11 columns of the Tcp line */
611 	for (int i = 0; i < 11; i++)
612 		snmp_stream >> ignore;
613 
614 	snmp_stream >> outSegs;
615 	snmp_stream >> retransSegs;
616 }
617 
getMachineLoad(uint64_t & idleTime,uint64_t & totalTime,bool logDetails)618 void getMachineLoad(uint64_t& idleTime, uint64_t& totalTime, bool logDetails) {
619 	INJECT_FAULT( platform_error, "getMachineLoad" ); // Even though this function doesn't throw errors, the equivalents for other platforms do, and since all of our simulation testing is on Linux...
620 	std::ifstream stat_stream("/proc/stat", std::ifstream::in);
621 
622 	std::string ignore;
623 	stat_stream >> ignore;
624 
625 	uint64_t t_user, t_nice, t_system, t_idle, t_iowait, t_irq, t_softirq, t_steal, t_guest;
626 	stat_stream >> t_user >> t_nice >> t_system >> t_idle >> t_iowait >> t_irq >> t_softirq >> t_steal >> t_guest;
627 
628 	totalTime = t_user+t_nice+t_system+t_idle+t_iowait+t_irq+t_softirq+t_steal+t_guest;
629 	idleTime = t_idle+t_iowait;
630 
631 	if( !DEBUG_DETERMINISM && logDetails )
632 		TraceEvent("MachineLoadDetail").detail("User", t_user).detail("Nice", t_nice).detail("System", t_system).detail("Idle", t_idle).detail("IOWait", t_iowait).detail("IRQ", t_irq).detail("SoftIRQ", t_softirq).detail("Steal", t_steal).detail("Guest", t_guest);
633 }
634 
getDiskStatistics(std::string const & directory,uint64_t & currentIOs,uint64_t & busyTicks,uint64_t & reads,uint64_t & writes,uint64_t & writeSectors,uint64_t & readSectors)635 void getDiskStatistics(std::string const& directory, uint64_t& currentIOs, uint64_t& busyTicks, uint64_t& reads, uint64_t& writes, uint64_t& writeSectors, uint64_t& readSectors) {
636 	INJECT_FAULT( platform_error, "getDiskStatistics" );
637 	currentIOs = 0;
638 
639 	struct stat buf;
640 	if (stat(directory.c_str(), &buf)) {
641 		TraceEvent(SevError, "GetDiskStatisticsStatError").detail("Directory", directory).GetLastError();
642 		throw platform_error();
643 	}
644 
645 	std::ifstream proc_stream("/proc/diskstats", std::ifstream::in);
646 	while (proc_stream.good()) {
647 		std::string line;
648 		getline(proc_stream, line);
649 		std::istringstream disk_stream(line, std::istringstream::in);
650 
651 		unsigned int majorId;
652 		unsigned int minorId;
653 		disk_stream >> majorId;
654 		disk_stream >> minorId;
655 		if(majorId == (unsigned int) gnu_dev_major(buf.st_dev) && minorId == (unsigned int) gnu_dev_minor(buf.st_dev)) {
656 			std::string ignore;
657 			uint64_t rd_ios;	/* # of reads completed */
658 			//	    This is the total number of reads completed successfully.
659 
660 			uint64_t rd_merges;	/* # of reads merged */
661 			//	    Reads and writes which are adjacent to each other may be merged for
662 			//	    efficiency.  Thus two 4K reads may become one 8K read before it is
663 			//	    ultimately handed to the disk, and so it will be counted (and queued)
664 			//	    as only one I/O.  This field lets you know how often this was done.
665 
666 			uint64_t rd_sectors; /*# of sectors read */
667 			//	    This is the total number of sectors read successfully.
668 
669 			uint64_t rd_ticks;	/* # of milliseconds spent reading */
670 			//	    This is the total number of milliseconds spent by all reads (as
671 			//	    measured from __make_request() to end_that_request_last()).
672 
673 			uint64_t wr_ios;	/* # of writes completed */
674 			//	    This is the total number of writes completed successfully.
675 
676 			uint64_t wr_merges;	/* # of writes merged */
677 			//	    Reads and writes which are adjacent to each other may be merged for
678 			//	    efficiency.  Thus two 4K reads may become one 8K read before it is
679 			//	    ultimately handed to the disk, and so it will be counted (and queued)
680 			//	    as only one I/O.  This field lets you know how often this was done.
681 
682 			uint64_t wr_sectors; /* # of sectors written */
683 			//	    This is the total number of sectors written successfully.
684 
685 			uint64_t wr_ticks;	/* # of milliseconds spent writing */
686 			//	    This is the total number of milliseconds spent by all writes (as
687 			//	    measured from __make_request() to end_that_request_last()).
688 
689 			uint64_t cur_ios;	/* # of I/Os currently in progress */
690 			//	    The only field that should go to zero. Incremented as requests are
691 			//	    given to appropriate struct request_queue and decremented as they finish.
692 
693 			uint64_t ticks;	/* # of milliseconds spent doing I/Os */
694 			//	    This field increases so long as field 9 is nonzero.
695 
696 			uint64_t aveq;	/* weighted # of milliseconds spent doing I/Os */
697 			//	    This field is incremented at each I/O start, I/O completion, I/O
698 			//	    merge, or read of these stats by the number of I/Os in progress
699 			//	    (field 9) times the number of milliseconds spent doing I/O since the
700 			//	    last update of this field.  This can provide an easy measure of both
701 			//	    I/O completion time and the backlog that may be accumulating.
702 
703 			disk_stream >> ignore;
704 			disk_stream >> rd_ios;
705 			disk_stream >> rd_merges;
706 			disk_stream >> rd_sectors;
707 			disk_stream >> rd_ticks;
708 			disk_stream >> wr_ios;
709 			disk_stream >> wr_merges;
710 			disk_stream >> wr_sectors;
711 			disk_stream >> wr_ticks;
712 			disk_stream >> cur_ios;
713 			disk_stream >> ticks;
714 			disk_stream >> aveq;
715 
716 			currentIOs = cur_ios;
717 			busyTicks = ticks;
718 			reads = rd_ios;
719 			writes = wr_ios;
720 			writeSectors = wr_sectors;
721 			readSectors = rd_sectors;
722 
723 			//TraceEvent("DiskMetricsRaw").detail("Input", line).detail("Ignore", ignore).detail("RdIos", rd_ios)
724 			//	.detail("RdMerges", rd_merges).detail("RdSectors", rd_sectors).detail("RdTicks", rd_ticks).detail("WrIos", wr_ios).detail("WrMerges", wr_merges)
725 			//	.detail("WrSectors", wr_sectors).detail("WrTicks", wr_ticks).detail("CurIos", cur_ios).detail("Ticks", ticks).detail("Aveq", aveq)
726 			//	.detail("CurrentIOs", currentIOs).detail("BusyTicks", busyTicks).detail("Reads", reads).detail("Writes", writes).detail("WriteSectors", writeSectors)
727 			//  .detail("ReadSectors", readSectors);
728 			return;
729 		} else
730 			disk_stream.ignore( std::numeric_limits<std::streamsize>::max(), '\n');
731 	}
732 
733 	if(!g_network->isSimulated()) TraceEvent(SevWarn, "GetDiskStatisticsDeviceNotFound").detail("Directory", directory);
734 }
735 
getDeviceId(std::string path)736 dev_t getDeviceId(std::string path) {
737 	struct stat statInfo;
738 
739 	while (true) {
740 		int returnValue = stat(path.c_str(), &statInfo);
741 		if (!returnValue) break;
742 
743 		if (errno == ENOENT) {
744 			path = parentDirectory(path);
745 		} else {
746 			TraceEvent(SevError, "GetDeviceIdError").detail("Path", path).GetLastError();
747 			throw platform_error();
748 		}
749 	}
750 
751 	return statInfo.st_dev;
752 }
753 
754 #endif
755 
756 #ifdef __APPLE__
getNetworkTraffic(const IPAddress & ip,uint64_t & bytesSent,uint64_t & bytesReceived,uint64_t & outSegs,uint64_t & retransSegs)757 void getNetworkTraffic(const IPAddress& ip, uint64_t& bytesSent, uint64_t& bytesReceived, uint64_t& outSegs,
758                        uint64_t& retransSegs) {
759 	INJECT_FAULT( platform_error, "getNetworkTraffic" );
760 
761 	const char* ifa_name = nullptr;
762 	try {
763 		ifa_name = getInterfaceName(ip);
764 	}
765 	catch(Error &e) {
766 		if(e.code() != error_code_platform_error) {
767 			throw;
768 		}
769 	}
770 
771 	if (!ifa_name)
772 		return;
773 
774 	int mib[] = {
775 		CTL_NET,
776 		PF_ROUTE,
777 		0,
778 		AF_INET,
779 		NET_RT_IFLIST2,
780 		0 /* If we could get an interface index instead of name, we would pass it here */
781 	};
782 
783 	size_t len;
784 
785 	if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
786 		TraceEvent(SevError, "GetNetworkTrafficError").GetLastError();
787 		throw platform_error();
788 	}
789 
790 	char *buf = (char*)malloc(len);
791 
792 	if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
793 		free(buf);
794 		TraceEvent(SevError, "GetNetworkTrafficReadInterfacesError").GetLastError();
795 		throw platform_error();
796 	}
797 
798 	char *lim = buf + len;
799 
800 	for (char *next = buf; next < lim; ) {
801 		struct if_msghdr* ifm = (struct if_msghdr*)next;
802 		next += ifm->ifm_msglen;
803 
804 		if ((ifm->ifm_type = RTM_IFINFO2)) {
805 			struct if_msghdr2* if2m = (struct if_msghdr2*)ifm;
806 			struct sockaddr_dl *sdl = (struct sockaddr_dl*)(if2m + 1);
807 
808 			if (sdl->sdl_nlen == strlen(ifa_name) && !strncmp(ifa_name, sdl->sdl_data, sdl->sdl_nlen)) {
809 				bytesSent = if2m->ifm_data.ifi_obytes;
810 				bytesReceived = if2m->ifm_data.ifi_ibytes;
811 				outSegs = if2m->ifm_data.ifi_opackets;
812 				retransSegs = 0;
813 				break;
814 			}
815 		}
816 	}
817 
818 	free(buf);
819 }
820 
getMachineLoad(uint64_t & idleTime,uint64_t & totalTime,bool logDetails)821 void getMachineLoad(uint64_t& idleTime, uint64_t& totalTime, bool logDetails) {
822 	INJECT_FAULT( platform_error, "getMachineLoad" );
823 	mach_msg_type_number_t count = HOST_CPU_LOAD_INFO_COUNT;
824 	host_cpu_load_info_data_t r_load;
825 
826 	if (host_statistics(mach_host_self(), HOST_CPU_LOAD_INFO, (host_info_t)&r_load, &count) != KERN_SUCCESS) {
827 		TraceEvent(SevError, "GetMachineLoad").GetLastError();
828 		throw platform_error();
829 	}
830 
831 	idleTime = r_load.cpu_ticks[CPU_STATE_IDLE];
832 	totalTime = r_load.cpu_ticks[CPU_STATE_IDLE] + r_load.cpu_ticks[CPU_STATE_USER] + r_load.cpu_ticks[CPU_STATE_NICE] + r_load.cpu_ticks[CPU_STATE_SYSTEM];
833 }
834 
getDiskStatistics(std::string const & directory,uint64_t & currentIOs,uint64_t & busyTicks,uint64_t & reads,uint64_t & writes,uint64_t & writeSectors,uint64_t & readSectors)835 void getDiskStatistics(std::string const& directory, uint64_t& currentIOs, uint64_t& busyTicks, uint64_t& reads, uint64_t& writes, uint64_t& writeSectors, uint64_t& readSectors) {
836 	INJECT_FAULT( platform_error, "getDiskStatistics" );
837 	currentIOs = 0;
838 	busyTicks = 0;
839 	writeSectors = 0;
840 	readSectors = 0;
841 
842 	const int kMaxDiskNameSize = 64;
843 
844 	struct statfs buf;
845 	if (statfs(directory.c_str(), &buf)) {
846 		Error e = systemErrorCodeToError();
847 		TraceEvent(SevError, "GetDiskStatisticsStatfsError").detail("Directory", directory).GetLastError().error(e);
848 		throw e;
849 	}
850 
851 	const char* dev = strrchr(buf.f_mntfromname, '/');
852 	if (!dev) {
853 		TraceEvent(SevError, "GetDiskStatisticsStrrchrError").detail("Directory", directory).GetLastError();
854 		throw platform_error();
855 	}
856 	dev++;
857 
858 	io_iterator_t disk_list;
859 
860 	// According to Apple docs, if this gets passed to IOServiceGetMatchingServices, we aren't responsible for the memory anymore,
861 	// the only case where it isn't passed is if it's null, in which case we also aren't responsible. So no need to call CFRelease
862 	// on this variable.
863 	CFMutableDictionaryRef match = IOBSDNameMatching(kIOMasterPortDefault, kNilOptions, dev);
864 
865 	if(!match) {
866 		TraceEvent(SevError, "IOBSDNameMatching");
867 		throw platform_error();
868 	}
869 
870 	if (IOServiceGetMatchingServices(kIOMasterPortDefault, match, &disk_list) != kIOReturnSuccess) {
871 		TraceEvent(SevError, "IOServiceGetMatchingServices");
872 		throw platform_error();
873 	}
874 
875 	io_registry_entry_t disk = IOIteratorNext(disk_list);
876 	if (!disk) {
877 		IOObjectRelease(disk_list);
878 		TraceEvent(SevError, "IOIteratorNext");
879 		throw platform_error();
880 	}
881 
882 	io_registry_entry_t tdisk = disk;
883 	while (!IOObjectConformsTo(disk, "IOBlockStorageDriver")) {
884 		IORegistryEntryGetParentEntry(disk, kIOServicePlane, &tdisk);
885 		IOObjectRelease(disk);
886 		disk = tdisk;
887 	}
888 
889 	CFDictionaryRef disk_dict = NULL;
890 	if (IORegistryEntryCreateCFProperties(disk, (CFMutableDictionaryRef*)&disk_dict, kCFAllocatorDefault, kNilOptions) != kIOReturnSuccess) {
891 		IOObjectRelease(disk);
892 		IOObjectRelease(disk_list);
893 		TraceEvent(SevError, "IORegistryEntryCreateCFProperties");
894 		throw platform_error();
895 	}
896 
897 	// Here and below, note that memory returned by CFDictionaryGetValue() is not owned by us, and should not be CFRelease()'d by us.
898 	CFDictionaryRef stats_dict = (CFDictionaryRef)CFDictionaryGetValue(disk_dict, CFSTR(kIOBlockStorageDriverStatisticsKey));
899 
900 	if (stats_dict == NULL) {
901 		CFRelease(disk_dict);
902 		IOObjectRelease(disk);
903 		IOObjectRelease(disk_list);
904 		TraceEvent(SevError, "CFDictionaryGetValue");
905 		throw platform_error();
906 	}
907 
908 	CFNumberRef number;
909 
910 	if ((number = (CFNumberRef)CFDictionaryGetValue(stats_dict, CFSTR(kIOBlockStorageDriverStatisticsReadsKey)))) {
911 		CFNumberGetValue(number, kCFNumberSInt64Type, &reads);
912 	}
913 
914 	if ((number = (CFNumberRef)CFDictionaryGetValue(stats_dict, CFSTR(kIOBlockStorageDriverStatisticsWritesKey)))) {
915 		CFNumberGetValue(number, kCFNumberSInt64Type, &writes);
916 	}
917 
918 	CFRelease(disk_dict);
919 	IOObjectRelease(disk);
920 	IOObjectRelease(disk_list);
921 }
922 #endif
923 
924 #if defined(_WIN32)
expandWildcardPath(const char * wildcardPath)925 std::vector<std::string> expandWildcardPath(const char *wildcardPath)
926 {
927 	PDH_STATUS Status;
928 	char *EndOfPaths;
929 	char *Paths = NULL;
930 	DWORD BufferSize = 0;
931 	std::vector<std::string> results;
932 
933 	Status = PdhExpandCounterPath(wildcardPath, Paths, &BufferSize);
934 	if (Status != PDH_MORE_DATA) {
935 		TraceEvent(SevWarn, "PdhExpandCounterPathError").detail("Reason", "Expand Path call made no sense").detail("Status", Status);
936 		goto Cleanup;
937 	}
938 
939 	Paths = (char *)malloc(BufferSize);
940 	Status = PdhExpandCounterPath(wildcardPath, Paths, &BufferSize);
941 
942 	if (Status != ERROR_SUCCESS) {
943 		TraceEvent(SevWarn, "PdhExpandCounterPathError").detail("Reason", "Expand Path call failed").detail("Status", Status);
944 		goto Cleanup;
945 	}
946 
947 	if (Paths == NULL) {
948 		TraceEvent("WindowsPdhExpandCounterPathError").detail("Reason", "Path could not be expanded");
949 		goto Cleanup;
950 	}
951 
952 	EndOfPaths = Paths + BufferSize;
953 
954 	for (char *p = Paths; ((p != EndOfPaths) && (*p != '\0')); p += strlen(p) + 1) {
955 		results.push_back( p );
956 		//printf("Counter: %s\n", p);
957 	}
958 
959 Cleanup:
960 	if (Paths)
961 	{
962 		free(Paths);
963 	}
964 	return results;
965 }
966 
addCounters(HQUERY Query,const char * path)967 std::vector<HCOUNTER> addCounters( HQUERY Query, const char *path ) {
968 	std::vector<HCOUNTER> counters;
969 
970 	std::vector<std::string> paths = expandWildcardPath( path );
971 
972 	for(int i = 0; i < paths.size(); i++) {
973 		HCOUNTER counter;
974 		handlePdhStatus( PdhAddCounter(Query, paths[i].c_str(), 0, &counter), "PdhAddCounter" );
975 		counters.push_back( counter );
976 	}
977 	return counters;
978 }
979 #endif
980 
981 struct SystemStatisticsState {
982 	double lastTime;
983 	double lastClockThread;
984 	double lastClockProcess;
985 	uint64_t processLastSent;
986 	uint64_t processLastReceived;
987 #if defined(_WIN32)
988 	struct {
989 		std::string diskDevice;
990 		std::string physicalDisk;
991 		std::string processor;
992 		std::string networkDevice;
993 		std::string tcpv4;
994 		std::string pctIdle;
995 		std::string diskQueueLength;
996 		std::string diskReadsPerSec;
997 		std::string diskWritesPerSec;
998 		std::string diskWriteBytesPerSec;
999 		std::string bytesSentPerSec;
1000 		std::string bytesRecvPerSec;
1001 		std::string segmentsOutPerSec;
1002 		std::string segmentsRetransPerSec;
1003 	} pdhStrings;
1004 	PDH_STATUS Status;
1005 	HQUERY Query;
1006 	HCOUNTER QueueLengthCounter;
1007 	HCOUNTER DiskTimeCounter;
1008 	HCOUNTER ReadsCounter;
1009 	HCOUNTER WritesCounter;
1010 	HCOUNTER WriteBytesCounter;
1011 	std::vector<HCOUNTER> SendCounters;
1012 	std::vector<HCOUNTER> ReceiveCounters;
1013 	HCOUNTER SegmentsOutCounter;
1014 	HCOUNTER SegmentsRetransCounter;
1015 	HCOUNTER ProcessorIdleCounter;
SystemStatisticsStateSystemStatisticsState1016 	SystemStatisticsState() : Query(NULL), QueueLengthCounter(NULL), DiskTimeCounter(NULL),
1017 		ReadsCounter(NULL), WritesCounter(NULL), WriteBytesCounter(NULL), ProcessorIdleCounter(NULL),
1018 #elif defined(__unixish__)
1019 	uint64_t machineLastSent, machineLastReceived;
1020 	uint64_t machineLastOutSegs, machineLastRetransSegs;
1021 	uint64_t lastBusyTicks, lastReads, lastWrites, lastWriteSectors, lastReadSectors;
1022 	uint64_t lastClockIdleTime, lastClockTotalTime;
1023 	SystemStatisticsState() : machineLastSent(0), machineLastReceived(0), machineLastOutSegs(0), machineLastRetransSegs(0),
1024 		lastBusyTicks(0), lastReads(0), lastWrites(0), lastWriteSectors(0), lastReadSectors(0), lastClockIdleTime(0), lastClockTotalTime(0),
1025 #else
1026 	#error Port me!
1027 #endif
1028 		lastTime(0), lastClockThread(0), lastClockProcess(0), processLastSent(0), processLastReceived(0) {}
1029 };
1030 
1031 #if defined(_WIN32)
initPdhStrings(SystemStatisticsState * state,std::string dataFolder)1032 void initPdhStrings(SystemStatisticsState *state, std::string dataFolder) {
1033 	if (setPdhString(234, state->pdhStrings.physicalDisk) &&
1034 		setPdhString(238, state->pdhStrings.processor) &&
1035 		setPdhString(510, state->pdhStrings.networkDevice) &&
1036 		setPdhString(638, state->pdhStrings.tcpv4) &&
1037 		setPdhString(1482, state->pdhStrings.pctIdle) &&
1038 		setPdhString(198, state->pdhStrings.diskQueueLength) &&
1039 		setPdhString(214, state->pdhStrings.diskReadsPerSec) &&
1040 		setPdhString(216, state->pdhStrings.diskWritesPerSec) &&
1041 		setPdhString(222, state->pdhStrings.diskWriteBytesPerSec) &&
1042 		setPdhString(506, state->pdhStrings.bytesSentPerSec) &&
1043 		setPdhString(264, state->pdhStrings.bytesRecvPerSec) &&
1044 		setPdhString(654, state->pdhStrings.segmentsOutPerSec) &&
1045 		setPdhString(656, state->pdhStrings.segmentsRetransPerSec)) {
1046 
1047 		if (!dataFolder.empty()) {
1048 			dataFolder = abspath(dataFolder);
1049 			char buf[512], buf2[512];
1050 			DWORD sz = 512, sz2 = 512;
1051 
1052 			if (!GetVolumePathName(dataFolder.c_str(), buf, 512)) {
1053 				TraceEvent(SevWarn, "GetVolumePathName").GetLastError().detail("Path", dataFolder);
1054 				return;
1055 			}
1056 
1057 			if (!GetVolumeNameForVolumeMountPoint(buf, buf2, 512)) {
1058 				TraceEvent(SevWarn, "GetVolumeNameForVolumeMountPoint").GetLastError().detail("Path", dataFolder);
1059 				return;
1060 			}
1061 
1062 			if (!strlen(buf2)) {
1063 				TraceEvent(SevWarn, "WinDiskStatsGetPathError").detail("Path", dataFolder);
1064 				return;
1065 			}
1066 
1067 			if (buf2[strlen(buf2) - 1] == '\\')
1068 				buf2[strlen(buf2) - 1] = 0;
1069 
1070 			HANDLE hDevice = CreateFile(buf2, 0, 0, NULL, OPEN_EXISTING, 0, NULL);
1071 			if (hDevice == INVALID_HANDLE_VALUE) {
1072 				TraceEvent(SevWarn, "CreateFile").GetLastError().detail("Path", dataFolder);
1073 				return;
1074 			}
1075 
1076 			STORAGE_DEVICE_NUMBER storage_device;
1077 			if (!DeviceIoControl(hDevice, IOCTL_STORAGE_GET_DEVICE_NUMBER, NULL, 0,
1078 								 &storage_device, sizeof(storage_device), &sz, NULL)) {
1079 				TraceEvent(SevWarn, "DeviceIoControl").GetLastError().detail("Path", dataFolder);
1080 				return;
1081 			}
1082 
1083 			// Find the drive letter involved!
1084 			sz = 512;
1085 			if (handlePdhStatus(PdhEnumObjectItems(NULL, NULL, state->pdhStrings.physicalDisk.c_str(),
1086 								buf2, &sz2, buf, &sz, PERF_DETAIL_NOVICE, 0), "PdhEnumObjectItems")) {
1087 				char *ptr = buf;
1088 				while (*ptr) {
1089 					if (isdigit(*ptr) && atoi(ptr) == storage_device.DeviceNumber) {
1090 						state->pdhStrings.diskDevice = ptr;
1091 						break;
1092 					}
1093 					ptr += strlen(ptr) + 1;
1094 				}
1095 			}
1096 
1097 			if (state->pdhStrings.diskDevice.empty()) {
1098 				TraceEvent(SevWarn, "WinDiskStatsGetPathError").detail("Path", dataFolder);
1099 				return;
1100 			}
1101 		}
1102 	}
1103 }
1104 #endif
1105 
getSystemStatistics(std::string dataFolder,const IPAddress * ip,SystemStatisticsState ** statState,bool logDetails)1106 SystemStatistics getSystemStatistics(std::string dataFolder, const IPAddress* ip, SystemStatisticsState** statState, bool logDetails) {
1107 	if( (*statState) == NULL )
1108 		(*statState) = new SystemStatisticsState();
1109 	SystemStatistics returnStats;
1110 
1111 	double nowTime = timer();
1112 	double nowClockProcess = getProcessorTimeProcess();
1113 	double nowClockThread = getProcessorTimeThread();
1114 	returnStats.elapsed = nowTime - (*statState)->lastTime;
1115 
1116 	returnStats.initialized = (*statState)->lastTime != 0;
1117 	if( returnStats.initialized ) {
1118 		returnStats.processCPUSeconds = (nowClockProcess - (*statState)->lastClockProcess);
1119 		returnStats.mainThreadCPUSeconds = (nowClockThread - (*statState)->lastClockThread);
1120 	}
1121 
1122 	returnStats.processMemory = getMemoryUsage();
1123 	returnStats.processResidentMemory = getResidentMemoryUsage();
1124 
1125 	MachineRAMInfo memInfo;
1126 	getMachineRAMInfo(memInfo);
1127 	returnStats.machineTotalRAM = memInfo.total;
1128 	returnStats.machineCommittedRAM = memInfo.committed;
1129 	returnStats.machineAvailableRAM = memInfo.available;
1130 
1131 	if(dataFolder != "") {
1132 		int64_t diskTotal, diskFree;
1133 		getDiskBytes(dataFolder, diskFree, diskTotal);
1134 		returnStats.processDiskTotalBytes = diskTotal;
1135 		returnStats.processDiskFreeBytes = diskFree;
1136 	}
1137 
1138 #if defined(_WIN32)
1139 	if((*statState)->Query == NULL) {
1140 		initPdhStrings(*statState, dataFolder);
1141 
1142 		TraceEvent("SetupQuery");
1143 		handlePdhStatus( PdhOpenQuery(NULL, NULL, &(*statState)->Query), "PdhOpenQuery" );
1144 
1145 		if( !(*statState)->pdhStrings.diskDevice.empty() ) {
1146 			handlePdhStatus(PdhAddCounter((*statState)->Query, ("\\" + (*statState)->pdhStrings.physicalDisk + "(" + (*statState)->pdhStrings.diskDevice + ")\\" + (*statState)->pdhStrings.pctIdle).c_str(), 0, &(*statState)->DiskTimeCounter), "PdhAddCounter");
1147 			handlePdhStatus(PdhAddCounter((*statState)->Query, ("\\" + (*statState)->pdhStrings.physicalDisk + "(" + (*statState)->pdhStrings.diskDevice + ")\\" + (*statState)->pdhStrings.diskQueueLength).c_str(), 0, &(*statState)->QueueLengthCounter), "PdhAddCounter");
1148 			handlePdhStatus(PdhAddCounter((*statState)->Query, ("\\" + (*statState)->pdhStrings.physicalDisk + "(" + (*statState)->pdhStrings.diskDevice + ")\\" + (*statState)->pdhStrings.diskReadsPerSec).c_str(), 0, &(*statState)->ReadsCounter), "PdhAddCounter");
1149 			handlePdhStatus(PdhAddCounter((*statState)->Query, ("\\" + (*statState)->pdhStrings.physicalDisk + "(" + (*statState)->pdhStrings.diskDevice + ")\\" + (*statState)->pdhStrings.diskWritesPerSec).c_str(), 0, &(*statState)->WritesCounter), "PdhAddCounter");
1150 			handlePdhStatus(PdhAddCounter((*statState)->Query, ("\\" + (*statState)->pdhStrings.physicalDisk + "(" + (*statState)->pdhStrings.diskDevice + ")\\" + (*statState)->pdhStrings.diskWriteBytesPerSec).c_str(), 0, &(*statState)->WriteBytesCounter), "PdhAddCounter");
1151 		}
1152 		(*statState)->SendCounters = addCounters((*statState)->Query, ("\\" + (*statState)->pdhStrings.networkDevice + "(*)\\" + (*statState)->pdhStrings.bytesSentPerSec).c_str());
1153 		(*statState)->ReceiveCounters = addCounters((*statState)->Query, ("\\" + (*statState)->pdhStrings.networkDevice + "(*)\\" + (*statState)->pdhStrings.bytesRecvPerSec).c_str());
1154 		handlePdhStatus(PdhAddCounter((*statState)->Query, ("\\" + (*statState)->pdhStrings.tcpv4 + "\\" + (*statState)->pdhStrings.segmentsOutPerSec).c_str(), 0, &(*statState)->SegmentsOutCounter), "PdhAddCounter");
1155 		handlePdhStatus(PdhAddCounter((*statState)->Query, ("\\" + (*statState)->pdhStrings.tcpv4 + "\\" + (*statState)->pdhStrings.segmentsRetransPerSec).c_str(), 0, &(*statState)->SegmentsRetransCounter), "PdhAddCounter");
1156 		handlePdhStatus(PdhAddCounter((*statState)->Query, ("\\" + (*statState)->pdhStrings.processor + "(*)\\" + (*statState)->pdhStrings.pctIdle).c_str(), 0, &(*statState)->ProcessorIdleCounter), "PdhAddCounter");
1157 	}
1158 	handlePdhStatus( PdhCollectQueryData((*statState)->Query), "PdhCollectQueryData" );
1159 
1160 	PDH_FMT_COUNTERVALUE DisplayValue;
1161 	if (returnStats.initialized) {
1162 		if (!(*statState)->pdhStrings.diskDevice.empty()) {
1163 			if( handlePdhStatus( PdhGetFormattedCounterValue((*statState)->DiskTimeCounter, PDH_FMT_DOUBLE, 0, &DisplayValue), "DiskTimeCounter" ) )
1164 				returnStats.processDiskIdleSeconds = DisplayValue.doubleValue * returnStats.elapsed / 100.0;
1165 			if( handlePdhStatus( PdhGetFormattedCounterValue((*statState)->QueueLengthCounter, PDH_FMT_DOUBLE, 0, &DisplayValue), "QueueLengthCounter" ) )
1166 				returnStats.processDiskQueueDepth = DisplayValue.doubleValue;
1167 			if( handlePdhStatus( PdhGetFormattedCounterValue((*statState)->ReadsCounter, PDH_FMT_DOUBLE, 0, &DisplayValue), "ReadsCounter" ) )
1168 				returnStats.processDiskRead = DisplayValue.doubleValue * returnStats.elapsed;
1169 			if( handlePdhStatus( PdhGetFormattedCounterValue((*statState)->WritesCounter, PDH_FMT_DOUBLE, 0, &DisplayValue), "WritesCounter" ) )
1170 				returnStats.processDiskWrite = DisplayValue.doubleValue * returnStats.elapsed;
1171 			if (handlePdhStatus(PdhGetFormattedCounterValue((*statState)->WriteBytesCounter, PDH_FMT_DOUBLE, 0, &DisplayValue), "WriteBytesCounter"))
1172 				returnStats.processDiskWriteSectors = DisplayValue.doubleValue * returnStats.elapsed / 512.0;
1173 		}
1174 		returnStats.machineMegabitsSent = 0.0;
1175 		for( int i = 0; i < (*statState)->SendCounters.size(); i++ )
1176 			if( handlePdhStatus( PdhGetFormattedCounterValue((*statState)->SendCounters[i], PDH_FMT_DOUBLE, 0, &DisplayValue), "SendCounter" ) )
1177 				returnStats.machineMegabitsSent += DisplayValue.doubleValue * 7.62939453e-6;
1178 		returnStats.machineMegabitsSent *= returnStats.elapsed;
1179 
1180 		returnStats.machineMegabitsReceived = 0.0;
1181 		for( int i = 0; i < (*statState)->ReceiveCounters.size(); i++ )
1182 			if( handlePdhStatus( PdhGetFormattedCounterValue((*statState)->ReceiveCounters[i], PDH_FMT_DOUBLE, 0, &DisplayValue), "ReceiveCounter" ) )
1183 				returnStats.machineMegabitsReceived += DisplayValue.doubleValue * 7.62939453e-6;
1184 		returnStats.machineMegabitsReceived *= returnStats.elapsed;
1185 
1186 		if (handlePdhStatus(PdhGetFormattedCounterValue((*statState)->SegmentsOutCounter, PDH_FMT_DOUBLE, 0, &DisplayValue), "SegmentsOutCounter"))
1187 			returnStats.machineOutSegs = DisplayValue.doubleValue * returnStats.elapsed;
1188 		if (handlePdhStatus(PdhGetFormattedCounterValue((*statState)->SegmentsRetransCounter, PDH_FMT_DOUBLE, 0, &DisplayValue), "SegmentsRetransCounter"))
1189 			returnStats.machineRetransSegs = DisplayValue.doubleValue * returnStats.elapsed;
1190 
1191 		if( handlePdhStatus( PdhGetFormattedCounterValue((*statState)->ProcessorIdleCounter, PDH_FMT_DOUBLE, 0, &DisplayValue), "ProcessorIdleCounter" ) )
1192 			returnStats.machineCPUSeconds = (100 - DisplayValue.doubleValue) * returnStats.elapsed / 100.0;
1193 	}
1194 #elif defined(__unixish__)
1195 	uint64_t machineNowSent = (*statState)->machineLastSent;
1196 	uint64_t machineNowReceived = (*statState)->machineLastReceived;
1197 	uint64_t machineOutSegs = (*statState)->machineLastOutSegs;
1198 	uint64_t machineRetransSegs = (*statState)->machineLastRetransSegs;
1199 
1200 	getNetworkTraffic(*ip, machineNowSent, machineNowReceived, machineOutSegs, machineRetransSegs);
1201 	if( returnStats.initialized ) {
1202 		returnStats.machineMegabitsSent = ((machineNowSent - (*statState)->machineLastSent) * 8e-6);
1203 		returnStats.machineMegabitsReceived = ((machineNowReceived - (*statState)->machineLastReceived) * 8e-6);
1204 		returnStats.machineOutSegs = machineOutSegs - (*statState)->machineLastOutSegs;
1205 		returnStats.machineRetransSegs = machineRetransSegs - (*statState)->machineLastRetransSegs;
1206 	}
1207 	(*statState)->machineLastSent = machineNowSent;
1208 	(*statState)->machineLastReceived = machineNowReceived;
1209 	(*statState)->machineLastOutSegs = machineOutSegs;
1210 	(*statState)->machineLastRetransSegs = machineRetransSegs;
1211 
1212 	uint64_t currentIOs;
1213 	uint64_t nowBusyTicks = (*statState)->lastBusyTicks;
1214 	uint64_t nowReads = (*statState)->lastReads;
1215 	uint64_t nowWrites = (*statState)->lastWrites;
1216 	uint64_t nowWriteSectors = (*statState)->lastWriteSectors;
1217 	uint64_t nowReadSectors = (*statState)->lastReadSectors;
1218 
1219 	if(dataFolder != "") {
1220 		getDiskStatistics(dataFolder, currentIOs, nowBusyTicks, nowReads, nowWrites, nowWriteSectors, nowReadSectors);
1221 		returnStats.processDiskQueueDepth = currentIOs;
1222 		returnStats.processDiskReadCount = nowReads;
1223 		returnStats.processDiskWriteCount = nowWrites;
1224 		if( returnStats.initialized ) {
1225 			returnStats.processDiskIdleSeconds = std::max<double>(0, returnStats.elapsed - std::min<double>(returnStats.elapsed, (nowBusyTicks - (*statState)->lastBusyTicks) / 1000.0));
1226 			returnStats.processDiskRead = (nowReads - (*statState)->lastReads);
1227 			returnStats.processDiskWrite = (nowWrites - (*statState)->lastWrites);
1228 			returnStats.processDiskWriteSectors = (nowWriteSectors - (*statState)->lastWriteSectors);
1229 			returnStats.processDiskReadSectors = (nowReadSectors - (*statState)->lastReadSectors);
1230 		}
1231 		(*statState)->lastBusyTicks = nowBusyTicks;
1232 		(*statState)->lastReads = nowReads;
1233 		(*statState)->lastWrites = nowWrites;
1234 		(*statState)->lastWriteSectors = nowWriteSectors;
1235 		(*statState)->lastReadSectors = nowReadSectors;
1236 	}
1237 
1238 	uint64_t clockIdleTime = (*statState)->lastClockIdleTime;
1239 	uint64_t clockTotalTime = (*statState)->lastClockTotalTime;
1240 
1241 	getMachineLoad(clockIdleTime, clockTotalTime, logDetails);
1242 	returnStats.machineCPUSeconds = clockTotalTime - (*statState)->lastClockTotalTime != 0 ? ( 1 - ((clockIdleTime - (*statState)->lastClockIdleTime) / ((double)(clockTotalTime - (*statState)->lastClockTotalTime)))) * returnStats.elapsed : 0;
1243 	(*statState)->lastClockIdleTime = clockIdleTime;
1244 	(*statState)->lastClockTotalTime = clockTotalTime;
1245 #endif
1246 	(*statState)->lastTime = nowTime;
1247 	(*statState)->lastClockProcess = nowClockProcess;
1248 	(*statState)->lastClockThread = nowClockThread;
1249 	return returnStats;
1250 }
1251 
1252 #ifdef _WIN32
1253 struct OffsetTimer {
1254 	double secondsPerCount, offset;
1255 
1256 	static const int64_t FILETIME_C_EPOCH = 11644473600LL * 10000000LL;	// Difference between FILETIME epoch (1601) and Unix epoch (1970) in 100ns FILETIME ticks
1257 
OffsetTimerOffsetTimer1258 	OffsetTimer() {
1259 		long long countsPerSecond;
1260 		if (!QueryPerformanceFrequency( (LARGE_INTEGER*)&countsPerSecond))
1261 			throw performance_counter_error();
1262 		secondsPerCount = 1.0 / countsPerSecond;
1263 
1264 		FILETIME fileTime;
1265 
1266 		offset = 0;
1267 		double timer = now();
1268 		GetSystemTimeAsFileTime(&fileTime);
1269 		static_assert( sizeof(fileTime) == sizeof(uint64_t), "FILETIME size wrong" );
1270 		offset = (*(uint64_t*)&fileTime - FILETIME_C_EPOCH) * 100e-9 - timer;
1271 	}
1272 
nowOffsetTimer1273 	double now() {
1274 		long long count;
1275 		if (!QueryPerformanceCounter( (LARGE_INTEGER*)&count ))
1276 			throw performance_counter_error();
1277 		return offset + count * secondsPerCount;
1278 	}
1279 };
1280 #elif defined(__linux__)
1281 #define DOUBLETIME(ts) (double(ts.tv_sec) + (ts.tv_nsec * 1e-9))
1282 #ifndef CLOCK_MONOTONIC_RAW
1283 #define CLOCK_MONOTONIC_RAW 4 // Confirmed safe to do with glibc >= 2.11 and kernel >= 2.6.28. No promises with older glibc. Older kernel definitely breaks it.
1284 #endif
1285 struct OffsetTimer {
1286 	double offset;
1287 
OffsetTimerOffsetTimer1288 	OffsetTimer() {
1289 		struct timespec ts;
1290 		clock_gettime(CLOCK_REALTIME, &ts);
1291 		offset = DOUBLETIME(ts);
1292 		clock_gettime(CLOCK_MONOTONIC, &ts);
1293 		offset -= DOUBLETIME(ts);
1294 	}
1295 
nowOffsetTimer1296 	double now() {
1297 		struct timespec ts;
1298 		clock_gettime(CLOCK_MONOTONIC, &ts);
1299 		return (offset + DOUBLETIME(ts));
1300 	}
1301 };
1302 
1303 #elif defined(__APPLE__)
1304 
1305 #include <mach/mach.h>
1306 #include <mach/mach_time.h>
1307 
1308 struct OffsetTimer {
1309 	mach_timebase_info_data_t timebase_info;
1310 	uint64_t offset;
1311 	double offset_seconds;
1312 
OffsetTimerOffsetTimer1313 	OffsetTimer() {
1314 		mach_timebase_info(&timebase_info);
1315 		offset = mach_absolute_time();
1316 
1317 		struct timeval tv;
1318 		gettimeofday(&tv, NULL);
1319 
1320 		offset_seconds = tv.tv_sec + 1e-6 * tv.tv_usec;
1321 	}
1322 
nowOffsetTimer1323 	double now() {
1324 		uint64_t elapsed = mach_absolute_time() - offset;
1325 		return offset_seconds + double((elapsed * timebase_info.numer) / timebase_info.denom) * 1e-9;
1326 	}
1327 };
1328 
1329 #else
1330 #error Port me!
1331 #endif
1332 
timer_monotonic()1333 double timer_monotonic() {
1334 	static OffsetTimer theTimer;
1335 	return theTimer.now();
1336 }
1337 
timer()1338 double timer() {
1339 #ifdef _WIN32
1340 	static const int64_t FILETIME_C_EPOCH = 11644473600LL * 10000000LL;	// Difference between FILETIME epoch (1601) and Unix epoch (1970) in 100ns FILETIME ticks
1341 	FILETIME fileTime;
1342 	GetSystemTimeAsFileTime(&fileTime);
1343 	static_assert( sizeof(fileTime) == sizeof(uint64_t), "FILETIME size wrong" );
1344 	return (*(uint64_t*)&fileTime - FILETIME_C_EPOCH) * 100e-9;
1345 #elif defined(__linux__)
1346 	struct timespec ts;
1347 	clock_gettime(CLOCK_REALTIME, &ts);
1348 	return double(ts.tv_sec) + (ts.tv_nsec * 1e-9);
1349 #elif defined(__APPLE__)
1350 	struct timeval tv;
1351 	gettimeofday(&tv, NULL);
1352 	return double(tv.tv_sec) + (tv.tv_usec * 1e-6);
1353 #else
1354 #error Port me!
1355 #endif
1356 };
1357 
timer_int()1358 uint64_t timer_int() {
1359 #ifdef _WIN32
1360 	static const int64_t FILETIME_C_EPOCH = 11644473600LL * 10000000LL;	// Difference between FILETIME epoch (1601) and Unix epoch (1970) in 100ns FILETIME ticks
1361 	FILETIME fileTime;
1362 	GetSystemTimeAsFileTime(&fileTime);
1363 	static_assert( sizeof(fileTime) == sizeof(uint64_t), "FILETIME size wrong" );
1364 	return (*(uint64_t*)&fileTime - FILETIME_C_EPOCH);
1365 #elif defined(__linux__)
1366 	struct timespec ts;
1367 	clock_gettime(CLOCK_REALTIME, &ts);
1368 	return uint64_t(ts.tv_sec) * 1e9 + ts.tv_nsec;
1369 #elif defined(__APPLE__)
1370 	struct timeval tv;
1371 	gettimeofday(&tv, NULL);
1372 	return uint64_t(tv.tv_sec) * 1e9 + (tv.tv_usec * 1e3);
1373 #else
1374 #error Port me!
1375 #endif
1376 };
1377 
getLocalTime(const time_t * timep,struct tm * result)1378 void getLocalTime(const time_t *timep, struct tm *result) {
1379 #ifdef _WIN32
1380 	if(localtime_s(result, timep) != 0) {
1381 		TraceEvent(SevError, "GetLocalTimeError").GetLastError();
1382 		throw platform_error();
1383 	}
1384 #elif defined(__unixish__)
1385 	if(localtime_r(timep, result) == NULL) {
1386 		TraceEvent(SevError, "GetLocalTimeError").GetLastError();
1387 		throw platform_error();
1388 	}
1389 #else
1390 #error Port me!
1391 #endif
1392 }
1393 
setMemoryQuota(size_t limit)1394 void setMemoryQuota( size_t limit ) {
1395 #if defined(USE_ASAN)
1396 	// ASAN doesn't work with memory quotas: https://github.com/google/sanitizers/wiki/AddressSanitizer#ulimit--v
1397 	return;
1398 #endif
1399 	INJECT_FAULT( platform_error, "setMemoryQuota" );
1400 #if defined(_WIN32)
1401 	HANDLE job = CreateJobObject( NULL, NULL );
1402 	if (!job) {
1403 		TraceEvent(SevError, "WinCreateJobError").GetLastError();
1404 		throw platform_error();
1405 	}
1406 	JOBOBJECT_EXTENDED_LIMIT_INFORMATION limits;
1407 	limits.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_JOB_MEMORY;
1408 	limits.JobMemoryLimit = limit;
1409 	if (!SetInformationJobObject( job, JobObjectExtendedLimitInformation, &limits, sizeof(limits) )) {
1410 		TraceEvent(SevError, "FailedToSetInfoOnJobObject").detail("Limit", limit).GetLastError();
1411 		throw platform_error();
1412 	}
1413 	if (!AssignProcessToJobObject( job, GetCurrentProcess() ))
1414 		TraceEvent(SevWarn, "FailedToSetMemoryLimit").GetLastError();
1415 #elif defined(__linux__)
1416 	struct rlimit rlim;
1417 	if (getrlimit(RLIMIT_AS, &rlim)) {
1418 		TraceEvent(SevError, "GetMemoryLimit").GetLastError();
1419 		throw platform_error();
1420 	} else if (limit > rlim.rlim_max) {
1421 		TraceEvent(SevError, "MemoryLimitTooHigh").detail("Limit", limit).detail("ResidentMaxLimit", rlim.rlim_max);
1422 		throw platform_error();
1423 	}
1424 	rlim.rlim_cur = limit;
1425 	if (setrlimit(RLIMIT_AS, &rlim)) {
1426 		TraceEvent(SevError, "SetMemoryLimit").detail("Limit", limit).GetLastError();
1427 		throw platform_error();
1428 	}
1429 #endif
1430 }
1431 
1432 #ifdef _WIN32
ModifyPrivilege(const char * szPrivilege,bool fEnable)1433 static int ModifyPrivilege( const char* szPrivilege, bool fEnable )
1434 {
1435 	HRESULT hr = S_OK;
1436 	TOKEN_PRIVILEGES NewState;
1437 	LUID luid;
1438 	HANDLE hToken = NULL;
1439 
1440 	// Open the process token for this process.
1441 	if (!OpenProcessToken( GetCurrentProcess(),
1442 						   TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
1443 						   &hToken ))
1444 	{
1445 		TraceEvent( SevWarn, "OpenProcessTokenError" ).error(large_alloc_failed()).GetLastError();
1446 		return ERROR_FUNCTION_FAILED;
1447 	}
1448 
1449 	// Get the local unique ID for the privilege.
1450 	if ( !LookupPrivilegeValue( NULL,
1451 								szPrivilege,
1452 								&luid ))
1453 	{
1454 		CloseHandle( hToken );
1455 		TraceEvent( SevWarn, "LookupPrivilegeValue" ).error(large_alloc_failed()).GetLastError();
1456 		return ERROR_FUNCTION_FAILED;
1457 	}
1458 
1459 	//cout << luid.HighPart << " " << luid.LowPart << endl;
1460 
1461 	// Assign values to the TOKEN_PRIVILEGE structure.
1462 	NewState.PrivilegeCount = 1;
1463 	NewState.Privileges[0].Luid = luid;
1464 	NewState.Privileges[0].Attributes =
1465 		(fEnable ? SE_PRIVILEGE_ENABLED : 0);
1466 
1467 	// Adjust the token privilege.
1468 	if (!AdjustTokenPrivileges(hToken,
1469 							   FALSE,
1470 							   &NewState,
1471 							   0,
1472 							   NULL,
1473 							   NULL))
1474 	{
1475 		TraceEvent( SevWarn, "AdjustTokenPrivileges" ).error(large_alloc_failed()).GetLastError();
1476 		hr = ERROR_FUNCTION_FAILED;
1477 	}
1478 
1479 	// Close the handle.
1480 	CloseHandle(hToken);
1481 
1482 	return hr;
1483 }
1484 #endif
1485 
1486 static bool largePagesPrivilegeEnabled = false;
1487 
enableLargePages()1488 static void enableLargePages() {
1489 	if (largePagesPrivilegeEnabled)
1490 		return;
1491 #ifdef _WIN32
1492 	ModifyPrivilege(SE_LOCK_MEMORY_NAME, true);
1493 	largePagesPrivilegeEnabled = true;
1494 #else
1495 	// SOMEDAY: can/should we teach the client how to enable large pages
1496 	// on Linux? Or just rely on the system to have been configured as
1497 	// desired?
1498 #endif
1499 }
1500 
allocateInternal(size_t length,bool largePages)1501 static void *allocateInternal(size_t length, bool largePages) {
1502 
1503 #ifdef _WIN32
1504 	DWORD allocType = MEM_COMMIT|MEM_RESERVE;
1505 
1506 	if (largePages)
1507 		allocType |= MEM_LARGE_PAGES;
1508 
1509 	return VirtualAlloc(NULL, length, allocType, PAGE_READWRITE);
1510 #elif defined(__linux__)
1511 	int flags = MAP_PRIVATE|MAP_ANONYMOUS;
1512 
1513 	if (largePages)
1514 		flags |= MAP_HUGETLB;
1515 
1516 	return mmap(NULL, length, PROT_READ|PROT_WRITE, flags, -1, 0);
1517 #elif defined(__APPLE__)
1518 	int flags = MAP_PRIVATE|MAP_ANON;
1519 
1520 	return mmap(NULL, length, PROT_READ|PROT_WRITE, flags, -1, 0);
1521 #else
1522 #error Port me!
1523 #endif
1524 }
1525 
1526 static bool largeBlockFail = false;
allocate(size_t length,bool allowLargePages)1527 void *allocate(size_t length, bool allowLargePages) {
1528 	if (allowLargePages)
1529 		enableLargePages();
1530 
1531 	void *block = ALLOC_FAIL;
1532 
1533 	if (allowLargePages && !largeBlockFail) {
1534 		block = allocateInternal(length, true);
1535 		if (block == ALLOC_FAIL) largeBlockFail = true;
1536 	}
1537 
1538 	if (block == ALLOC_FAIL)
1539 		block = allocateInternal(length, false);
1540 
1541 	// FIXME: SevWarnAlways trace if "close" to out of memory
1542 
1543 	if (block == ALLOC_FAIL)
1544 		platform::outOfMemory();
1545 
1546 	return block;
1547 }
1548 
1549 #if 0
1550 void* numaAllocate(size_t size) {
1551 	void* thePtr = (void*)0xA00000000LL;
1552 	enableLargePages();
1553 
1554 	size_t vaPageSize = 2<<20;//64<<10;
1555 	int nVAPages = size / vaPageSize;
1556 
1557 	int nodes;
1558 	if (!GetNumaHighestNodeNumber((PULONG)&nodes)) {
1559 		TraceEvent(SevError, "GetNumaHighestNodeNumber").getLastError();
1560 		throw platform_error();
1561 	}
1562 	++nodes;
1563 
1564 	for(int i=0; i<nodes; i++) {
1565 		char* p = (char*)thePtr + i*nVAPages/nodes*vaPageSize;
1566 		char* e = (char*)thePtr + (i+1)*nVAPages/nodes*vaPageSize;
1567 		//printf("  %p + %lld\n", p, e-p);
1568 		// SOMEDAY: removed NUMA extensions for compatibity with Windows Server 2003 -- make execution dynamic
1569 		if (!VirtualAlloc/*ExNuma*/(/*GetCurrentProcess(),*/ p, e-p, MEM_COMMIT|MEM_RESERVE|MEM_LARGE_PAGES, PAGE_READWRITE/*, i*/)) {
1570 			Error e = platform_error();
1571 			TraceEvent(e, "VirtualAlloc").GetLastError();
1572 			throw e;
1573 		}
1574 	}
1575 	return thePtr;
1576 }
1577 #endif
1578 
setAffinity(int proc)1579 void setAffinity(int proc) {
1580 #if defined(_WIN32)
1581 	/*if (SetProcessAffinityMask(GetCurrentProcess(), 0x5555))//0x5555555555555555UL))
1582 		printf("Set affinity mask\n");
1583 	else
1584 		printf("Failed to set affinity mask: error %d\n", GetLastError());*/
1585 	SetThreadAffinityMask( GetCurrentThread(), 1ULL<<proc );
1586 #elif defined(__linux__)
1587 	cpu_set_t set;
1588 	CPU_ZERO(&set);
1589 	CPU_SET(proc, &set);
1590 	sched_setaffinity(0, sizeof(cpu_set_t), &set);
1591 #endif
1592 }
1593 
1594 
1595 namespace platform {
1596 
getRandomSeed()1597 int getRandomSeed() {
1598 	INJECT_FAULT( platform_error, "getRandomSeed" );
1599 	int randomSeed;
1600 	int retryCount = 0;
1601 
1602 #ifdef _WIN32
1603 	do {
1604 		retryCount++;
1605 		if( rand_s( (unsigned int *)&randomSeed ) != 0 ) {
1606 			TraceEvent(SevError, "WindowsRandomSeedError");
1607 			throw platform_error();
1608 		}
1609 	} while (randomSeed == 0 && retryCount < FLOW_KNOBS->RANDOMSEED_RETRY_LIMIT);	// randomSeed cannot be 0 since we use mersenne twister in DeterministicRandom. Get a new one if randomSeed is 0.
1610 #else
1611 	int devRandom = open("/dev/urandom", O_RDONLY);
1612 	do {
1613 		retryCount++;
1614 		if (read(devRandom, &randomSeed, sizeof(randomSeed)) != sizeof(randomSeed) ) {
1615 			TraceEvent(SevError, "OpenURandom").GetLastError();
1616 			throw platform_error();
1617 		}
1618 	} while (randomSeed == 0 && retryCount < FLOW_KNOBS->RANDOMSEED_RETRY_LIMIT);
1619 	close(devRandom);
1620 #endif
1621 
1622 	if (randomSeed == 0) {
1623 		TraceEvent(SevError, "RandomSeedZeroError");
1624 		throw platform_error();
1625 	}
1626 	return randomSeed;
1627 }
1628 }; // namespace platform
1629 
joinPath(std::string const & directory,std::string const & filename)1630 std::string joinPath( std::string const& directory, std::string const& filename ) {
1631 	auto d = directory;
1632 	auto f = filename;
1633 	while (f.size() && (f[0] == '/' || f[0] == CANONICAL_PATH_SEPARATOR))
1634 		f = f.substr(1);
1635 	while (d.size() && (d.back() == '/' || d.back() == CANONICAL_PATH_SEPARATOR))
1636 		d.resize(d.size() - 1);
1637 	return d + CANONICAL_PATH_SEPARATOR + f;
1638 }
1639 
renamedFile()1640 void renamedFile() {
1641 	INJECT_FAULT( io_error, "renameFile" );
1642 }
1643 
renameFile(std::string const & fromPath,std::string const & toPath)1644 void renameFile( std::string const& fromPath, std::string const& toPath ) {
1645 	INJECT_FAULT( io_error, "renameFile" );
1646 #ifdef _WIN32
1647 	if (MoveFile( fromPath.c_str(), toPath.c_str() )) {
1648 		//renamedFile();
1649 		return;
1650 	}
1651 #elif (defined(__linux__) || defined(__APPLE__))
1652 	if (!rename( fromPath.c_str(), toPath.c_str() )) {
1653 		//FIXME: We cannot inject faults after renaming the file, because we could end up with two asyncFileNonDurable open for the same file
1654 		//renamedFile();
1655 		return;
1656 	}
1657 #else
1658 	#error Port me!
1659 #endif
1660 	TraceEvent(SevError, "RenameFile").detail("FromPath", fromPath).detail("ToPath", toPath).GetLastError();
1661 	throw io_error();
1662 }
1663 
atomicReplace(std::string const & path,std::string const & content,bool textmode)1664 void atomicReplace( std::string const& path, std::string const& content, bool textmode ) {
1665 	FILE* f = 0;
1666 	try {
1667 		INJECT_FAULT( io_error, "atomicReplace" );
1668 
1669 		std::string tempfilename = joinPath(parentDirectory(path), g_random->randomUniqueID().toString() + ".tmp");
1670 		f = textmode ? fopen( tempfilename.c_str(), "wt" ) : fopen(tempfilename.c_str(), "wb");
1671 		if(!f)
1672 			throw io_error();
1673 	#ifdef _WIN32
1674 		// In Windows case, ReplaceFile API is used which preserves the ownership,
1675 		// ACLs and other attributes of the original file
1676 	#elif defined(__unixish__)
1677 		// get the uid/gid/mode bits of old file and set it on new file, else fail
1678 		struct stat info;
1679 		bool exists = true;
1680 		if (stat(path.c_str(), &info) < 0) {
1681 			if (errno == ENOENT) {
1682 				exists = false;
1683 			} else {
1684 				TraceEvent("StatFailed").detail("Path", path);
1685 				throw io_error();
1686 			}
1687 		}
1688 		if (exists && chown(tempfilename.c_str(), info.st_uid, info.st_gid) < 0) {
1689 			TraceEvent("ChownFailed")
1690 				.detail("TempFilename", tempfilename)
1691 				.detail("OriginalFile", path)
1692 				.detail("Uid", info.st_uid)
1693 				.detail("Gid", info.st_gid);
1694 			deleteFile(tempfilename);
1695 			throw io_error();
1696 		}
1697 		if (exists && chmod(tempfilename.c_str(), info.st_mode) < 0) {
1698 			TraceEvent("ChmodFailed")
1699 				.detail("TempFilename", tempfilename)
1700 				.detail("OriginalFile", path)
1701 				.detail("Mode", info.st_mode);
1702 			deleteFile(tempfilename);
1703 			throw io_error();
1704 		}
1705 	#else
1706 	#error Port me!
1707 	#endif
1708 
1709 		if( textmode && fprintf( f, "%s", content.c_str() ) < 0)
1710 			throw io_error();
1711 
1712 		if (!textmode && fwrite(content.c_str(), sizeof(uint8_t), content.size(), f) != content.size())
1713 			throw io_error();
1714 
1715 		if(fflush(f) != 0)
1716 			throw io_error();
1717 
1718 	#ifdef _WIN32
1719 		HANDLE h = (HANDLE)_get_osfhandle(_fileno(f));
1720 		if(!g_network->isSimulated()) {
1721 			if(!FlushFileBuffers(h))
1722 				throw io_error();
1723 		}
1724 
1725 		if(fclose(f) != 0) {
1726 			f = 0;
1727 			throw io_error();
1728 		}
1729 		f = 0;
1730 
1731 		if(!ReplaceFile( path.c_str(), tempfilename.c_str(), NULL, NULL, NULL, NULL ))
1732 			throw io_error();
1733 	#elif defined(__unixish__)
1734 		if(!g_network->isSimulated()) {
1735 			if(fsync( fileno(f) ) != 0)
1736 				throw io_error();
1737 		}
1738 
1739 		if(fclose(f) != 0) {
1740 			f = 0;
1741 			throw io_error();
1742 		}
1743 		f = 0;
1744 
1745 		if(rename( tempfilename.c_str(), path.c_str() ) != 0)
1746 			throw io_error();
1747 	#else
1748 	#error Port me!
1749 	#endif
1750 
1751 		INJECT_FAULT( io_error, "atomicReplace" );
1752 	}
1753 	catch(Error &e) {
1754 		TraceEvent(SevWarn, "AtomicReplace").error(e).detail("Path", path).GetLastError();
1755 		if (f) fclose(f);
1756 		throw;
1757 	}
1758 }
1759 
deletedFile()1760 static bool deletedFile() {
1761 	INJECT_FAULT( platform_error, "deleteFile" );
1762 	return true;
1763 }
1764 
deleteFile(std::string const & filename)1765 bool deleteFile( std::string const& filename ) {
1766 	INJECT_FAULT( platform_error, "deleteFile" );
1767 #ifdef _WIN32
1768 	if (DeleteFile(filename.c_str()))
1769 		return deletedFile();
1770 	if (GetLastError() == ERROR_FILE_NOT_FOUND)
1771 		return false;
1772 #elif defined(__unixish__)
1773 	if (!unlink( filename.c_str() ))
1774 		return deletedFile();
1775 	if (errno == ENOENT)
1776 		return false;
1777 #else
1778 	#error Port me!
1779 #endif
1780 	Error e = systemErrorCodeToError();
1781 	TraceEvent(SevError, "DeleteFile").detail("Filename", filename).GetLastError().error(e);
1782 	throw errno;
1783 }
1784 
createdDirectory()1785 static void createdDirectory() { INJECT_FAULT( platform_error, "createDirectory" ); }
1786 
1787 namespace platform {
1788 
createDirectory(std::string const & directory)1789 bool createDirectory( std::string const& directory ) {
1790 	INJECT_FAULT( platform_error, "createDirectory" );
1791 
1792 #ifdef _WIN32
1793 	if (CreateDirectory( directory.c_str(), NULL )) {
1794 		createdDirectory();
1795 		return true;
1796 	}
1797 	if (GetLastError() == ERROR_ALREADY_EXISTS)
1798 		return false;
1799 	if (GetLastError() == ERROR_PATH_NOT_FOUND) {
1800 		size_t delim = directory.find_last_of("/\\");
1801 		if (delim != std::string::npos) {
1802 			createDirectory( directory.substr(0, delim) );
1803 			return createDirectory( directory );
1804 		}
1805 	}
1806 	Error e = systemErrorCodeToError();
1807 	TraceEvent(SevError, "CreateDirectory").detail("Directory", directory).GetLastError().error(e);
1808 	throw e;
1809 #elif (defined(__linux__) || defined(__APPLE__))
1810 	size_t sep = 0;
1811 	do {
1812 		sep = directory.find_first_of('/', sep + 1);
1813 		if ( mkdir( directory.substr(0, sep).c_str(), 0755 ) != 0 ) {
1814 			if (errno == EEXIST)
1815 				continue;
1816 
1817 			Error e;
1818 			if(errno == EACCES) {
1819 				e = file_not_writable();
1820 			}
1821 			else {
1822 				e = systemErrorCodeToError();
1823 			}
1824 
1825 			TraceEvent(SevError, "CreateDirectory").detail("Directory", directory).GetLastError().error(e);
1826 			throw e;
1827 		}
1828 		createdDirectory();
1829 	} while (sep != std::string::npos && sep != directory.length() - 1);
1830 	return true;
1831 #else
1832 	#error Port me!
1833 #endif
1834 }
1835 
1836 }; // namespace platform
1837 
1838 const uint8_t separatorChar = CANONICAL_PATH_SEPARATOR;
1839 StringRef separator(&separatorChar, 1);
1840 StringRef dotdot = LiteralStringRef("..");
1841 
cleanPath(std::string const & path)1842 std::string cleanPath(std::string const &path) {
1843 	std::vector<StringRef> finalParts;
1844 	bool absolute = !path.empty() && path[0] == CANONICAL_PATH_SEPARATOR;
1845 
1846 	StringRef p(path);
1847 
1848 	while(p.size() != 0) {
1849 		StringRef part = p.eat(separator);
1850 		if(part.size() == 0 || (part.size() == 1 && part[0] == '.'))
1851 			continue;
1852 		if(part == dotdot) {
1853 			if(!finalParts.empty() && finalParts.back() != dotdot) {
1854 				finalParts.pop_back();
1855 				continue;
1856 			}
1857 			if(absolute) {
1858 				continue;
1859 			}
1860 		}
1861 		finalParts.push_back(part);
1862 	}
1863 
1864 	std::string result;
1865 	result.reserve(PATH_MAX);
1866 	if(absolute) {
1867 		result.append(1, CANONICAL_PATH_SEPARATOR);
1868 	}
1869 
1870 	for(int i = 0; i < finalParts.size(); ++i) {
1871 		if(i != 0) {
1872 			result.append(1, CANONICAL_PATH_SEPARATOR);
1873 		}
1874 		result.append((const char *)finalParts[i].begin(), finalParts[i].size());
1875 	}
1876 
1877 	return result.empty() ? "." : result;
1878 }
1879 
1880 // Removes the last component from a path string (if possible) and returns the result with one trailing separator.
1881 // If there is only one path component, the result will be "" for relative paths and "/" for absolute paths.
1882 // Note that this is NOT the same as getting the parent of path, as the final component could be ".."
1883 // or "." and it would still be simply removed.
1884 // ALL of the following inputs will yield the result "/a/"
1885 //   /a/b
1886 //   /a/b/
1887 //   /a/..
1888 //   /a/../
1889 //   /a/.
1890 //   /a/./
1891 //   /a//..//
popPath(const std::string & path)1892 std::string popPath(const std::string &path) {
1893 	int i = path.size() - 1;
1894 	// Skip over any trailing separators
1895 	while(i >= 0 && path[i] == CANONICAL_PATH_SEPARATOR) {
1896 		--i;
1897 	}
1898 	// Skip over non separators
1899 	while(i >= 0 && path[i] != CANONICAL_PATH_SEPARATOR) {
1900 		--i;
1901 	}
1902 	// Skip over trailing separators again
1903 	bool foundSeparator = false;
1904 	while(i >= 0 && path[i] == CANONICAL_PATH_SEPARATOR) {
1905 		--i;
1906 		foundSeparator = true;
1907 	}
1908 
1909 	if(foundSeparator) {
1910 		++i;
1911 	}
1912 	else {
1913 		// If absolute then we popped off the only path component so return "/"
1914 		if(!path.empty() && path.front() == CANONICAL_PATH_SEPARATOR) {
1915 			return "/";
1916 		}
1917 	}
1918 	return path.substr(0, i + 1);
1919 }
1920 
abspath(std::string const & path,bool resolveLinks,bool mustExist)1921 std::string abspath( std::string const& path, bool resolveLinks, bool mustExist ) {
1922 	if(path.empty()) {
1923 		Error e = platform_error();
1924 		Severity sev = e.code() == error_code_io_error ? SevError : SevWarnAlways;
1925 		TraceEvent(sev, "AbsolutePathError").detail("Path", path).error(e);
1926 		throw e;
1927 	}
1928 
1929 	// Returns an absolute path canonicalized to use only CANONICAL_PATH_SEPARATOR
1930 	INJECT_FAULT( platform_error, "abspath" );
1931 
1932 	if(!resolveLinks) {
1933 		// TODO:  Not resolving symbolic links does not yet behave well on Windows because of drive letters
1934 		// and network names, so it's not currently allowed here (but it is allowed in fdbmonitor which is unix-only)
1935 		ASSERT(false);
1936 		// Treat paths starting with ~ or separator as absolute, meaning they shouldn't be appended to the current working dir
1937 		bool absolute = !path.empty() && (path[0] == CANONICAL_PATH_SEPARATOR || path[0] == '~');
1938 		std::string clean = cleanPath(absolute ? path : joinPath(platform::getWorkingDirectory(), path));
1939 		if(mustExist && !fileExists(clean)) {
1940 			Error e = systemErrorCodeToError();
1941 			Severity sev = e.code() == error_code_io_error ? SevError : SevWarnAlways;
1942 			TraceEvent(sev, "AbsolutePathError").detail("Path", path).GetLastError().error(e);
1943 			throw e;
1944 		}
1945 		return clean;
1946 	}
1947 
1948 #ifdef _WIN32
1949 	char nameBuffer[MAX_PATH];
1950 	if(!GetFullPathName(path.c_str(), MAX_PATH, nameBuffer, NULL) || (mustExist && !fileExists(nameBuffer))) {
1951 		Error e = systemErrorCodeToError();
1952 		Severity sev = e.code() == error_code_io_error ? SevError : SevWarnAlways;
1953 		TraceEvent(sev, "AbsolutePathError").detail("Path", path).GetLastError().error(e);
1954 		throw e;
1955 	}
1956 	// Not totally obvious from the help whether GetFullPathName canonicalizes slashes, so let's do it...
1957 	for(char*x = nameBuffer; *x; x++)
1958 		if (*x == '/')
1959 			*x = CANONICAL_PATH_SEPARATOR;
1960 	return nameBuffer;
1961 #elif (defined(__linux__) || defined(__APPLE__))
1962 
1963 	char result[PATH_MAX];
1964 	// Must resolve links, so first try realpath on the whole thing
1965 	const char *r = realpath( path.c_str(), result );
1966 	if(r == nullptr) {
1967 		// If the error was ENOENT and the path doesn't have to exist,
1968 		// try to resolve symlinks in progressively shorter prefixes of the path
1969 		if(errno == ENOENT && !mustExist) {
1970 			std::string prefix = popPath(path);
1971 			std::string suffix = path.substr(prefix.size());
1972 			if(prefix.empty() && (suffix.empty() || suffix[0] != '~')) {
1973 				prefix = ".";
1974 			}
1975 			if(!prefix.empty()) {
1976 				return cleanPath(joinPath(abspath(prefix, true, false), suffix));
1977 			}
1978 		}
1979 		Error e = systemErrorCodeToError();
1980 		Severity sev = e.code() == error_code_io_error ? SevError : SevWarnAlways;
1981 		TraceEvent(sev, "AbsolutePathError").detail("Path", path).GetLastError().error(e);
1982 		throw e;
1983 	}
1984 	return std::string(r);
1985 #else
1986 	#error Port me!
1987 #endif
1988 }
1989 
parentDirectory(std::string const & path,bool resolveLinks,bool mustExist)1990 std::string parentDirectory( std::string const& path, bool resolveLinks, bool mustExist ) {
1991 	return popPath(abspath(path, resolveLinks, mustExist));
1992 }
1993 
basename(std::string const & filename)1994 std::string basename( std::string const& filename ) {
1995 	auto abs = abspath(filename);
1996 	size_t sep = abs.find_last_of( CANONICAL_PATH_SEPARATOR );
1997 	if (sep == std::string::npos) return filename;
1998 	return abs.substr(sep+1);
1999 }
2000 
getUserHomeDirectory()2001 std::string getUserHomeDirectory() {
2002 #if defined(__unixish__)
2003 	const char* ret = getenv( "HOME" );
2004 	if ( !ret ) {
2005 		if ( struct passwd *pw = getpwuid( getuid() ) ) {
2006 			ret = pw->pw_dir;
2007 		}
2008 	}
2009 	return ret;
2010 #elif defined(_WIN32)
2011 	TCHAR szPath[MAX_PATH];
2012 	if( SHGetFolderPath(NULL, CSIDL_PROFILE, NULL, 0, szPath)  != S_OK ) {
2013 		TraceEvent(SevError, "GetUserHomeDirectory").GetLastError();
2014 		throw platform_error();
2015 	}
2016 	std::string path(szPath);
2017 	return path;
2018 #else
2019 #error Port me!
2020 #endif
2021 }
2022 
2023 #ifdef _WIN32
2024 #define FILE_ATTRIBUTE_DATA DWORD
2025 #elif (defined(__linux__) || defined(__APPLE__))
2026 #define FILE_ATTRIBUTE_DATA mode_t
2027 #else
2028 #error Port me!
2029 #endif
2030 
acceptFile(FILE_ATTRIBUTE_DATA fileAttributes,std::string name,std::string extension)2031 bool acceptFile( FILE_ATTRIBUTE_DATA fileAttributes, std::string name, std::string extension ) {
2032 #ifdef _WIN32
2033 	return !(fileAttributes & FILE_ATTRIBUTE_DIRECTORY) && StringRef(name).endsWith(extension);
2034 #elif (defined(__linux__) || defined(__APPLE__))
2035 	return S_ISREG(fileAttributes) && StringRef(name).endsWith(extension);
2036 #else
2037 	#error Port me!
2038 #endif
2039 }
2040 
acceptDirectory(FILE_ATTRIBUTE_DATA fileAttributes,std::string name,std::string extension)2041 bool acceptDirectory( FILE_ATTRIBUTE_DATA fileAttributes, std::string name, std::string extension ) {
2042 #ifdef _WIN32
2043 	return (fileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
2044 #elif (defined(__linux__) || defined(__APPLE__))
2045 	return S_ISDIR(fileAttributes);
2046 #else
2047 	#error Port me!
2048 #endif
2049 }
2050 
findFiles(std::string const & directory,std::string const & extension,bool (* accept_file)(FILE_ATTRIBUTE_DATA,std::string,std::string))2051 std::vector<std::string> findFiles( std::string const& directory, std::string const& extension,
2052 		bool (*accept_file)(FILE_ATTRIBUTE_DATA, std::string, std::string)) {
2053 	INJECT_FAULT( platform_error, "findFiles" );
2054 	std::vector<std::string> result;
2055 
2056 #ifdef _WIN32
2057 	WIN32_FIND_DATA fd;
2058 	HANDLE h = FindFirstFile( (directory + "/*" + extension).c_str(), &fd );
2059 	if (h == INVALID_HANDLE_VALUE) {
2060 		if (GetLastError() != ERROR_FILE_NOT_FOUND && GetLastError() != ERROR_PATH_NOT_FOUND) {
2061 			TraceEvent(SevError, "FindFirstFile").detail("Directory", directory).detail("Extension", extension).GetLastError();
2062 			throw platform_error();
2063 		}
2064 	} else {
2065 		while (true) {
2066 			std::string name = fd.cFileName;
2067 			if ((*accept_file)(fd.dwFileAttributes, name, extension)) {
2068 				result.push_back( name );
2069 			}
2070 			if (!FindNextFile( h, &fd ))
2071 				break;
2072 		}
2073 		if (GetLastError() != ERROR_NO_MORE_FILES) {
2074 			TraceEvent(SevError, "FindNextFile").detail("Directory", directory).detail("Extension", extension).GetLastError();
2075 			FindClose(h);
2076 			throw platform_error();
2077 		}
2078 		FindClose(h);
2079 	}
2080 #elif (defined(__linux__) || defined(__APPLE__))
2081 	DIR *dip;
2082 
2083 	if ((dip = opendir(directory.c_str())) != NULL) {
2084 		struct dirent *dit;
2085 		while ((dit = readdir(dip)) != NULL) {
2086 			std::string name(dit->d_name);
2087 			struct stat buf;
2088 			if (stat(joinPath(directory, name).c_str(), &buf)) {
2089 				bool isError = errno != ENOENT;
2090 				TraceEvent(isError ? SevError : SevWarn, "StatFailed")
2091 					.detail("Directory", directory)
2092 					.detail("Extension", extension)
2093 					.detail("Name", name)
2094 					.GetLastError();
2095 				if( isError )
2096 					throw platform_error();
2097 				else
2098 					continue;
2099 			}
2100 			if ((*accept_file)(buf.st_mode, name, extension))
2101 				result.push_back( name );
2102 		}
2103 
2104 		closedir(dip);
2105 	}
2106 #else
2107 	#error Port me!
2108 #endif
2109 	std::sort(result.begin(), result.end());
2110 	return result;
2111 }
2112 
2113 
2114 namespace platform {
2115 
listFiles(std::string const & directory,std::string const & extension)2116 std::vector<std::string> listFiles( std::string const& directory, std::string const& extension ) {
2117 	return findFiles( directory, extension, &acceptFile );
2118 }
2119 
listDirectories(std::string const & directory)2120 std::vector<std::string> listDirectories( std::string const& directory ) {
2121 	return findFiles( directory, "", &acceptDirectory );
2122 }
2123 
findFilesRecursively(std::string path,std::vector<std::string> & out)2124 void findFilesRecursively(std::string path, std::vector<std::string> &out) {
2125 	// Add files to output, prefixing path
2126 	std::vector<std::string> files = platform::listFiles(path);
2127 	for(auto const &f : files)
2128 		out.push_back(joinPath(path, f));
2129 
2130 	// Recurse for directories
2131 	std::vector<std::string> directories = platform::listDirectories(path);
2132 	for(auto const &dir : directories) {
2133 		if(dir != "." && dir != "..")
2134 			findFilesRecursively(joinPath(path, dir), out);
2135 	}
2136 };
2137 
2138 }; // namespace platform
2139 
2140 
threadSleep(double seconds)2141 void threadSleep( double seconds ) {
2142 #ifdef _WIN32
2143 	Sleep( (DWORD)(seconds * 1e3) );
2144 #elif (defined(__linux__) || defined(__APPLE__))
2145 	struct timespec req, rem;
2146 
2147 	req.tv_sec = seconds;
2148 	req.tv_nsec = (seconds - req.tv_sec) * 1e9L;
2149 
2150 	while (nanosleep(&req, &rem) == -1 && errno == EINTR) {
2151 		req.tv_sec = rem.tv_sec;
2152 		req.tv_nsec = rem.tv_nsec;
2153 	}
2154 #else
2155 	#error Port me!
2156 #endif
2157 }
2158 
threadYield()2159 void threadYield() {
2160 #ifdef _WIN32
2161 	Sleep(0);
2162 #elif defined( __unixish__ )
2163 	sched_yield();
2164 #else
2165 #error Port me!
2166 #endif
2167 }
2168 
2169 namespace platform {
2170 
makeTemporary(const char * filename)2171 void makeTemporary( const char* filename ) {
2172 #ifdef _WIN32
2173 	SetFileAttributes(filename, FILE_ATTRIBUTE_TEMPORARY);
2174 #endif
2175 }
2176 }; // namespace platform
2177 
2178 #ifdef _WIN32
startThread(void (* func)(void *),void * arg)2179 THREAD_HANDLE startThread(void (*func) (void *), void *arg) {
2180 	return (void *)_beginthread(func, 0, arg);
2181 }
2182 #elif (defined(__linux__) || defined(__APPLE__))
startThread(void * (* func)(void *),void * arg)2183 THREAD_HANDLE startThread(void *(*func) (void *), void *arg) {
2184 	pthread_t t;
2185 	pthread_create(&t, NULL, func, arg);
2186 	return t;
2187 }
2188 #else
2189 	#error Port me!
2190 #endif
2191 
waitThread(THREAD_HANDLE thread)2192 void waitThread(THREAD_HANDLE thread) {
2193 #ifdef _WIN32
2194 	WaitForSingleObject(thread, INFINITE);
2195 #elif (defined(__linux__) || defined(__APPLE__))
2196 	pthread_join(thread, NULL);
2197 #else
2198 	#error Port me!
2199 #endif
2200 }
2201 
deprioritizeThread()2202 void deprioritizeThread() {
2203 #ifdef __linux__
2204 	int tid = syscall(SYS_gettid);
2205 	setpriority( PRIO_PROCESS, tid, 10 );
2206 #elif defined(_WIN32)
2207 #endif
2208 }
2209 
fileExists(std::string const & filename)2210 bool fileExists(std::string const& filename) {
2211 	FILE* f = fopen(filename.c_str(), "rb");
2212 	if (!f) return false;
2213 	fclose(f);
2214 	return true;
2215 }
2216 
directoryExists(std::string const & path)2217 bool directoryExists(std::string const& path) {
2218 #ifdef _WIN32
2219 	DWORD bits = ::GetFileAttributes(path.c_str());
2220 	return bits != INVALID_FILE_ATTRIBUTES && (bits & FILE_ATTRIBUTE_DIRECTORY);
2221 #else
2222 	DIR *d = opendir(path.c_str());
2223 	if(d == nullptr)
2224 		return false;
2225 	closedir(d);
2226 	return true;
2227 #endif
2228 }
2229 
fileSize(std::string const & filename)2230 int64_t fileSize(std::string const& filename) {
2231 #ifdef _WIN32
2232 	struct _stati64 file_status;
2233 	if(_stati64(filename.c_str(), &file_status) != 0)
2234 		return 0;
2235 	else
2236 		return file_status.st_size;
2237 #elif (defined(__linux__) || defined(__APPLE__))
2238 	struct stat file_status;
2239 	if(stat(filename.c_str(), &file_status) != 0)
2240 		return 0;
2241 	else
2242 		return file_status.st_size;
2243 #else
2244 	#error Port me!
2245 #endif
2246 }
2247 
readFileBytes(std::string const & filename,int maxSize)2248 std::string readFileBytes( std::string const& filename, int maxSize ) {
2249 	std::string s;
2250 	FILE* f = fopen(filename.c_str(), "rb");
2251 	if (!f) throw file_not_readable();
2252 	try {
2253 		fseek(f, 0, SEEK_END);
2254 		size_t size = ftell(f);
2255 		if (size > maxSize)
2256 			throw file_too_large();
2257 		s.resize( size );
2258 		fseek(f, 0, SEEK_SET);
2259 		if (!fread( &s[0], size, 1, f ))
2260 			throw file_not_readable();
2261 	} catch (...) {
2262 		fclose(f);
2263 		throw;
2264 	}
2265 	fclose(f);
2266 	return s;
2267 }
2268 
writeFileBytes(std::string const & filename,const uint8_t * data,size_t count)2269 void writeFileBytes(std::string const& filename, const uint8_t* data, size_t count) {
2270 	FILE* f = fopen(filename.c_str(), "wb");
2271 	if (!f)
2272 	{
2273 		TraceEvent(SevError, "WriteFileBytes").detail("Filename", filename).GetLastError();
2274 		throw file_not_writable();
2275 	}
2276 
2277 	try {
2278 		size_t length = fwrite(data, sizeof(uint8_t), count, f);
2279 		if (length != count)
2280 		{
2281 			TraceEvent(SevError, "WriteFileBytes").detail("Filename", filename).detail("WrittenLength", length).GetLastError();
2282 			throw file_not_writable();
2283 		}
2284 	}
2285 	catch (...) {
2286 		fclose(f);
2287 		throw;
2288 	}
2289 	fclose(f);
2290 }
2291 
writeFile(std::string const & filename,std::string const & content)2292 void writeFile(std::string const& filename, std::string const& content) {
2293 	writeFileBytes(filename, (const uint8_t*)(content.c_str()), content.size());
2294 }
2295 
2296 
2297 namespace platform {
2298 
getEnvironmentVar(const char * name,std::string & value)2299 bool getEnvironmentVar(const char* name, std::string& value) {
2300 #if defined(__unixish__)
2301 	char* val = getenv(name);
2302 	if (val) {
2303 		value = std::string(val);
2304 		return true;
2305 	}
2306 	return false;
2307 #elif defined(_WIN32)
2308 	int len = GetEnvironmentVariable(name, NULL, 0);
2309 	if (len == 0) {
2310 		if (GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
2311 			return false;
2312 		}
2313 		TraceEvent(SevError, "GetEnvironmentVariable").detail("Name", name).GetLastError();
2314 		throw platform_error();
2315 	}
2316 	value.resize(len);
2317 	int rc = GetEnvironmentVariable(name, &value[0], len);
2318 	if (rc + 1 != len) {
2319 		TraceEvent(SevError, "WrongEnvVarLength")
2320 			.detail("ExpectedLength", len)
2321 			.detail("ReceivedLength", rc + 1);
2322 		throw platform_error();
2323 	}
2324 	value.resize(len-1);
2325 	return true;
2326 #else
2327 #error Port me!
2328 #endif
2329 }
2330 
setEnvironmentVar(const char * name,const char * value,int overwrite)2331 int setEnvironmentVar(const char *name, const char *value, int overwrite)
2332 {
2333 #if defined(_WIN32)
2334 	int errcode = 0;
2335 	if(!overwrite) {
2336 		size_t envsize = 0;
2337 		errcode = getenv_s(&envsize, NULL, 0, name);
2338 		if(errcode || envsize) return errcode;
2339 	}
2340 	return _putenv_s(name, value);
2341 #else
2342 	return setenv(name, value, overwrite);
2343 #endif
2344 }
2345 
2346 #if defined(_WIN32)
2347 #define getcwd(buf, maxlen) _getcwd(buf, maxlen)
2348 #endif
getWorkingDirectory()2349 std::string getWorkingDirectory() {
2350 	char *buf;
2351 	if( (buf = getcwd(NULL, 0)) == NULL ) {
2352 		TraceEvent(SevWarnAlways, "GetWorkingDirectoryError").GetLastError();
2353 		throw platform_error();
2354 	}
2355 	std::string result(buf);
2356 	free(buf);
2357 	return result;
2358 }
2359 
2360 }; // namespace platform
2361 
2362 extern std::string format( const char *form, ... );
2363 
2364 
2365 namespace platform {
2366 
getDefaultPluginPath(const char * plugin_name)2367 std::string getDefaultPluginPath( const char* plugin_name ) {
2368 #ifdef _WIN32
2369 	std::string installPath;
2370 	if(!platform::getEnvironmentVar("FOUNDATIONDB_INSTALL_PATH", installPath)) {
2371 		// This is relying of the DLL search order to load the plugin,
2372 		//  starting in the same directory as the executable.
2373 		return plugin_name;
2374 	}
2375 	return format( "%splugins\\%s.dll", installPath.c_str(), plugin_name );
2376 #elif defined(__linux__)
2377 	return format( "/usr/lib/foundationdb/plugins/%s.so", plugin_name );
2378 #elif defined(__APPLE__)
2379 	return format( "/usr/local/foundationdb/plugins/%s.dylib", plugin_name );
2380 #else
2381 	#error Port me!
2382 #endif
2383 }
2384 }; // namespace platform
2385 
2386 #ifdef ALLOC_INSTRUMENTATION
2387 #define TRACEALLOCATOR( size ) TraceEvent("MemSample").detail("Count", FastAllocator<size>::getApproximateMemoryUnused()/size).detail("TotalSize", FastAllocator<size>::getApproximateMemoryUnused()).detail("SampleCount", 1).detail("Hash", "FastAllocatedUnused" #size ).detail("Bt", "na")
2388 #ifdef __linux__
2389 #include <cxxabi.h>
2390 #endif
2391 uint8_t *g_extra_memory;
2392 #endif
2393 
2394 namespace platform {
2395 
outOfMemory()2396 void outOfMemory() {
2397 #ifdef ALLOC_INSTRUMENTATION
2398 	delete [] g_extra_memory;
2399 	std::vector< std::pair<std::string, const char*> > typeNames;
2400 	for( auto i = allocInstr.begin(); i != allocInstr.end(); ++i ) {
2401 		std::string s;
2402 #ifdef __linux__
2403 		char *demangled = abi::__cxa_demangle(i->first, NULL, NULL, NULL);
2404 		if (demangled) {
2405 			s = demangled;
2406 			if (StringRef(s).startsWith(LiteralStringRef("(anonymous namespace)::")))
2407 				s = s.substr(LiteralStringRef("(anonymous namespace)::").size());
2408 			free(demangled);
2409 		} else
2410 			s = i->first;
2411 #else
2412 		s = i->first;
2413 		if (StringRef(s).startsWith(LiteralStringRef("class `anonymous namespace'::")))
2414 			s = s.substr(LiteralStringRef("class `anonymous namespace'::").size());
2415 		else if (StringRef(s).startsWith(LiteralStringRef("class ")))
2416 			s = s.substr(LiteralStringRef("class ").size());
2417 		else if (StringRef(s).startsWith(LiteralStringRef("struct ")))
2418 			s = s.substr(LiteralStringRef("struct ").size());
2419 #endif
2420 		typeNames.push_back( std::make_pair(s, i->first) );
2421 	}
2422 	std::sort(typeNames.begin(), typeNames.end());
2423 	for(int i=0; i<typeNames.size(); i++) {
2424 		const char* n = typeNames[i].second;
2425 		auto& f = allocInstr[n];
2426 		if(f.maxAllocated > 10000)
2427 			TraceEvent("AllocInstrument").detail("CurrentAlloc", f.allocCount-f.deallocCount)
2428 				.detail("Name", typeNames[i].first.c_str());
2429 	}
2430 
2431 	std::unordered_map<uint32_t, BackTraceAccount> traceCounts;
2432 	size_t memSampleSize;
2433 	memSample_entered = true;
2434 	{
2435 		ThreadSpinLockHolder holder( memLock );
2436 		traceCounts = backTraceLookup;
2437 		memSampleSize = memSample.size();
2438 	}
2439 	memSample_entered = false;
2440 
2441 	TraceEvent("MemSampleSummary")
2442 		.detail("InverseByteSampleRatio", SAMPLE_BYTES)
2443 		.detail("MemorySamples", memSampleSize)
2444 		.detail("BackTraces", traceCounts.size());
2445 
2446 	for( auto i = traceCounts.begin(); i != traceCounts.end(); ++i ) {
2447 		char buf[1024];
2448 		std::vector<void *> *frames = i->second.backTrace;
2449 		std::string backTraceStr;
2450 #if defined(_WIN32)
2451 		for (int j = 1; j < frames->size(); j++) {
2452 			_snprintf(buf, 1024, "%p ", frames->at(j));
2453 			backTraceStr += buf;
2454 		}
2455 #else
2456 		backTraceStr = format_backtrace(&(*frames)[0], frames->size());
2457 #endif
2458 		TraceEvent("MemSample")
2459 			.detail("Count", (int64_t)i->second.count)
2460 			.detail("TotalSize", i->second.totalSize)
2461 			.detail("SampleCount", i->second.sampleCount)
2462 			.detail("Hash", format("%lld", i->first))
2463 			.detail("Bt", backTraceStr);
2464 	}
2465 
2466 	TraceEvent("MemSample")
2467 		.detail("Count", traceCounts.size())
2468 		.detail("TotalSize", traceCounts.size() * ((int)(sizeof(uint32_t) + sizeof(size_t) + sizeof(size_t))))
2469 		.detail("SampleCount", traceCounts.size())
2470 		.detail("Hash", "backTraces")
2471 		.detail("Bt", "na");
2472 
2473 	TraceEvent("MemSample")
2474 		.detail("Count", memSampleSize)
2475 		.detail("TotalSize", memSampleSize * ((int)(sizeof(void*) + sizeof(uint32_t) + sizeof(size_t))))
2476 		.detail("SapmleCount", memSampleSize)
2477 		.detail("Hash", "memSamples")
2478 		.detail("Bt", "na");
2479 	TRACEALLOCATOR(16);
2480 	TRACEALLOCATOR(32);
2481 	TRACEALLOCATOR(64);
2482 	TRACEALLOCATOR(128);
2483 	TRACEALLOCATOR(256);
2484 	TRACEALLOCATOR(512);
2485 	TRACEALLOCATOR(1024);
2486 	TRACEALLOCATOR(2048);
2487 	TRACEALLOCATOR(4096);
2488 	TRACEALLOCATOR(8192);
2489 	g_traceBatch.dump();
2490 #endif
2491 
2492 	criticalError(FDB_EXIT_NO_MEM, "OutOfMemory", "Out of memory");
2493 }
2494 }; // namespace platform
2495 
criticalError(int exitCode,const char * type,const char * message)2496 extern "C" void criticalError(int exitCode, const char *type, const char *message) {
2497 	// Be careful!  This function may be called asynchronously from a thread or in other weird conditions
2498 
2499 	fprintf(stderr, "ERROR: %s\n", message);
2500 
2501 	if (g_network && !g_network->isSimulated())
2502 	{
2503 		TraceEvent ev(SevError, type);
2504 		ev.detail("Message", message);
2505 	}
2506 
2507 	flushAndExit(exitCode);
2508 }
2509 
2510 extern void flushTraceFileVoid();
2511 
flushAndExit(int exitCode)2512 extern "C" void flushAndExit(int exitCode) {
2513 	flushTraceFileVoid();
2514 	fflush(stdout);
2515 	closeTraceFile();
2516 #ifdef _WIN32
2517 	// This function is documented as being asynchronous, but we suspect it might actually be synchronous in the
2518 	// case that it is passed a handle to the current process. If not, then there may be cases where we escalate
2519 	// to the crashAndDie call below.
2520 	TerminateProcess(GetCurrentProcess(), exitCode);
2521 #else
2522 	_exit(exitCode);
2523 #endif
2524 	// should never reach here, but you never know
2525 	crashAndDie();
2526 }
2527 
2528 #ifdef __unixish__
2529 #include <dlfcn.h>
2530 
2531 #ifdef __linux__
2532 #include <link.h>
2533 #endif
2534 
2535 struct ImageInfo {
2536 	void *offset;
2537 	std::string symbolFileName;
2538 
ImageInfoImageInfo2539 	ImageInfo() : offset(NULL), symbolFileName("") {}
2540 };
2541 
getImageInfo(const void * symbol)2542 ImageInfo getImageInfo(const void *symbol) {
2543 	Dl_info info;
2544 	ImageInfo imageInfo;
2545 
2546 #ifdef __linux__
2547 	link_map *linkMap;
2548 	int res = dladdr1(symbol, &info, (void**)&linkMap, RTLD_DL_LINKMAP);
2549 #else
2550 	int res = dladdr(symbol, &info);
2551 #endif
2552 
2553 	if(res != 0) {
2554 		std::string imageFile = basename(info.dli_fname);
2555 		// If we have a client library that doesn't end in the appropriate extension, we will get the wrong debug suffix. This should only be a cosmetic problem, though.
2556 #ifdef __linux__
2557 		imageInfo.offset = (void*)linkMap->l_addr;
2558 		if(imageFile.length() >= 3 && imageFile.rfind(".so") == imageFile.length()-3) {
2559 #else
2560 		imageInfo.offset = info.dli_fbase;
2561 		if(imageFile.length() >= 6 && imageFile.rfind(".dylib") == imageFile.length()-6) {
2562 #endif
2563 			imageInfo.symbolFileName = imageFile + "-debug";
2564 		}
2565 		else {
2566 			imageInfo.symbolFileName = imageFile + ".debug";
2567 		}
2568 	}
2569 	else {
2570 		imageInfo.symbolFileName = "unknown";
2571 	}
2572 
2573 	return imageInfo;
2574 }
2575 
2576 ImageInfo getCachedImageInfo() {
2577 	// The use of "getCachedImageInfo" is arbitrary and was a best guess at a good way to get the image of the
2578 	//  most likely candidate for the "real" flow library or binary
2579 	static ImageInfo info = getImageInfo((const void *)&getCachedImageInfo);
2580 	return info;
2581 }
2582 
2583 #include <execinfo.h>
2584 
2585 namespace platform {
2586 void* getImageOffset() {
2587 	return getCachedImageInfo().offset;
2588 }
2589 
2590 size_t raw_backtrace(void** addresses, int maxStackDepth) {
2591 #if !defined(__APPLE__)
2592 	// absl::GetStackTrace doesn't have an implementation for MacOS.
2593 	return absl::GetStackTrace(addresses, maxStackDepth, 0);
2594 #else
2595 	return backtrace(addresses, maxStackDepth);
2596 #endif
2597 }
2598 
2599 std::string format_backtrace(void **addresses, int numAddresses) {
2600 	ImageInfo const& imageInfo = getCachedImageInfo();
2601 #ifdef __APPLE__
2602 	std::string s = format("atos -o %s -arch x86_64 -l %p", imageInfo.symbolFileName.c_str(), imageInfo.offset);
2603 	for(int i = 1; i < numAddresses; i++) {
2604 		s += format(" %p", addresses[i]);
2605 	}
2606 #else
2607 	std::string s = format("addr2line -e %s -p -C -f -i", imageInfo.symbolFileName.c_str());
2608 	for(int i = 1; i < numAddresses; i++) {
2609 		s += format(" %p", (char*)addresses[i]-(char*)imageInfo.offset);
2610 	}
2611 #endif
2612 	return s;
2613 }
2614 
2615 std::string get_backtrace() {
2616 	void *addresses[50];
2617 	size_t size = raw_backtrace(addresses, 50);
2618 	return format_backtrace(addresses, size);
2619 }
2620 }; // namespace platform
2621 #else
2622 
2623 namespace platform {
2624 std::string get_backtrace() { return std::string(); }
2625 std::string format_backtrace(void **addresses, int numAddresses) { return std::string(); }
2626 void* getImageOffset() { return NULL; }
2627 }; // namespace platform
2628 #endif
2629 
2630 bool isLibraryLoaded(const char* lib_path) {
2631 #if !defined(__linux__) && !defined(__APPLE__) && !defined(_WIN32)
2632 #error Port me!
2633 #endif
2634 
2635 	void* dlobj = NULL;
2636 
2637 #if defined(__unixish__)
2638 	dlobj = dlopen( lib_path, RTLD_NOLOAD | RTLD_LAZY );
2639 #else
2640 	dlobj = GetModuleHandle( lib_path );
2641 #endif
2642 
2643 	return dlobj != NULL;
2644 }
2645 
2646 void* loadLibrary(const char* lib_path) {
2647 #if !defined(__linux__) && !defined(__APPLE__) && !defined(_WIN32)
2648 #error Port me!
2649 #endif
2650 
2651 	void* dlobj = NULL;
2652 
2653 #if defined(__unixish__)
2654 	dlobj = dlopen( lib_path, RTLD_LAZY | RTLD_LOCAL );
2655 	if(dlobj == NULL) {
2656 		TraceEvent(SevWarn, "LoadLibraryFailed").detail("Library", lib_path).detail("Error", dlerror());
2657 	}
2658 #else
2659 	dlobj = LoadLibrary( lib_path );
2660 	if(dlobj == NULL) {
2661 		TraceEvent(SevWarn, "LoadLibraryFailed").detail("Library", lib_path).GetLastError();
2662 	}
2663 #endif
2664 
2665 	return dlobj;
2666 }
2667 
2668 void* loadFunction(void* lib, const char* func_name) {
2669 	void* dlfcn = NULL;
2670 
2671 #if defined(__unixish__)
2672 	dlfcn = dlsym( lib, func_name );
2673 	if(dlfcn == NULL) {
2674 		TraceEvent(SevWarn, "LoadFunctionFailed").detail("Function", func_name).detail("Error", dlerror());
2675 	}
2676 #else
2677 	dlfcn = GetProcAddress( (HINSTANCE)lib, func_name );
2678 	if(dlfcn == NULL) {
2679 		TraceEvent(SevWarn, "LoadFunctionFailed").detail("Function", func_name).GetLastError();
2680 	}
2681 #endif
2682 
2683 	return dlfcn;
2684 }
2685 
2686 void platformInit() {
2687 #ifdef WIN32
2688 	_set_FMA3_enable(0); // Workaround for VS 2013 code generation bug. See https://connect.microsoft.com/VisualStudio/feedback/details/811093/visual-studio-2013-rtm-c-x64-code-generation-bug-for-avx2-instructions
2689 #endif
2690 #ifdef __linux__
2691 	struct timespec ts;
2692 	if (clock_gettime(CLOCK_MONOTONIC, &ts) != 0) {
2693 		criticalError(FDB_EXIT_ERROR, "MonotonicTimeUnavailable", "clock_gettime(CLOCK_MONOTONIC, ...) returned an error. Check your kernel and glibc versions.");
2694 	}
2695 #endif
2696 }
2697 
2698 void crashHandler(int sig) {
2699 #ifdef __linux__
2700 	// Pretty much all of this handler is risking undefined behavior and hangs,
2701 	//  but the idea is that we're about to crash anyway...
2702 	std::string backtrace = platform::get_backtrace();
2703 
2704 	bool error = (sig != SIGUSR2);
2705 
2706 	fflush(stdout);
2707 	TraceEvent(error ? SevError : SevInfo, error ? "Crash" : "ProcessTerminated")
2708 		.detail("Signal", sig)
2709 		.detail("Name", strsignal(sig))
2710 		.detail("Trace", backtrace);
2711 	flushTraceFileVoid();
2712 
2713 	fprintf(stderr, "SIGNAL: %s (%d)\n", strsignal(sig), sig);
2714 	fprintf(stderr, "Trace: %s\n", backtrace.c_str());
2715 
2716 	_exit(128 + sig);
2717 #else
2718 	// No crash handler for other platforms!
2719 #endif
2720 }
2721 
2722 void registerCrashHandler() {
2723 #ifdef __linux__
2724 	// For these otherwise fatal errors, attempt to log a trace of
2725 	// what was happening and then exit
2726 	struct sigaction action;
2727 	action.sa_handler = crashHandler;
2728 	sigfillset( &action.sa_mask );
2729 	action.sa_flags = 0;
2730 
2731 	sigaction(SIGILL, &action, NULL);
2732 	sigaction(SIGFPE, &action, NULL);
2733 	sigaction(SIGSEGV, &action, NULL);
2734 	sigaction(SIGBUS, &action, NULL);
2735 	sigaction(SIGUSR2, &action, NULL);
2736 #else
2737 	// No crash handler for other platforms!
2738 #endif
2739 }
2740 
2741 #ifdef __linux__
2742 extern volatile void** net2backtraces;
2743 extern volatile size_t net2backtraces_offset;
2744 extern volatile size_t net2backtraces_max;
2745 extern volatile bool net2backtraces_overflow;
2746 extern volatile int net2backtraces_count;
2747 extern volatile double net2liveness;
2748 extern volatile thread_local int profilingEnabled;
2749 extern void initProfiling();
2750 
2751 volatile thread_local bool profileThread = false;
2752 #endif
2753 
2754 volatile thread_local int profilingEnabled = 1;
2755 
2756 void setProfilingEnabled(int enabled) {
2757 	profilingEnabled = enabled;
2758 }
2759 
2760 void profileHandler(int sig) {
2761 #ifdef __linux__
2762 	if (!profileThread || !profilingEnabled) {
2763 		return;
2764 	}
2765 
2766 	net2backtraces_count++;
2767 	if (!net2backtraces || net2backtraces_max - net2backtraces_offset < 50) {
2768 		net2backtraces_overflow = true;
2769 		return;
2770 	}
2771 
2772 	// We are casting away the volatile-ness of the backtrace array, but we believe that should be reasonably safe in the signal handler
2773 	ProfilingSample* ps = const_cast<ProfilingSample*>((volatile ProfilingSample*)(net2backtraces + net2backtraces_offset));
2774 
2775 	ps->timestamp = timer();
2776 
2777 	// SOMEDAY: should we limit the maximum number of frames from
2778 	// backtrace beyond just available space?
2779 	size_t size = backtrace(ps->frames, net2backtraces_max - net2backtraces_offset - 2);
2780 
2781 	ps->length = size;
2782 
2783 	net2backtraces_offset += size + 2;
2784 #else
2785 	// No slow task profiling for other platforms!
2786 #endif
2787 }
2788 
2789 void* checkThread(void *arg) {
2790 #ifdef __linux__
2791 	pthread_t mainThread = *(pthread_t*)arg;
2792 	free(arg);
2793 
2794 	double lastValue = net2liveness;
2795 	double lastSignal = 0;
2796 	double logInterval = FLOW_KNOBS->SLOWTASK_PROFILING_INTERVAL;
2797 	while(true) {
2798 		threadSleep(FLOW_KNOBS->SLOWTASK_PROFILING_INTERVAL);
2799 		if(lastValue == net2liveness) {
2800 			double t = timer();
2801 			if(lastSignal == 0 || t - lastSignal >= logInterval) {
2802 				if(lastSignal > 0) {
2803 					logInterval = std::min(FLOW_KNOBS->SLOWTASK_PROFILING_MAX_LOG_INTERVAL, FLOW_KNOBS->SLOWTASK_PROFILING_LOG_BACKOFF * logInterval);
2804 				}
2805 
2806 				lastSignal = t;
2807 				pthread_kill(mainThread, SIGPROF);
2808 			}
2809 		}
2810 		else {
2811 			lastSignal = 0;
2812 			logInterval = FLOW_KNOBS->SLOWTASK_PROFILING_INTERVAL;
2813 		}
2814 		lastValue = net2liveness;
2815 	}
2816 	return NULL;
2817 #else
2818 	// No slow task profiling for other platforms!
2819 	return NULL;
2820 #endif
2821 }
2822 
2823 void setupSlowTaskProfiler() {
2824 #ifdef __linux__
2825 	if(FLOW_KNOBS->SLOWTASK_PROFILING_INTERVAL > 0) {
2826 		TraceEvent("StartingSlowTaskProfilingThread").detail("Interval", FLOW_KNOBS->SLOWTASK_PROFILING_INTERVAL);
2827 		initProfiling();
2828 		profileThread = true;
2829 
2830 		struct sigaction action;
2831 		action.sa_handler = profileHandler;
2832 		sigfillset(&action.sa_mask);
2833 		action.sa_flags = 0;
2834 		sigaction(SIGPROF, &action, NULL);
2835 
2836 		// Start a thread which will use signals to log stacks on long events
2837 		pthread_t *mainThread = (pthread_t*)malloc(sizeof(pthread_t));
2838 		*mainThread = pthread_self();
2839 		startThread(&checkThread, (void*)mainThread);
2840 	}
2841 #else
2842 	// No slow task profiling for other platforms!
2843 #endif
2844 }
2845 
2846 #ifdef __linux__
2847 // There's no good place to put this, so it's here.
2848 // Ubuntu's packaging of libstdc++_pic offers different symbols than libstdc++.  Go figure.
2849 // Notably, it's missing a definition of std::istream::ignore(long), which causes compilation errors
2850 // in the bindings.  Thus, we provide weak versions of their definitions, so that if the
2851 // linked-against libstdc++ is missing their definitions, we'll be able to use the provided
2852 // ignore(long, int) version.
2853 #include <istream>
2854 namespace std {
2855 typedef basic_istream<char, std::char_traits<char>> char_basic_istream;
2856 template <>
2857 char_basic_istream& __attribute__((weak)) char_basic_istream::ignore(streamsize count) {
2858   return ignore(count, std::char_traits<char>::eof());
2859 }
2860 typedef basic_istream<wchar_t, std::char_traits<wchar_t>> wchar_basic_istream;
2861 template <>
2862 wchar_basic_istream& __attribute__((weak)) wchar_basic_istream::ignore(streamsize count) {
2863   return ignore(count, std::char_traits<wchar_t>::eof());
2864 }
2865 }
2866 #endif
2867 
2868 // UnitTest for getMemoryInfo
2869 #ifdef __linux__
2870 TEST_CASE("/flow/Platform/getMemoryInfo") {
2871 
2872 	printf("UnitTest flow/Platform/getMemoryInfo 1\n");
2873 	std::string memString =
2874 		"MemTotal:       24733228 kB\n"
2875 		"MemFree:         2077580 kB\n"
2876 		"Buffers:          266940 kB\n"
2877 		"Cached:         16798292 kB\n"
2878 		"SwapCached:       210240 kB\n"
2879 		"Active:         12447724 kB\n"
2880 		"Inactive:        9175508 kB\n"
2881 		"Active(anon):    3458596 kB\n"
2882 		"Inactive(anon):  1102948 kB\n"
2883 		"Active(file):    8989128 kB\n"
2884 		"Inactive(file):  8072560 kB\n"
2885 		"Unevictable:           0 kB\n"
2886 		"Mlocked:               0 kB\n"
2887 		"SwapTotal:      25165820 kB\n"
2888 		"SwapFree:       23680228 kB\n"
2889 		"Dirty:               200 kB\n"
2890 		"Writeback:             0 kB\n"
2891 		"AnonPages:       4415148 kB\n"
2892 		"Mapped:            62804 kB\n"
2893 		"Shmem:              3544 kB\n"
2894 		"Slab:             620144 kB\n"
2895 		"SReclaimable:     556640 kB\n"
2896 		"SUnreclaim:        63504 kB\n"
2897 		"KernelStack:        5240 kB\n"
2898 		"PageTables:        47292 kB\n"
2899 		"NFS_Unstable:          0 kB\n"
2900 		"Bounce:                0 kB\n"
2901 		"WritebackTmp:          0 kB\n"
2902 		"CommitLimit:    37532432 kB\n"
2903 		"Committed_AS:    8603484 kB\n"
2904 		"VmallocTotal:   34359738367 kB\n"
2905 		"VmallocUsed:      410576 kB\n";
2906 
2907 	std::map<StringRef, int64_t> request = {
2908 		{ LiteralStringRef("MemTotal:"), 0 },
2909 		{ LiteralStringRef("MemFree:"), 0 },
2910 		{ LiteralStringRef("MemAvailable:"), 0 },
2911 		{ LiteralStringRef("Buffers:"), 0 },
2912 		{ LiteralStringRef("Cached:"), 0 },
2913 		{ LiteralStringRef("SwapTotal:"), 0 },
2914 		{ LiteralStringRef("SwapFree:"), 0 },
2915 	};
2916 
2917 	std::stringstream memInfoStream(memString);
2918 	getMemoryInfo(request, memInfoStream);
2919 	ASSERT(request[LiteralStringRef("MemTotal:")] == 24733228);
2920 	ASSERT(request[LiteralStringRef("MemFree:")] == 2077580);
2921 	ASSERT(request[LiteralStringRef("MemAvailable:")] == 0);
2922 	ASSERT(request[LiteralStringRef("Buffers:")] == 266940);
2923 	ASSERT(request[LiteralStringRef("Cached:")] == 16798292);
2924 	ASSERT(request[LiteralStringRef("SwapTotal:")] == 25165820);
2925 	ASSERT(request[LiteralStringRef("SwapFree:")] == 23680228);
2926 	for (auto & item : request) {
2927 		printf("%s:%ld\n", item.first.toString().c_str(), item.second );
2928 	}
2929 
2930 	printf("UnitTest flow/Platform/getMemoryInfo 2\n");
2931 	std::string memString1 =
2932 		"Slab:             192816 kB\n"
2933 		"SReclaimable:     158404 kB\n"
2934 		"SUnreclaim:        34412 kB\n"
2935 		"KernelStack:        7152 kB\n"
2936 		"PageTables:        45284 kB\n"
2937 		"NFS_Unstable:          0 kB\n"
2938 		"Bounce:                0 kB\n"
2939 		"WritebackTmp:          0 kB\n"
2940 		"MemTotal:       31856496 kB\n"
2941 		"MemFree:        25492716 kB\n"
2942 		"MemAvailable:   28470756 kB\n"
2943 		"Buffers:          313644 kB\n"
2944 		"Cached:          2956444 kB\n"
2945 		"SwapCached:            0 kB\n"
2946 		"Active:          3708432 kB\n"
2947 		"Inactive:        2163752 kB\n"
2948 		"Active(anon):    2604524 kB\n"
2949 		"Inactive(anon):   199896 kB\n"
2950 		"Active(file):    1103908 kB\n"
2951 		"Inactive(file):  1963856 kB\n"
2952 		"Unevictable:           0 kB\n"
2953 		"Mlocked:               0 kB\n"
2954 		"SwapTotal:             0 kB\n"
2955 		"SwapFree:              0 kB\n"
2956 		"Dirty:                 0 kB\n"
2957 		"Writeback:             0 kB\n"
2958 		"AnonPages:       2602108 kB\n"
2959 		"Mapped:           361088 kB\n"
2960 		"Shmem:            202332 kB\n"
2961 		"CommitLimit:    15928248 kB\n"
2962 		"Committed_AS:    5556756 kB\n"
2963 		"VmallocTotal:   34359738367 kB\n"
2964 		"VmallocUsed:      427528 kB\n"
2965 		"VmallocChunk:   34359283752 kB\n"
2966 		"HardwareCorrupted:     0 kB\n"
2967 		"AnonHugePages:   1275904 kB\n";
2968 
2969 	std::stringstream memInfoStream1(memString1);
2970 	getMemoryInfo(request, memInfoStream1);
2971 	ASSERT(request[LiteralStringRef("MemTotal:")] == 31856496);
2972 	ASSERT(request[LiteralStringRef("MemFree:")] == 25492716);
2973 	ASSERT(request[LiteralStringRef("MemAvailable:")] == 28470756);
2974 	ASSERT(request[LiteralStringRef("Buffers:")] == 313644);
2975 	ASSERT(request[LiteralStringRef("Cached:")] == 2956444);
2976 	ASSERT(request[LiteralStringRef("SwapTotal:")] == 0);
2977 	ASSERT(request[LiteralStringRef("SwapFree:")] == 0);
2978 	for (auto & item : request) {
2979 		printf("%s:%ld\n", item.first.toString().c_str(), item.second);
2980 	}
2981 
2982 	return Void();
2983 }
2984 #endif
2985 
2986 int testPathFunction(const char *name, std::function<std::string(std::string)> fun, std::string a, ErrorOr<std::string> b) {
2987 	ErrorOr<std::string> result;
2988 	try { result  = fun(a); } catch(Error &e) { result = e; }
2989 	bool r = result.isError() == b.isError() && (b.isError() || b.get() == result.get()) && (!b.isError() || b.getError().code() == result.getError().code());
2990 
2991 	printf("%s: %s('%s') -> %s", r ? "PASS" : "FAIL", name, a.c_str(), result.isError() ? result.getError().what() : format("'%s'", result.get().c_str()).c_str());
2992 	if(!r) {
2993 		printf("  *ERROR* expected %s", b.isError() ? b.getError().what() : format("'%s'", b.get().c_str()).c_str());
2994 	}
2995 	printf("\n");
2996 	return r ? 0 : 1;
2997 }
2998 
2999 int testPathFunction2(const char *name, std::function<std::string(std::string, bool, bool)> fun, std::string a, bool resolveLinks, bool mustExist, ErrorOr<std::string> b) {
3000 	// Skip tests with resolveLinks set to false as the implementation is not complete
3001 	if(resolveLinks == false) {
3002 		printf("SKIPPED: %s('%s', %d, %d)\n", name, a.c_str(), resolveLinks, mustExist);
3003 		return 0;
3004 	}
3005 
3006 	ErrorOr<std::string> result;
3007 	try { result  = fun(a, resolveLinks, mustExist); } catch(Error &e) { result = e; }
3008 	bool r = result.isError() == b.isError() && (b.isError() || b.get() == result.get()) && (!b.isError() || b.getError().code() == result.getError().code());
3009 
3010 	printf("%s: %s('%s', %d, %d) -> %s", r ? "PASS" : "FAIL", name, a.c_str(), resolveLinks, mustExist, result.isError() ? result.getError().what() : format("'%s'", result.get().c_str()).c_str());
3011 	if(!r) {
3012 		printf("  *ERROR* expected %s", b.isError() ? b.getError().what() : format("'%s'", b.get().c_str()).c_str());
3013 	}
3014 	printf("\n");
3015 	return r ? 0 : 1;
3016 }
3017 
3018 TEST_CASE("/flow/Platform/directoryOps") {
3019 	int errors = 0;
3020 
3021 	errors += testPathFunction("popPath", popPath, "a", "");
3022 	errors += testPathFunction("popPath", popPath, "a/", "");
3023 	errors += testPathFunction("popPath", popPath, "a///", "");
3024 	errors += testPathFunction("popPath", popPath, "a///..", "a/");
3025 	errors += testPathFunction("popPath", popPath, "a///../", "a/");
3026 	errors += testPathFunction("popPath", popPath, "a///..//", "a/");
3027 	errors += testPathFunction("popPath", popPath, "/", "/");
3028 	errors += testPathFunction("popPath", popPath, "/a", "/");
3029 	errors += testPathFunction("popPath", popPath, "/a/b", "/a/");
3030 	errors += testPathFunction("popPath", popPath, "/a/b/", "/a/");
3031 	errors += testPathFunction("popPath", popPath, "/a/b/..", "/a/b/");
3032 	errors += testPathFunction("popPath", popPath, "/a/b///..//", "/a/b/");
3033 
3034 	errors += testPathFunction("cleanPath", cleanPath, "/", "/");
3035 	errors += testPathFunction("cleanPath", cleanPath, "///.///", "/");
3036 	errors += testPathFunction("cleanPath", cleanPath, "/a/b/.././../c/./././////./d/..//", "/c");
3037 	errors += testPathFunction("cleanPath", cleanPath, "a/b/.././../c/./././////./d/..//", "c");
3038 	errors += testPathFunction("cleanPath", cleanPath, "..", "..");
3039 	errors += testPathFunction("cleanPath", cleanPath, "../.././", "../..");
3040 	errors += testPathFunction("cleanPath", cleanPath, "../a/b/..//", "../a");
3041 	errors += testPathFunction("cleanPath", cleanPath, "a/b/.././../c/./././////./d/..//..", ".");
3042 	errors += testPathFunction("cleanPath", cleanPath, "/..", "/");
3043 	errors += testPathFunction("cleanPath", cleanPath, "/../foo/bar///", "/foo/bar");
3044 	errors += testPathFunction("cleanPath", cleanPath, "/a/b/../.././../", "/");
3045 	errors += testPathFunction("cleanPath", cleanPath, ".", ".");
3046 
3047 	// Creating this directory in backups avoids some sanity checks
3048 	platform::createDirectory("simfdb/backups/one/two/three");
3049 	std::string cwd = platform::getWorkingDirectory();
3050 
3051 #ifndef _WIN32
3052 	// Create some symlinks and test resolution (or non-resolution) of them
3053 	ASSERT(symlink("one/two", "simfdb/backups/four") == 0);
3054 	ASSERT(symlink("../backups/four", "simfdb/backups/five") == 0);
3055 
3056 	errors += testPathFunction2("abspath", abspath, "simfdb/backups/four/../two", true, true, joinPath(cwd, "simfdb/backups/one/two"));
3057 	errors += testPathFunction2("abspath", abspath, "simfdb/backups/five/../two", true, true, joinPath(cwd, "simfdb/backups/one/two"));
3058 	errors += testPathFunction2("abspath", abspath, "simfdb/backups/five/../two", true, false, joinPath(cwd, "simfdb/backups/one/two"));
3059 	errors += testPathFunction2("abspath", abspath, "simfdb/backups/five/../three", true, true, platform_error());
3060 	errors += testPathFunction2("abspath", abspath, "simfdb/backups/five/../three", true, false, joinPath(cwd, "simfdb/backups/one/three"));
3061 	errors += testPathFunction2("abspath", abspath, "simfdb/backups/five/../three/../four", true, false, joinPath(cwd, "simfdb/backups/one/four"));
3062 
3063 	errors += testPathFunction2("parentDirectory", parentDirectory, "simfdb/backups/four/../two", true, true, joinPath(cwd, "simfdb/backups/one/"));
3064 	errors += testPathFunction2("parentDirectory", parentDirectory, "simfdb/backups/five/../two", true, true, joinPath(cwd, "simfdb/backups/one/"));
3065 	errors += testPathFunction2("parentDirectory", parentDirectory, "simfdb/backups/five/../two", true, false, joinPath(cwd, "simfdb/backups/one/"));
3066 	errors += testPathFunction2("parentDirectory", parentDirectory, "simfdb/backups/five/../three", true, true, platform_error());
3067 	errors += testPathFunction2("parentDirectory", parentDirectory, "simfdb/backups/five/../three", true, false, joinPath(cwd, "simfdb/backups/one/"));
3068 	errors += testPathFunction2("parentDirectory", parentDirectory, "simfdb/backups/five/../three/../four", true, false, joinPath(cwd, "simfdb/backups/one/"));
3069 #endif
3070 
3071 	errors += testPathFunction2("abspath", abspath, "/", false, false, "/");
3072 	errors += testPathFunction2("abspath", abspath, "/foo//bar//baz/.././", false, false, "/foo/bar");
3073 	errors += testPathFunction2("abspath", abspath, "/", true, false, "/");
3074 	errors += testPathFunction2("abspath", abspath, "", true, false, platform_error());
3075 	errors += testPathFunction2("abspath", abspath, ".", true, false, cwd);
3076 	errors += testPathFunction2("abspath", abspath, "/a", true, false, "/a");
3077 	errors += testPathFunction2("abspath", abspath, "one/two/three/four", false, true, platform_error());
3078 	errors += testPathFunction2("abspath", abspath, "one/two/three/four", false, false, joinPath(cwd, "one/two/three/four"));
3079 	errors += testPathFunction2("abspath", abspath, "one/two/three/./four", false, false, joinPath(cwd, "one/two/three/four"));
3080 	errors += testPathFunction2("abspath", abspath, "one/two/three/./four", false, false, joinPath(cwd, "one/two/three/four"));
3081 	errors += testPathFunction2("abspath", abspath, "one/two/three/./four/..", false, false, joinPath(cwd, "one/two/three"));
3082 	errors += testPathFunction2("abspath", abspath, "one/./two/../three/./four", false, false, joinPath(cwd, "one/three/four"));
3083 	errors += testPathFunction2("abspath", abspath, "one/./two/../three/./four", false, true, platform_error());
3084 	errors += testPathFunction2("abspath", abspath, "one/two/three/./four", false, true, platform_error());
3085 	errors += testPathFunction2("abspath", abspath, "simfdb/backups/one/two/three", false, true, joinPath(cwd, "simfdb/backups/one/two/three"));
3086 	errors += testPathFunction2("abspath", abspath, "simfdb/backups/one/two/threefoo", false, true, platform_error());
3087 	errors += testPathFunction2("abspath", abspath, "simfdb/backups/four/../two", false, false, joinPath(cwd, "simfdb/backups/two"));
3088 	errors += testPathFunction2("abspath", abspath, "simfdb/backups/four/../two", false, true, platform_error());
3089 	errors += testPathFunction2("abspath", abspath, "simfdb/backups/five/../two", false, true, platform_error());
3090 	errors += testPathFunction2("abspath", abspath, "simfdb/backups/five/../two", false, false, joinPath(cwd, "simfdb/backups/two"));
3091 	errors += testPathFunction2("abspath", abspath, "foo/./../foo2/./bar//", false, false, joinPath(cwd, "foo2/bar"));
3092 	errors += testPathFunction2("abspath", abspath, "foo/./../foo2/./bar//", false, true, platform_error());
3093 	errors += testPathFunction2("abspath", abspath, "foo/./../foo2/./bar//", true, false, joinPath(cwd, "foo2/bar"));
3094 	errors += testPathFunction2("abspath", abspath, "foo/./../foo2/./bar//", true, true, platform_error());
3095 
3096 	errors += testPathFunction2("parentDirectory", parentDirectory, "", true, false, platform_error());
3097 	errors += testPathFunction2("parentDirectory", parentDirectory, "/", true, false, "/");
3098 	errors += testPathFunction2("parentDirectory", parentDirectory, "/a", true, false, "/");
3099 	errors += testPathFunction2("parentDirectory", parentDirectory, ".", false, false, cleanPath(joinPath(cwd, "..")) + "/");
3100 	errors += testPathFunction2("parentDirectory", parentDirectory, "./foo", false, false, cleanPath(cwd) + "/");
3101 	errors += testPathFunction2("parentDirectory", parentDirectory, "one/two/three/four", false, true, platform_error());
3102 	errors += testPathFunction2("parentDirectory", parentDirectory, "one/two/three/four", false, false, joinPath(cwd, "one/two/three/"));
3103 	errors += testPathFunction2("parentDirectory", parentDirectory, "one/two/three/./four", false, false, joinPath(cwd, "one/two/three/"));
3104 	errors += testPathFunction2("parentDirectory", parentDirectory, "one/two/three/./four/..", false, false, joinPath(cwd, "one/two/"));
3105 	errors += testPathFunction2("parentDirectory", parentDirectory, "one/./two/../three/./four", false, false, joinPath(cwd, "one/three/"));
3106 	errors += testPathFunction2("parentDirectory", parentDirectory, "one/./two/../three/./four", false, true, platform_error());
3107 	errors += testPathFunction2("parentDirectory", parentDirectory, "one/two/three/./four", false, true, platform_error());
3108 	errors += testPathFunction2("parentDirectory", parentDirectory, "simfdb/backups/one/two/three", false, true, joinPath(cwd, "simfdb/backups/one/two/"));
3109 	errors += testPathFunction2("parentDirectory", parentDirectory, "simfdb/backups/one/two/threefoo", false, true, platform_error());
3110 	errors += testPathFunction2("parentDirectory", parentDirectory, "simfdb/backups/four/../two", false, false, joinPath(cwd, "simfdb/backups/"));
3111 	errors += testPathFunction2("parentDirectory", parentDirectory, "simfdb/backups/four/../two", false, true, platform_error());
3112 	errors += testPathFunction2("parentDirectory", parentDirectory, "simfdb/backups/five/../two", false, true, platform_error());
3113 	errors += testPathFunction2("parentDirectory", parentDirectory, "simfdb/backups/five/../two", false, false, joinPath(cwd, "simfdb/backups/"));
3114 	errors += testPathFunction2("parentDirectory", parentDirectory, "foo/./../foo2/./bar//", false, false, joinPath(cwd, "foo2/"));
3115 	errors += testPathFunction2("parentDirectory", parentDirectory, "foo/./../foo2/./bar//", false, true, platform_error());
3116 	errors += testPathFunction2("parentDirectory", parentDirectory, "foo/./../foo2/./bar//", true, false, joinPath(cwd, "foo2/"));
3117 	errors += testPathFunction2("parentDirectory", parentDirectory, "foo/./../foo2/./bar//", true, true, platform_error());
3118 
3119 	printf("%d errors.\n", errors);
3120 
3121 	ASSERT(errors == 0);
3122 	return Void();
3123 }
3124