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.

1013 lines
25KB

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