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