You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

986 lines
24KB

  1. #include <arch.hpp>
  2. #include <thread>
  3. #include <regex>
  4. #include <chrono>
  5. #include <cfenv> // for std::fesetround
  6. #include <ghc/filesystem.hpp>
  7. #include <dirent.h>
  8. #include <sys/stat.h>
  9. #include <cxxabi.h> // for abi::__cxa_demangle
  10. #if defined ARCH_LIN || defined ARCH_MAC
  11. #include <pthread.h>
  12. #include <time.h> // for clock_gettime
  13. #include <sched.h>
  14. #include <execinfo.h> // for backtrace and backtrace_symbols
  15. #include <unistd.h> // for execl
  16. #include <sys/utsname.h>
  17. #include <dlfcn.h> // for dladdr
  18. #endif
  19. #if defined ARCH_MAC
  20. #include <mach/mach_init.h>
  21. #include <mach/thread_act.h>
  22. #include <mach/mach_time.h>
  23. #include <sys/sysctl.h>
  24. #endif
  25. #if defined ARCH_WIN
  26. #include <windows.h>
  27. #include <shellapi.h>
  28. #include <processthreadsapi.h>
  29. #include <dbghelp.h>
  30. #endif
  31. #include <simd/common.hpp> // for _mm_getcsr etc
  32. #include <archive.h>
  33. #include <archive_entry.h>
  34. #if defined ARCH_MAC
  35. #include <locale.h>
  36. #endif
  37. #include <system.hpp>
  38. #include <string.hpp>
  39. #include <logger.hpp>
  40. /*
  41. In C++17, this will be `std::filesystem`
  42. Important: When using `fs::path`, always convert strings to/from UTF-8 using
  43. fs::path p = fs::u8path(s);
  44. std::string s = p.generic_u8string();
  45. */
  46. namespace fs = ghc::filesystem;
  47. namespace rack {
  48. namespace system {
  49. std::string join(const std::string& path1, const std::string& path2) {
  50. return (fs::u8path(path1) / fs::u8path(path2)).generic_u8string();
  51. }
  52. static void appendEntries(std::vector<std::string>& entries, const fs::path& dir, int depth) {
  53. try {
  54. for (const auto& entry : fs::directory_iterator(dir)) {
  55. entries.push_back(entry.path().generic_u8string());
  56. // Recurse if depth > 0 (limited recursion) or depth < 0 (infinite recursion).
  57. if (depth != 0) {
  58. if (entry.is_directory()) {
  59. appendEntries(entries, entry.path(), depth - 1);
  60. }
  61. }
  62. }
  63. }
  64. catch (fs::filesystem_error& e) {}
  65. }
  66. std::vector<std::string> getEntries(const std::string& dirPath, int depth) {
  67. std::vector<std::string> entries;
  68. appendEntries(entries, fs::u8path(dirPath), depth);
  69. return entries;
  70. }
  71. bool exists(const std::string& path) {
  72. try {
  73. return fs::exists(fs::u8path(path));
  74. }
  75. catch (fs::filesystem_error& e) {
  76. return false;
  77. }
  78. }
  79. bool isFile(const std::string& path) {
  80. try {
  81. return fs::is_regular_file(fs::u8path(path));
  82. }
  83. catch (fs::filesystem_error& e) {
  84. return false;
  85. }
  86. }
  87. bool isDirectory(const std::string& path) {
  88. try {
  89. return fs::is_directory(fs::u8path(path));
  90. }
  91. catch (fs::filesystem_error& e) {
  92. return false;
  93. }
  94. }
  95. uint64_t getFileSize(const std::string& path) {
  96. try {
  97. return fs::file_size(fs::u8path(path));
  98. }
  99. catch (fs::filesystem_error& e) {
  100. return 0;
  101. }
  102. }
  103. bool rename(const std::string& srcPath, const std::string& destPath) {
  104. try {
  105. fs::rename(fs::u8path(srcPath), fs::u8path(destPath));
  106. return true;
  107. }
  108. catch (fs::filesystem_error& e) {
  109. return false;
  110. }
  111. }
  112. bool copy(const std::string& srcPath, const std::string& destPath) {
  113. try {
  114. fs::copy(fs::u8path(srcPath), fs::u8path(destPath), fs::copy_options::recursive | fs::copy_options::overwrite_existing);
  115. return true;
  116. }
  117. catch (fs::filesystem_error& e) {
  118. return false;
  119. }
  120. }
  121. bool createDirectory(const std::string& path) {
  122. try {
  123. return fs::create_directory(fs::u8path(path));
  124. }
  125. catch (fs::filesystem_error& e) {
  126. return false;
  127. }
  128. }
  129. bool createDirectories(const std::string& path) {
  130. try {
  131. return fs::create_directories(fs::u8path(path));
  132. }
  133. catch (fs::filesystem_error& e) {
  134. return false;
  135. }
  136. }
  137. bool createSymbolicLink(const std::string& target, const std::string& link) {
  138. try {
  139. fs::create_symlink(fs::u8path(target), fs::u8path(link));
  140. return true;
  141. }
  142. catch (fs::filesystem_error& e) {
  143. return false;
  144. }
  145. }
  146. bool remove(const std::string& path) {
  147. try {
  148. return fs::remove(fs::u8path(path));
  149. }
  150. catch (fs::filesystem_error& e) {
  151. return false;
  152. }
  153. }
  154. int removeRecursively(const std::string& pathStr) {
  155. fs::path path = fs::u8path(pathStr);
  156. try {
  157. // Make all entries writable before attempting to remove
  158. for (auto& entry : fs::recursive_directory_iterator(path)) {
  159. fs::permissions(entry.path(), fs::perms::owner_write, fs::perm_options::add);
  160. }
  161. return fs::remove_all(path);
  162. }
  163. catch (fs::filesystem_error& e) {
  164. return 0;
  165. }
  166. }
  167. std::string getWorkingDirectory() {
  168. try {
  169. return fs::current_path().generic_u8string();
  170. }
  171. catch (fs::filesystem_error& e) {
  172. return "";
  173. }
  174. }
  175. void setWorkingDirectory(const std::string& path) {
  176. try {
  177. fs::current_path(fs::u8path(path));
  178. }
  179. catch (fs::filesystem_error& e) {
  180. // Do nothing
  181. }
  182. }
  183. std::string getTempDirectory() {
  184. try {
  185. return fs::temp_directory_path().generic_u8string();
  186. }
  187. catch (fs::filesystem_error& e) {
  188. return "";
  189. }
  190. }
  191. std::string getAbsolute(const std::string& path) {
  192. try {
  193. return fs::absolute(fs::u8path(path)).generic_u8string();
  194. }
  195. catch (fs::filesystem_error& e) {
  196. return "";
  197. }
  198. }
  199. std::string getCanonical(const std::string& path) {
  200. try {
  201. return fs::canonical(fs::u8path(path)).generic_u8string();
  202. }
  203. catch (fs::filesystem_error& e) {
  204. return "";
  205. }
  206. }
  207. std::string getDirectory(const std::string& path) {
  208. try {
  209. return fs::u8path(path).parent_path().generic_u8string();
  210. }
  211. catch (fs::filesystem_error& e) {
  212. return "";
  213. }
  214. }
  215. std::string getFilename(const std::string& path) {
  216. try {
  217. return fs::u8path(path).filename().generic_u8string();
  218. }
  219. catch (fs::filesystem_error& e) {
  220. return "";
  221. }
  222. }
  223. std::string getStem(const std::string& path) {
  224. try {
  225. return fs::u8path(path).stem().generic_u8string();
  226. }
  227. catch (fs::filesystem_error& e) {
  228. return "";
  229. }
  230. }
  231. std::string getExtension(const std::string& path) {
  232. try {
  233. return fs::u8path(path).extension().generic_u8string();
  234. }
  235. catch (fs::filesystem_error& e) {
  236. return "";
  237. }
  238. }
  239. std::vector<uint8_t> readFile(const std::string& path) {
  240. std::vector<uint8_t> data;
  241. FILE* f = std::fopen(path.c_str(), "rb");
  242. if (!f)
  243. throw Exception("Cannot read file %s", path.c_str());
  244. DEFER({
  245. std::fclose(f);
  246. });
  247. // Get file size so we can make a single allocation
  248. std::fseek(f, 0, SEEK_END);
  249. size_t len = std::ftell(f);
  250. std::fseek(f, 0, SEEK_SET);
  251. data.resize(len);
  252. std::fread(data.data(), 1, len, f);
  253. return data;
  254. }
  255. uint8_t* readFile(const std::string& path, size_t* size) {
  256. FILE* f = std::fopen(path.c_str(), "rb");
  257. if (!f)
  258. throw Exception("Cannot read file %s", path.c_str());
  259. DEFER({
  260. std::fclose(f);
  261. });
  262. // Get file size so we can make a single allocation
  263. std::fseek(f, 0, SEEK_END);
  264. size_t len = std::ftell(f);
  265. std::fseek(f, 0, SEEK_SET);
  266. uint8_t* data = (uint8_t*) std::malloc(len);
  267. std::fread(data, 1, len, f);
  268. if (size)
  269. *size = len;
  270. return data;
  271. }
  272. void writeFile(const std::string& path, const std::vector<uint8_t>& data) {
  273. FILE* f = std::fopen(path.c_str(), "wb");
  274. if (!f)
  275. throw Exception("Cannot create file %s", path.c_str());
  276. DEFER({
  277. std::fclose(f);
  278. });
  279. std::fwrite(data.data(), 1, data.size(), f);
  280. }
  281. /** Returns `p` in relative path form, relative to `base`
  282. Limitation: `p` must be a descendant of `base`. Doesn't support adding `../` to the return path.
  283. */
  284. static std::string getRelativePath(std::string path, std::string base) {
  285. try {
  286. path = fs::absolute(fs::u8path(path)).generic_u8string();
  287. base = fs::absolute(fs::u8path(base)).generic_u8string();
  288. }
  289. catch (fs::filesystem_error& e) {
  290. throw Exception("%s", e.what());
  291. }
  292. if (path.size() < base.size())
  293. throw Exception("getRelativePath() error: path is shorter than base");
  294. if (!std::equal(base.begin(), base.end(), path.begin()))
  295. throw Exception("getRelativePath() error: path does not begin with base");
  296. // If path == base, this correctly returns "."
  297. return "." + std::string(path.begin() + base.size(), path.end());
  298. }
  299. static la_ssize_t archiveWriteVectorCallback(struct archive* a, void* client_data, const void* buffer, size_t length) {
  300. assert(client_data);
  301. std::vector<uint8_t>& data = *((std::vector<uint8_t>*) client_data);
  302. uint8_t* buf = (uint8_t*) buffer;
  303. data.insert(data.end(), buf, buf + length);
  304. return length;
  305. }
  306. static void archiveDirectory(const std::string& archivePath, std::vector<uint8_t>* archiveData, const std::string& dirPath, int compressionLevel) {
  307. // Based on minitar.c create() in libarchive examples
  308. int r;
  309. // Open archive for writing
  310. struct archive* a = archive_write_new();
  311. DEFER({archive_write_free(a);});
  312. // For some reason libarchive adds 10k of padding to archive_write_open() (but not archive_write_open_filename()) unless this is set to 0.
  313. archive_write_set_bytes_per_block(a, 0);
  314. archive_write_set_format_pax_restricted(a);
  315. archive_write_add_filter_zstd(a);
  316. if (!(0 <= compressionLevel && compressionLevel <= 19))
  317. throw Exception("Invalid Zstandard compression level");
  318. r = archive_write_set_filter_option(a, NULL, "compression-level", std::to_string(compressionLevel).c_str());
  319. if (r < ARCHIVE_OK)
  320. throw Exception("Archiver could not set filter option: %s", archive_error_string(a));
  321. if (archiveData) {
  322. // Open vector
  323. archive_write_open(a, (void*) archiveData, NULL, archiveWriteVectorCallback, NULL);
  324. }
  325. else {
  326. // Open file
  327. #if defined ARCH_WIN
  328. r = archive_write_open_filename_w(a, string::UTF8toUTF16(archivePath).c_str());
  329. #else
  330. r = archive_write_open_filename(a, archivePath.c_str());
  331. #endif
  332. if (r < ARCHIVE_OK)
  333. throw Exception("Archiver could not open archive %s for writing: %s", archivePath.c_str(), archive_error_string(a));
  334. }
  335. DEFER({archive_write_close(a);});
  336. // Open dir for reading
  337. struct archive* disk = archive_read_disk_new();
  338. DEFER({archive_read_free(disk);});
  339. #if defined ARCH_WIN
  340. r = archive_read_disk_open_w(disk, string::UTF8toUTF16(dirPath).c_str());
  341. #else
  342. r = archive_read_disk_open(disk, dirPath.c_str());
  343. #endif
  344. if (r < ARCHIVE_OK)
  345. throw Exception("Archiver could not open dir %s for reading: %s", dirPath.c_str(), archive_error_string(a));
  346. DEFER({archive_read_close(a);});
  347. // Iterate dir
  348. for (;;) {
  349. struct archive_entry* entry = archive_entry_new();
  350. DEFER({archive_entry_free(entry);});
  351. r = archive_read_next_header2(disk, entry);
  352. if (r == ARCHIVE_EOF)
  353. break;
  354. if (r < ARCHIVE_OK)
  355. throw Exception("Archiver could not get next entry from archive: %s", archive_error_string(disk));
  356. // Recurse dirs
  357. archive_read_disk_descend(disk);
  358. // Convert absolute path to relative path
  359. std::string entryPath;
  360. #if defined ARCH_WIN
  361. entryPath = string::UTF16toUTF8(archive_entry_pathname_w(entry));
  362. #else
  363. entryPath = archive_entry_pathname(entry);
  364. #endif
  365. entryPath = getRelativePath(entryPath, dirPath);
  366. #if defined ARCH_WIN
  367. // FIXME This doesn't seem to set UTF-8 paths on Windows.
  368. archive_entry_copy_pathname_w(entry, string::UTF8toUTF16(entryPath).c_str());
  369. #else
  370. archive_entry_set_pathname(entry, entryPath.c_str());
  371. #endif
  372. // Set uid and gid to 0 because we don't need to store these.
  373. archive_entry_set_uid(entry, 0);
  374. archive_entry_set_uname(entry, NULL);
  375. archive_entry_set_gid(entry, 0);
  376. archive_entry_set_gname(entry, NULL);
  377. // Write file to archive
  378. r = archive_write_header(a, entry);
  379. if (r < ARCHIVE_OK)
  380. throw Exception("Archiver could not write entry to archive: %s", archive_error_string(a));
  381. // Manually copy data
  382. #if defined ARCH_WIN
  383. std::string entrySourcePath = string::UTF16toUTF8(archive_entry_sourcepath_w(entry));
  384. #else
  385. std::string entrySourcePath = archive_entry_sourcepath(entry);
  386. #endif
  387. FILE* f = std::fopen(entrySourcePath.c_str(), "rb");
  388. DEFER({std::fclose(f);});
  389. char buf[1 << 16];
  390. ssize_t len;
  391. while ((len = std::fread(buf, 1, sizeof(buf), f)) > 0) {
  392. archive_write_data(a, buf, len);
  393. }
  394. }
  395. }
  396. void archiveDirectory(const std::string& archivePath, const std::string& dirPath, int compressionLevel) {
  397. archiveDirectory(archivePath, NULL, dirPath, compressionLevel);
  398. }
  399. std::vector<uint8_t> archiveDirectory(const std::string& dirPath, int compressionLevel) {
  400. std::vector<uint8_t> archiveData;
  401. archiveDirectory("", &archiveData, dirPath, compressionLevel);
  402. return archiveData;
  403. }
  404. struct ArchiveReadVectorData {
  405. const std::vector<uint8_t>* data = NULL;
  406. size_t pos = 0;
  407. };
  408. static la_ssize_t archiveReadVectorCallback(struct archive *a, void* client_data, const void** buffer) {
  409. assert(client_data);
  410. ArchiveReadVectorData* arvd = (ArchiveReadVectorData*) client_data;
  411. assert(arvd->data);
  412. const std::vector<uint8_t>& data = *arvd->data;
  413. *buffer = &data[arvd->pos];
  414. // Read up to some block size of bytes
  415. size_t len = std::min(data.size() - arvd->pos, size_t(1 << 16));
  416. arvd->pos += len;
  417. return len;
  418. }
  419. static void unarchiveToDirectory(const std::string& archivePath, const std::vector<uint8_t>* archiveData, const std::string& dirPathStr) {
  420. #if defined ARCH_MAC
  421. // libarchive depends on locale so set thread locale
  422. // If locale is not found, returns NULL which resets thread to global locale
  423. locale_t loc = newlocale(LC_CTYPE_MASK, "en_US.UTF-8", NULL);
  424. locale_t oldLoc = uselocale(loc);
  425. freelocale(loc);
  426. DEFER({
  427. uselocale(oldLoc);
  428. });
  429. #endif
  430. fs::path dirPath = fs::u8path(dirPathStr);
  431. // Based on minitar.c extract() in libarchive examples
  432. int r;
  433. // Open archive for reading
  434. struct archive* a = archive_read_new();
  435. if (!a)
  436. throw Exception("Unarchiver could not be created");
  437. DEFER({archive_read_free(a);});
  438. archive_read_support_filter_zstd(a);
  439. // archive_read_support_filter_all(a);
  440. archive_read_support_format_tar(a);
  441. // archive_read_support_format_all(a);
  442. ArchiveReadVectorData arvd;
  443. if (archiveData) {
  444. // Open vector
  445. arvd.data = archiveData;
  446. archive_read_open(a, &arvd, NULL, archiveReadVectorCallback, NULL);
  447. }
  448. else {
  449. // Open file
  450. const size_t blockSize = 1 << 16;
  451. #if defined ARCH_WIN
  452. r = archive_read_open_filename_w(a, string::UTF8toUTF16(archivePath).c_str(), blockSize);
  453. #else
  454. r = archive_read_open_filename(a, archivePath.c_str(), blockSize);
  455. #endif
  456. if (r < ARCHIVE_OK)
  457. throw Exception("Unarchiver could not open archive %s: %s", archivePath.c_str(), archive_error_string(a));
  458. }
  459. DEFER({archive_read_close(a);});
  460. // Open dir for writing
  461. struct archive* disk = archive_write_disk_new();
  462. DEFER({archive_write_free(disk);});
  463. // Don't restore timestamps
  464. int flags = 0;
  465. // Delete existing files instead of truncating and rewriting
  466. flags |= ARCHIVE_EXTRACT_UNLINK;
  467. archive_write_disk_set_options(disk, flags);
  468. DEFER({archive_write_close(disk);});
  469. // Iterate archive
  470. for (;;) {
  471. // Get next entry
  472. struct archive_entry* entry;
  473. r = archive_read_next_header(a, &entry);
  474. if (r == ARCHIVE_EOF)
  475. break;
  476. if (r < ARCHIVE_OK)
  477. throw Exception("Unarchiver could not read entry from archive: %s", archive_error_string(a));
  478. // Convert relative pathname to absolute based on dirPath
  479. fs::path entryPath = fs::u8path(archive_entry_pathname(entry));
  480. // DEBUG("entryPath: %s", entryPath.generic_u8string().c_str());
  481. if (!entryPath.is_relative())
  482. throw Exception("Unarchiver does not support absolute tar paths: %s", entryPath.u8string().c_str());
  483. entryPath = dirPath / entryPath;
  484. #if defined ARCH_WIN
  485. archive_entry_copy_pathname_w(entry, string::UTF8toUTF16(entryPath.u8string()).c_str());
  486. #else
  487. archive_entry_set_pathname(entry, entryPath.u8string().c_str());
  488. #endif
  489. mode_t mode = archive_entry_mode(entry);
  490. mode_t filetype = archive_entry_filetype(entry);
  491. int64_t size = archive_entry_size(entry);
  492. // Force minimum modes
  493. if (filetype == AE_IFREG) {
  494. mode |= 0644;
  495. }
  496. else if (filetype == AE_IFDIR) {
  497. mode |= 0755;
  498. }
  499. archive_entry_set_mode(entry, mode);
  500. // Delete zero-byte files
  501. if (filetype == AE_IFREG && size == 0) {
  502. remove(entryPath.generic_u8string());
  503. continue;
  504. }
  505. // Write entry to disk
  506. r = archive_write_header(disk, entry);
  507. if (r < ARCHIVE_OK)
  508. throw Exception("Unarchiver could not write file to dir: %s", archive_error_string(disk));
  509. // Copy data to file
  510. for (;;) {
  511. const void* buf;
  512. size_t size;
  513. int64_t offset;
  514. // Read data from archive
  515. r = archive_read_data_block(a, &buf, &size, &offset);
  516. if (r == ARCHIVE_EOF)
  517. break;
  518. if (r < ARCHIVE_OK)
  519. throw Exception("Unarchiver could not read data from archive: %s", archive_error_string(a));
  520. // Write data to file
  521. r = archive_write_data_block(disk, buf, size, offset);
  522. if (r < ARCHIVE_OK)
  523. throw Exception("Unarchiver could not write data to file: %s", archive_error_string(disk));
  524. }
  525. // Close file
  526. r = archive_write_finish_entry(disk);
  527. if (r < ARCHIVE_OK)
  528. throw Exception("Unarchiver could not close file: %s", archive_error_string(disk));
  529. }
  530. }
  531. void unarchiveToDirectory(const std::string& archivePath, const std::string& dirPath) {
  532. unarchiveToDirectory(archivePath, NULL, dirPath);
  533. }
  534. void unarchiveToDirectory(const std::vector<uint8_t>& archiveData, const std::string& dirPath) {
  535. unarchiveToDirectory("", &archiveData, dirPath);
  536. }
  537. int getLogicalCoreCount() {
  538. return std::thread::hardware_concurrency();
  539. }
  540. void setThreadName(const std::string& name) {
  541. #if defined ARCH_LIN
  542. pthread_setname_np(pthread_self(), name.substr(0, 15).c_str());
  543. #elif defined ARCH_MAC
  544. // Not supported (yet) on Mac
  545. #elif defined ARCH_WIN
  546. // Not supported on Windows
  547. #endif
  548. }
  549. std::string getStackTrace() {
  550. void* stack[128];
  551. int stackLen = LENGTHOF(stack);
  552. std::string s;
  553. #if defined ARCH_LIN || defined ARCH_MAC
  554. stackLen = backtrace(stack, stackLen);
  555. // Skip the first line because it's this function.
  556. for (int i = 1; i < stackLen; i++) {
  557. // Get symbol info from stack address
  558. Dl_info info = {};
  559. if (dladdr(stack[i], &info)) {
  560. // Ignore error
  561. }
  562. // Binary/library filename
  563. s += info.dli_fname ? info.dli_fname : "??";
  564. s += ": ";
  565. // Attempt to demangle C++ symbol name
  566. if (info.dli_sname) {
  567. char* demangled = abi::__cxa_demangle(info.dli_sname, NULL, NULL, NULL);
  568. if (demangled) {
  569. s += demangled;
  570. free(demangled);
  571. }
  572. else {
  573. s += info.dli_sname;
  574. }
  575. }
  576. else {
  577. s += "??";
  578. }
  579. s += " +";
  580. ptrdiff_t offset = (char*) stack[i] - (char*) info.dli_saddr;
  581. s += string::f("0x%x", offset);
  582. s += "\n";
  583. }
  584. #elif defined ARCH_WIN
  585. HANDLE process = GetCurrentProcess();
  586. SymInitialize(process, NULL, true);
  587. stackLen = CaptureStackBackTrace(0, stackLen, stack, NULL);
  588. SYMBOL_INFO* symbol = (SYMBOL_INFO*) calloc(sizeof(SYMBOL_INFO) + 256, 1);
  589. symbol->MaxNameLen = 255;
  590. symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
  591. for (int i = 1; i < stackLen; i++) {
  592. SymFromAddr(process, (DWORD64) stack[i], 0, symbol);
  593. s += string::f("%d: %s 0x%" PRIx64 "\n", stackLen - i - 1, symbol->Name, symbol->Address);
  594. }
  595. free(symbol);
  596. #endif
  597. return s;
  598. }
  599. #if defined ARCH_WIN
  600. static int64_t startCounter = 0;
  601. static double counterTime = 0.0;
  602. #endif
  603. #if defined ARCH_LIN
  604. static int64_t startTime = 0;
  605. #endif
  606. #if defined ARCH_MAC
  607. static int64_t startCounter = 0;
  608. static double counterTime = 0.0;
  609. #endif
  610. static void initTime() {
  611. #if defined ARCH_WIN
  612. LARGE_INTEGER counter;
  613. QueryPerformanceCounter(&counter);
  614. startCounter = counter.QuadPart;
  615. LARGE_INTEGER frequency;
  616. QueryPerformanceFrequency(&frequency);
  617. counterTime = 1.0 / frequency.QuadPart;
  618. #endif
  619. #if defined ARCH_LIN
  620. struct timespec ts;
  621. clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
  622. startTime = int64_t(ts.tv_sec) * 1000000000LL + ts.tv_nsec;
  623. #endif
  624. #if defined ARCH_MAC
  625. startCounter = mach_absolute_time();
  626. mach_timebase_info_data_t tb;
  627. mach_timebase_info(&tb);
  628. counterTime = 1e-9 * (double) tb.numer / tb.denom;
  629. #endif
  630. }
  631. double getTime() {
  632. #if defined ARCH_WIN
  633. LARGE_INTEGER counter;
  634. QueryPerformanceCounter(&counter);
  635. return (counter.QuadPart - startCounter) * counterTime;
  636. #elif defined ARCH_LIN
  637. struct timespec ts;
  638. clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
  639. int64_t time = int64_t(ts.tv_sec) * 1000000000LL + ts.tv_nsec;
  640. return (time - startTime) / 1e9;
  641. #elif defined ARCH_MAC
  642. int64_t counter = mach_absolute_time();
  643. return (counter - startCounter) * counterTime;
  644. #else
  645. return 0.0;
  646. #endif
  647. }
  648. double getUnixTime() {
  649. // This is not guaranteed to return the time since 1970 in C++11. (It only does in C++20).
  650. // However, it does on all platforms I care about.
  651. auto duration = std::chrono::system_clock::now().time_since_epoch();
  652. return std::chrono::duration<double>(duration).count();
  653. }
  654. double getThreadTime() {
  655. #if defined ARCH_LIN
  656. struct timespec ts;
  657. clockid_t cid;
  658. pthread_getcpuclockid(pthread_self(), &cid);
  659. clock_gettime(cid, &ts);
  660. return ts.tv_sec + ts.tv_nsec * 1e-9;
  661. #elif defined ARCH_MAC
  662. mach_port_t thread = mach_thread_self();
  663. mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
  664. thread_basic_info_data_t info;
  665. kern_return_t kr = thread_info(thread, THREAD_BASIC_INFO, (thread_info_t) &info, &count);
  666. if (kr != KERN_SUCCESS || (info.flags & TH_FLAGS_IDLE) != 0)
  667. return 0.0;
  668. return info.user_time.seconds + info.user_time.microseconds * 1e-6;
  669. #elif defined ARCH_WIN
  670. FILETIME creationTime;
  671. FILETIME exitTime;
  672. FILETIME kernelTime;
  673. FILETIME userTime;
  674. if (GetThreadTimes(GetCurrentThread(), &creationTime, &exitTime, &kernelTime, &userTime) == 0)
  675. return 0.0;
  676. return ((uint64_t(userTime.dwHighDateTime) << 32) + userTime.dwLowDateTime) * 1e-7;
  677. #else
  678. return 0.0;
  679. #endif
  680. }
  681. void sleep(double time) {
  682. std::this_thread::sleep_for(std::chrono::duration<double>(time));
  683. }
  684. std::string getOperatingSystemInfo() {
  685. #if defined ARCH_LIN
  686. struct utsname u;
  687. uname(&u);
  688. return string::f("%s %s %s %s", u.sysname, u.release, u.version, u.machine);
  689. #elif defined ARCH_MAC
  690. // From https://opensource.apple.com/source/cctools/cctools-973.0.1/libstuff/macosx_deployment_target.c.auto.html
  691. char osversion[32];
  692. int osversion_name[2] = {CTL_KERN, KERN_OSRELEASE};
  693. size_t osversion_len = sizeof(osversion) - 1;
  694. if (sysctl(osversion_name, 2, osversion, &osversion_len, NULL, 0) != 0)
  695. return "Mac";
  696. int major = 0;
  697. int minor = 0;
  698. if (sscanf(osversion, "%d.%d", &major, &minor) != 2)
  699. return "Mac";
  700. // Try to match version numbers to retail versions
  701. if (major >= 20) {
  702. if (major == 22) {
  703. minor -= 1;
  704. }
  705. major -= 9;
  706. return string::f("Mac %d.%d", major, minor);
  707. }
  708. else {
  709. major -= 4;
  710. return string::f("Mac 10.%d.%d", major, minor);
  711. }
  712. #elif defined ARCH_WIN
  713. OSVERSIONINFOW info;
  714. ZeroMemory(&info, sizeof(info));
  715. info.dwOSVersionInfoSize = sizeof(info);
  716. GetVersionExW(&info);
  717. // See https://docs.microsoft.com/en-us/windows/desktop/api/winnt/ns-winnt-_osversioninfoa for a list of Windows version numbers.
  718. return string::f("Windows %lu.%lu", info.dwMajorVersion, info.dwMinorVersion);
  719. #endif
  720. }
  721. void openBrowser(const std::string& url) {
  722. if (url.empty())
  723. return;
  724. INFO("Opening browser URL %s", url.c_str());
  725. std::string urlL = url;
  726. std::thread t([=] {
  727. #if defined ARCH_LIN
  728. std::string command = "xdg-open \"" + urlL + "\"";
  729. (void) std::system(command.c_str());
  730. #endif
  731. #if defined ARCH_MAC
  732. std::string command = "open \"" + urlL + "\"";
  733. std::system(command.c_str());
  734. #endif
  735. #if defined ARCH_WIN
  736. ShellExecuteW(NULL, L"open", string::UTF8toUTF16(urlL).c_str(), NULL, NULL, SW_SHOWDEFAULT);
  737. #endif
  738. });
  739. t.detach();
  740. }
  741. void openDirectory(const std::string& path) {
  742. if (path.empty())
  743. return;
  744. INFO("Opening directory %s", path.c_str());
  745. std::string pathL = path;
  746. std::thread t([=] {
  747. #if defined ARCH_LIN
  748. std::string command = "xdg-open \"" + pathL + "\"";
  749. (void) std::system(command.c_str());
  750. #endif
  751. #if defined ARCH_MAC
  752. std::string command = "open \"" + pathL + "\"";
  753. std::system(command.c_str());
  754. #endif
  755. #if defined ARCH_WIN
  756. ShellExecuteW(NULL, L"explore", string::UTF8toUTF16(pathL).c_str(), NULL, NULL, SW_SHOWDEFAULT);
  757. #endif
  758. });
  759. t.detach();
  760. }
  761. void runProcessDetached(const std::string& path) {
  762. #if defined ARCH_WIN
  763. SHELLEXECUTEINFOW shExInfo;
  764. ZeroMemory(&shExInfo, sizeof(shExInfo));
  765. shExInfo.cbSize = sizeof(shExInfo);
  766. shExInfo.lpVerb = L"runas";
  767. std::wstring pathW = string::UTF8toUTF16(path);
  768. shExInfo.lpFile = pathW.c_str();
  769. shExInfo.nShow = SW_SHOW;
  770. if (ShellExecuteExW(&shExInfo)) {
  771. // Do nothing
  772. }
  773. #else
  774. // Not implemented on Linux or Mac
  775. assert(0);
  776. #endif
  777. }
  778. uint32_t getFpuFlags() {
  779. #if defined ARCH_X64
  780. return _mm_getcsr();
  781. #elif defined ARCH_ARM64
  782. uint64_t fpcr;
  783. __asm__ volatile("mrs %0, fpcr" : "=r" (fpcr));
  784. return fpcr;
  785. #endif
  786. }
  787. void setFpuFlags(uint32_t flags) {
  788. #if defined ARCH_X64
  789. _mm_setcsr(flags);
  790. #elif defined ARCH_ARM64
  791. uint64_t fpcr = flags;
  792. __asm__ volatile("msr fpcr, %0" :: "r" (fpcr));
  793. #endif
  794. }
  795. void resetFpuFlags() {
  796. uint32_t flags = getFpuFlags();
  797. #if defined ARCH_X64
  798. // Set CPU to flush-to-zero (FTZ) and denormals-are-zero (DAZ) mode
  799. // https://software.intel.com/en-us/node/682949
  800. // _MM_SET_FLUSH_ZERO_MODE(_MM_FLUSH_ZERO_ON);
  801. flags |= 0x8000;
  802. // _MM_SET_DENORMALS_ZERO_MODE(_MM_DENORMALS_ZERO_ON);
  803. flags |= 0x0040;
  804. // Round-to-nearest is default
  805. flags &= ~0x6000;
  806. #elif defined ARCH_ARM64
  807. // Set Flush-to-Zero
  808. flags |= 1 << 24;
  809. // ARM64 always uses DAZ
  810. // Round-to-nearest is default
  811. flags &= ~((1 << 22) | (1 << 23));
  812. #endif
  813. setFpuFlags(flags);
  814. }
  815. void init() {
  816. initTime();
  817. }
  818. } // namespace system
  819. } // namespace rack