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.

880 lines
22KB

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