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.

1006 lines
25KB

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