Ptc.cs 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855
  1. using ARMeilleure.CodeGen;
  2. using ARMeilleure.CodeGen.Unwinding;
  3. using ARMeilleure.CodeGen.X86;
  4. using ARMeilleure.Memory;
  5. using ARMeilleure.Translation.Cache;
  6. using Ryujinx.Common.Configuration;
  7. using Ryujinx.Common.Logging;
  8. using System;
  9. using System.Buffers.Binary;
  10. using System.Collections.Concurrent;
  11. using System.Collections.Generic;
  12. using System.Diagnostics;
  13. using System.IO;
  14. using System.IO.Compression;
  15. using System.Runtime.InteropServices;
  16. using System.Security.Cryptography;
  17. using System.Threading;
  18. namespace ARMeilleure.Translation.PTC
  19. {
  20. public static class Ptc
  21. {
  22. private const string HeaderMagic = "PTChd";
  23. private const int InternalVersion = 1814; //! To be incremented manually for each change to the ARMeilleure project.
  24. private const string ActualDir = "0";
  25. private const string BackupDir = "1";
  26. private const string TitleIdTextDefault = "0000000000000000";
  27. private const string DisplayVersionDefault = "0";
  28. internal const int PageTablePointerIndex = -1; // Must be a negative value.
  29. internal const int JumpPointerIndex = -2; // Must be a negative value.
  30. internal const int DynamicPointerIndex = -3; // Must be a negative value.
  31. private const byte FillingByte = 0x00;
  32. private const CompressionLevel SaveCompressionLevel = CompressionLevel.Fastest;
  33. private static readonly MemoryStream _infosStream;
  34. private static readonly MemoryStream _codesStream;
  35. private static readonly MemoryStream _relocsStream;
  36. private static readonly MemoryStream _unwindInfosStream;
  37. private static readonly BinaryWriter _infosWriter;
  38. private static readonly ManualResetEvent _waitEvent;
  39. private static readonly AutoResetEvent _loggerEvent;
  40. private static readonly object _lock;
  41. private static bool _disposed;
  42. private static volatile int _translateCount;
  43. internal static PtcJumpTable PtcJumpTable { get; private set; }
  44. internal static string TitleIdText { get; private set; }
  45. internal static string DisplayVersion { get; private set; }
  46. internal static string CachePathActual { get; private set; }
  47. internal static string CachePathBackup { get; private set; }
  48. internal static PtcState State { get; private set; }
  49. static Ptc()
  50. {
  51. _infosStream = new MemoryStream();
  52. _codesStream = new MemoryStream();
  53. _relocsStream = new MemoryStream();
  54. _unwindInfosStream = new MemoryStream();
  55. _infosWriter = new BinaryWriter(_infosStream, EncodingCache.UTF8NoBOM, true);
  56. _waitEvent = new ManualResetEvent(true);
  57. _loggerEvent = new AutoResetEvent(false);
  58. _lock = new object();
  59. _disposed = false;
  60. PtcJumpTable = new PtcJumpTable();
  61. TitleIdText = TitleIdTextDefault;
  62. DisplayVersion = DisplayVersionDefault;
  63. CachePathActual = string.Empty;
  64. CachePathBackup = string.Empty;
  65. Disable();
  66. }
  67. public static void Initialize(string titleIdText, string displayVersion, bool enabled)
  68. {
  69. Wait();
  70. ClearMemoryStreams();
  71. PtcJumpTable.Clear();
  72. PtcProfiler.Stop();
  73. PtcProfiler.Wait();
  74. PtcProfiler.ClearEntries();
  75. if (String.IsNullOrEmpty(titleIdText) || titleIdText == TitleIdTextDefault)
  76. {
  77. TitleIdText = TitleIdTextDefault;
  78. DisplayVersion = DisplayVersionDefault;
  79. CachePathActual = string.Empty;
  80. CachePathBackup = string.Empty;
  81. Disable();
  82. return;
  83. }
  84. Logger.Info?.Print(LogClass.Ptc, $"Initializing Profiled Persistent Translation Cache (enabled: {enabled}).");
  85. TitleIdText = titleIdText;
  86. DisplayVersion = !String.IsNullOrEmpty(displayVersion) ? displayVersion : DisplayVersionDefault;
  87. if (enabled)
  88. {
  89. string workPathActual = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", ActualDir);
  90. string workPathBackup = Path.Combine(AppDataManager.GamesDirPath, TitleIdText, "cache", "cpu", BackupDir);
  91. if (!Directory.Exists(workPathActual))
  92. {
  93. Directory.CreateDirectory(workPathActual);
  94. }
  95. if (!Directory.Exists(workPathBackup))
  96. {
  97. Directory.CreateDirectory(workPathBackup);
  98. }
  99. CachePathActual = Path.Combine(workPathActual, DisplayVersion);
  100. CachePathBackup = Path.Combine(workPathBackup, DisplayVersion);
  101. Enable();
  102. PreLoad();
  103. PtcProfiler.PreLoad();
  104. }
  105. else
  106. {
  107. CachePathActual = string.Empty;
  108. CachePathBackup = string.Empty;
  109. Disable();
  110. }
  111. }
  112. internal static void ClearMemoryStreams()
  113. {
  114. _infosStream.SetLength(0L);
  115. _codesStream.SetLength(0L);
  116. _relocsStream.SetLength(0L);
  117. _unwindInfosStream.SetLength(0L);
  118. }
  119. private static void PreLoad()
  120. {
  121. string fileNameActual = String.Concat(CachePathActual, ".cache");
  122. string fileNameBackup = String.Concat(CachePathBackup, ".cache");
  123. FileInfo fileInfoActual = new FileInfo(fileNameActual);
  124. FileInfo fileInfoBackup = new FileInfo(fileNameBackup);
  125. if (fileInfoActual.Exists && fileInfoActual.Length != 0L)
  126. {
  127. if (!Load(fileNameActual, false))
  128. {
  129. if (fileInfoBackup.Exists && fileInfoBackup.Length != 0L)
  130. {
  131. Load(fileNameBackup, true);
  132. }
  133. }
  134. }
  135. else if (fileInfoBackup.Exists && fileInfoBackup.Length != 0L)
  136. {
  137. Load(fileNameBackup, true);
  138. }
  139. }
  140. private static bool Load(string fileName, bool isBackup)
  141. {
  142. using (FileStream compressedStream = new FileStream(fileName, FileMode.Open))
  143. using (DeflateStream deflateStream = new DeflateStream(compressedStream, CompressionMode.Decompress, true))
  144. using (MemoryStream stream = new MemoryStream())
  145. using (MD5 md5 = MD5.Create())
  146. {
  147. int hashSize = md5.HashSize / 8;
  148. try
  149. {
  150. deflateStream.CopyTo(stream);
  151. }
  152. catch
  153. {
  154. InvalidateCompressedStream(compressedStream);
  155. return false;
  156. }
  157. stream.Seek(0L, SeekOrigin.Begin);
  158. byte[] currentHash = new byte[hashSize];
  159. stream.Read(currentHash, 0, hashSize);
  160. byte[] expectedHash = md5.ComputeHash(stream);
  161. if (!CompareHash(currentHash, expectedHash))
  162. {
  163. InvalidateCompressedStream(compressedStream);
  164. return false;
  165. }
  166. stream.Seek((long)hashSize, SeekOrigin.Begin);
  167. Header header = ReadHeader(stream);
  168. if (header.Magic != HeaderMagic)
  169. {
  170. InvalidateCompressedStream(compressedStream);
  171. return false;
  172. }
  173. if (header.CacheFileVersion != InternalVersion)
  174. {
  175. InvalidateCompressedStream(compressedStream);
  176. return false;
  177. }
  178. if (header.FeatureInfo != GetFeatureInfo())
  179. {
  180. InvalidateCompressedStream(compressedStream);
  181. return false;
  182. }
  183. if (header.OSPlatform != GetOSPlatform())
  184. {
  185. InvalidateCompressedStream(compressedStream);
  186. return false;
  187. }
  188. if (header.InfosLen % InfoEntry.Stride != 0)
  189. {
  190. InvalidateCompressedStream(compressedStream);
  191. return false;
  192. }
  193. byte[] infosBuf = new byte[header.InfosLen];
  194. byte[] codesBuf = new byte[header.CodesLen];
  195. byte[] relocsBuf = new byte[header.RelocsLen];
  196. byte[] unwindInfosBuf = new byte[header.UnwindInfosLen];
  197. stream.Read(infosBuf, 0, header.InfosLen);
  198. stream.Read(codesBuf, 0, header.CodesLen);
  199. stream.Read(relocsBuf, 0, header.RelocsLen);
  200. stream.Read(unwindInfosBuf, 0, header.UnwindInfosLen);
  201. try
  202. {
  203. PtcJumpTable = PtcJumpTable.Deserialize(stream);
  204. }
  205. catch
  206. {
  207. PtcJumpTable = new PtcJumpTable();
  208. InvalidateCompressedStream(compressedStream);
  209. return false;
  210. }
  211. _infosStream.Write(infosBuf, 0, header.InfosLen);
  212. _codesStream.Write(codesBuf, 0, header.CodesLen);
  213. _relocsStream.Write(relocsBuf, 0, header.RelocsLen);
  214. _unwindInfosStream.Write(unwindInfosBuf, 0, header.UnwindInfosLen);
  215. }
  216. long fileSize = new FileInfo(fileName).Length;
  217. Logger.Info?.Print(LogClass.Ptc, $"{(isBackup ? "Loaded Backup Translation Cache" : "Loaded Translation Cache")} (size: {fileSize} bytes, translated functions: {GetInfosEntriesCount()}).");
  218. return true;
  219. }
  220. private static bool CompareHash(ReadOnlySpan<byte> currentHash, ReadOnlySpan<byte> expectedHash)
  221. {
  222. return currentHash.SequenceEqual(expectedHash);
  223. }
  224. private static Header ReadHeader(MemoryStream stream)
  225. {
  226. using (BinaryReader headerReader = new BinaryReader(stream, EncodingCache.UTF8NoBOM, true))
  227. {
  228. Header header = new Header();
  229. header.Magic = headerReader.ReadString();
  230. header.CacheFileVersion = headerReader.ReadUInt32();
  231. header.FeatureInfo = headerReader.ReadUInt64();
  232. header.OSPlatform = headerReader.ReadUInt32();
  233. header.InfosLen = headerReader.ReadInt32();
  234. header.CodesLen = headerReader.ReadInt32();
  235. header.RelocsLen = headerReader.ReadInt32();
  236. header.UnwindInfosLen = headerReader.ReadInt32();
  237. return header;
  238. }
  239. }
  240. private static void InvalidateCompressedStream(FileStream compressedStream)
  241. {
  242. compressedStream.SetLength(0L);
  243. }
  244. private static void PreSave(object state)
  245. {
  246. _waitEvent.Reset();
  247. string fileNameActual = String.Concat(CachePathActual, ".cache");
  248. string fileNameBackup = String.Concat(CachePathBackup, ".cache");
  249. FileInfo fileInfoActual = new FileInfo(fileNameActual);
  250. if (fileInfoActual.Exists && fileInfoActual.Length != 0L)
  251. {
  252. File.Copy(fileNameActual, fileNameBackup, true);
  253. }
  254. Save(fileNameActual);
  255. _waitEvent.Set();
  256. }
  257. private static void Save(string fileName)
  258. {
  259. using (MemoryStream stream = new MemoryStream())
  260. using (MD5 md5 = MD5.Create())
  261. {
  262. int hashSize = md5.HashSize / 8;
  263. stream.Seek((long)hashSize, SeekOrigin.Begin);
  264. WriteHeader(stream);
  265. _infosStream.WriteTo(stream);
  266. _codesStream.WriteTo(stream);
  267. _relocsStream.WriteTo(stream);
  268. _unwindInfosStream.WriteTo(stream);
  269. PtcJumpTable.Serialize(stream, PtcJumpTable);
  270. stream.Seek((long)hashSize, SeekOrigin.Begin);
  271. byte[] hash = md5.ComputeHash(stream);
  272. stream.Seek(0L, SeekOrigin.Begin);
  273. stream.Write(hash, 0, hashSize);
  274. using (FileStream compressedStream = new FileStream(fileName, FileMode.OpenOrCreate))
  275. using (DeflateStream deflateStream = new DeflateStream(compressedStream, SaveCompressionLevel, true))
  276. {
  277. try
  278. {
  279. stream.WriteTo(deflateStream);
  280. }
  281. catch
  282. {
  283. compressedStream.Position = 0L;
  284. }
  285. if (compressedStream.Position < compressedStream.Length)
  286. {
  287. compressedStream.SetLength(compressedStream.Position);
  288. }
  289. }
  290. }
  291. long fileSize = new FileInfo(fileName).Length;
  292. Logger.Info?.Print(LogClass.Ptc, $"Saved Translation Cache (size: {fileSize} bytes, translated functions: {GetInfosEntriesCount()}).");
  293. }
  294. private static void WriteHeader(MemoryStream stream)
  295. {
  296. using (BinaryWriter headerWriter = new BinaryWriter(stream, EncodingCache.UTF8NoBOM, true))
  297. {
  298. headerWriter.Write((string)HeaderMagic); // Header.Magic
  299. headerWriter.Write((uint)InternalVersion); // Header.CacheFileVersion
  300. headerWriter.Write((ulong)GetFeatureInfo()); // Header.FeatureInfo
  301. headerWriter.Write((uint)GetOSPlatform()); // Header.OSPlatform
  302. headerWriter.Write((int)_infosStream.Length); // Header.InfosLen
  303. headerWriter.Write((int)_codesStream.Length); // Header.CodesLen
  304. headerWriter.Write((int)_relocsStream.Length); // Header.RelocsLen
  305. headerWriter.Write((int)_unwindInfosStream.Length); // Header.UnwindInfosLen
  306. }
  307. }
  308. internal static void LoadTranslations(ConcurrentDictionary<ulong, TranslatedFunction> funcs, IMemoryManager memory, JumpTable jumpTable)
  309. {
  310. if ((int)_infosStream.Length == 0 ||
  311. (int)_codesStream.Length == 0 ||
  312. (int)_relocsStream.Length == 0 ||
  313. (int)_unwindInfosStream.Length == 0)
  314. {
  315. return;
  316. }
  317. Debug.Assert(funcs.Count == 0);
  318. _infosStream.Seek(0L, SeekOrigin.Begin);
  319. _codesStream.Seek(0L, SeekOrigin.Begin);
  320. _relocsStream.Seek(0L, SeekOrigin.Begin);
  321. _unwindInfosStream.Seek(0L, SeekOrigin.Begin);
  322. using (BinaryReader infosReader = new BinaryReader(_infosStream, EncodingCache.UTF8NoBOM, true))
  323. using (BinaryReader codesReader = new BinaryReader(_codesStream, EncodingCache.UTF8NoBOM, true))
  324. using (BinaryReader relocsReader = new BinaryReader(_relocsStream, EncodingCache.UTF8NoBOM, true))
  325. using (BinaryReader unwindInfosReader = new BinaryReader(_unwindInfosStream, EncodingCache.UTF8NoBOM, true))
  326. {
  327. for (int i = 0; i < GetInfosEntriesCount(); i++)
  328. {
  329. InfoEntry infoEntry = ReadInfo(infosReader);
  330. if (infoEntry.Stubbed)
  331. {
  332. SkipCode(infoEntry.CodeLen);
  333. SkipReloc(infoEntry.RelocEntriesCount);
  334. SkipUnwindInfo(unwindInfosReader);
  335. }
  336. else if (infoEntry.HighCq || !PtcProfiler.ProfiledFuncs.TryGetValue(infoEntry.Address, out var value) || !value.highCq)
  337. {
  338. byte[] code = ReadCode(codesReader, infoEntry.CodeLen);
  339. if (infoEntry.RelocEntriesCount != 0)
  340. {
  341. RelocEntry[] relocEntries = GetRelocEntries(relocsReader, infoEntry.RelocEntriesCount);
  342. PatchCode(code, relocEntries, memory.PageTablePointer, jumpTable);
  343. }
  344. UnwindInfo unwindInfo = ReadUnwindInfo(unwindInfosReader);
  345. TranslatedFunction func = FastTranslate(code, infoEntry.GuestSize, unwindInfo, infoEntry.HighCq);
  346. bool isAddressUnique = funcs.TryAdd(infoEntry.Address, func);
  347. Debug.Assert(isAddressUnique, $"The address 0x{infoEntry.Address:X16} is not unique.");
  348. }
  349. else
  350. {
  351. infoEntry.Stubbed = true;
  352. UpdateInfo(infoEntry);
  353. StubCode(infoEntry.CodeLen);
  354. StubReloc(infoEntry.RelocEntriesCount);
  355. StubUnwindInfo(unwindInfosReader);
  356. }
  357. }
  358. }
  359. if (_infosStream.Position < _infosStream.Length ||
  360. _codesStream.Position < _codesStream.Length ||
  361. _relocsStream.Position < _relocsStream.Length ||
  362. _unwindInfosStream.Position < _unwindInfosStream.Length)
  363. {
  364. throw new Exception("Could not reach the end of one or more memory streams.");
  365. }
  366. jumpTable.Initialize(PtcJumpTable, funcs);
  367. PtcJumpTable.WriteJumpTable(jumpTable, funcs);
  368. PtcJumpTable.WriteDynamicTable(jumpTable);
  369. Logger.Info?.Print(LogClass.Ptc, $"{funcs.Count} translated functions loaded");
  370. }
  371. private static int GetInfosEntriesCount()
  372. {
  373. return (int)_infosStream.Length / InfoEntry.Stride;
  374. }
  375. private static InfoEntry ReadInfo(BinaryReader infosReader)
  376. {
  377. InfoEntry infoEntry = new InfoEntry();
  378. infoEntry.Address = infosReader.ReadUInt64();
  379. infoEntry.GuestSize = infosReader.ReadUInt64();
  380. infoEntry.HighCq = infosReader.ReadBoolean();
  381. infoEntry.Stubbed = infosReader.ReadBoolean();
  382. infoEntry.CodeLen = infosReader.ReadInt32();
  383. infoEntry.RelocEntriesCount = infosReader.ReadInt32();
  384. return infoEntry;
  385. }
  386. private static void SkipCode(int codeLen)
  387. {
  388. _codesStream.Seek(codeLen, SeekOrigin.Current);
  389. }
  390. private static void SkipReloc(int relocEntriesCount)
  391. {
  392. _relocsStream.Seek(relocEntriesCount * RelocEntry.Stride, SeekOrigin.Current);
  393. }
  394. private static void SkipUnwindInfo(BinaryReader unwindInfosReader)
  395. {
  396. int pushEntriesLength = unwindInfosReader.ReadInt32();
  397. _unwindInfosStream.Seek(pushEntriesLength * UnwindPushEntry.Stride + UnwindInfo.Stride, SeekOrigin.Current);
  398. }
  399. private static byte[] ReadCode(BinaryReader codesReader, int codeLen)
  400. {
  401. byte[] codeBuf = new byte[codeLen];
  402. codesReader.Read(codeBuf, 0, codeLen);
  403. return codeBuf;
  404. }
  405. private static RelocEntry[] GetRelocEntries(BinaryReader relocsReader, int relocEntriesCount)
  406. {
  407. RelocEntry[] relocEntries = new RelocEntry[relocEntriesCount];
  408. for (int i = 0; i < relocEntriesCount; i++)
  409. {
  410. int position = relocsReader.ReadInt32();
  411. int index = relocsReader.ReadInt32();
  412. relocEntries[i] = new RelocEntry(position, index);
  413. }
  414. return relocEntries;
  415. }
  416. private static void PatchCode(Span<byte> code, RelocEntry[] relocEntries, IntPtr pageTablePointer, JumpTable jumpTable)
  417. {
  418. foreach (RelocEntry relocEntry in relocEntries)
  419. {
  420. ulong imm;
  421. if (relocEntry.Index == PageTablePointerIndex)
  422. {
  423. imm = (ulong)pageTablePointer.ToInt64();
  424. }
  425. else if (relocEntry.Index == JumpPointerIndex)
  426. {
  427. imm = (ulong)jumpTable.JumpPointer.ToInt64();
  428. }
  429. else if (relocEntry.Index == DynamicPointerIndex)
  430. {
  431. imm = (ulong)jumpTable.DynamicPointer.ToInt64();
  432. }
  433. else if (Delegates.TryGetDelegateFuncPtrByIndex(relocEntry.Index, out IntPtr funcPtr))
  434. {
  435. imm = (ulong)funcPtr.ToInt64();
  436. }
  437. else
  438. {
  439. throw new Exception($"Unexpected reloc entry {relocEntry}.");
  440. }
  441. BinaryPrimitives.WriteUInt64LittleEndian(code.Slice(relocEntry.Position, 8), imm);
  442. }
  443. }
  444. private static UnwindInfo ReadUnwindInfo(BinaryReader unwindInfosReader)
  445. {
  446. int pushEntriesLength = unwindInfosReader.ReadInt32();
  447. UnwindPushEntry[] pushEntries = new UnwindPushEntry[pushEntriesLength];
  448. for (int i = 0; i < pushEntriesLength; i++)
  449. {
  450. int pseudoOp = unwindInfosReader.ReadInt32();
  451. int prologOffset = unwindInfosReader.ReadInt32();
  452. int regIndex = unwindInfosReader.ReadInt32();
  453. int stackOffsetOrAllocSize = unwindInfosReader.ReadInt32();
  454. pushEntries[i] = new UnwindPushEntry((UnwindPseudoOp)pseudoOp, prologOffset, regIndex, stackOffsetOrAllocSize);
  455. }
  456. int prologueSize = unwindInfosReader.ReadInt32();
  457. return new UnwindInfo(pushEntries, prologueSize);
  458. }
  459. private static TranslatedFunction FastTranslate(byte[] code, ulong guestSize, UnwindInfo unwindInfo, bool highCq)
  460. {
  461. CompiledFunction cFunc = new CompiledFunction(code, unwindInfo);
  462. IntPtr codePtr = JitCache.Map(cFunc);
  463. GuestFunction gFunc = Marshal.GetDelegateForFunctionPointer<GuestFunction>(codePtr);
  464. TranslatedFunction tFunc = new TranslatedFunction(gFunc, guestSize, highCq);
  465. return tFunc;
  466. }
  467. private static void UpdateInfo(InfoEntry infoEntry)
  468. {
  469. _infosStream.Seek(-InfoEntry.Stride, SeekOrigin.Current);
  470. // WriteInfo.
  471. _infosWriter.Write((ulong)infoEntry.Address);
  472. _infosWriter.Write((ulong)infoEntry.GuestSize);
  473. _infosWriter.Write((bool)infoEntry.HighCq);
  474. _infosWriter.Write((bool)infoEntry.Stubbed);
  475. _infosWriter.Write((int)infoEntry.CodeLen);
  476. _infosWriter.Write((int)infoEntry.RelocEntriesCount);
  477. }
  478. private static void StubCode(int codeLen)
  479. {
  480. for (int i = 0; i < codeLen; i++)
  481. {
  482. _codesStream.WriteByte(FillingByte);
  483. }
  484. }
  485. private static void StubReloc(int relocEntriesCount)
  486. {
  487. for (int i = 0; i < relocEntriesCount * RelocEntry.Stride; i++)
  488. {
  489. _relocsStream.WriteByte(FillingByte);
  490. }
  491. }
  492. private static void StubUnwindInfo(BinaryReader unwindInfosReader)
  493. {
  494. int pushEntriesLength = unwindInfosReader.ReadInt32();
  495. for (int i = 0; i < pushEntriesLength * UnwindPushEntry.Stride + UnwindInfo.Stride; i++)
  496. {
  497. _unwindInfosStream.WriteByte(FillingByte);
  498. }
  499. }
  500. internal static void MakeAndSaveTranslations(ConcurrentDictionary<ulong, TranslatedFunction> funcs, IMemoryManager memory, JumpTable jumpTable)
  501. {
  502. var profiledFuncsToTranslate = PtcProfiler.GetProfiledFuncsToTranslate(funcs);
  503. if (profiledFuncsToTranslate.Count == 0)
  504. {
  505. return;
  506. }
  507. _translateCount = 0;
  508. ThreadPool.QueueUserWorkItem(TranslationLogger, profiledFuncsToTranslate.Count);
  509. void TranslateFuncs()
  510. {
  511. while (profiledFuncsToTranslate.TryDequeue(out var item))
  512. {
  513. ulong address = item.address;
  514. Debug.Assert(PtcProfiler.IsAddressInStaticCodeRange(address));
  515. TranslatedFunction func = Translator.Translate(memory, jumpTable, address, item.mode, item.highCq);
  516. bool isAddressUnique = funcs.TryAdd(address, func);
  517. Debug.Assert(isAddressUnique, $"The address 0x{address:X16} is not unique.");
  518. if (func.HighCq)
  519. {
  520. jumpTable.RegisterFunction(address, func);
  521. }
  522. Interlocked.Increment(ref _translateCount);
  523. if (State != PtcState.Enabled)
  524. {
  525. break;
  526. }
  527. }
  528. }
  529. int maxDegreeOfParallelism = (Environment.ProcessorCount * 3) / 4;
  530. List<Thread> threads = new List<Thread>();
  531. for (int i = 0; i < maxDegreeOfParallelism; i++)
  532. {
  533. Thread thread = new Thread(TranslateFuncs);
  534. thread.IsBackground = true;
  535. threads.Add(thread);
  536. }
  537. threads.ForEach((thread) => thread.Start());
  538. threads.ForEach((thread) => thread.Join());
  539. threads.Clear();
  540. _loggerEvent.Set();
  541. Translator.ResetPools();
  542. PtcJumpTable.Initialize(jumpTable);
  543. PtcJumpTable.ReadJumpTable(jumpTable);
  544. PtcJumpTable.ReadDynamicTable(jumpTable);
  545. ThreadPool.QueueUserWorkItem(PreSave);
  546. }
  547. private static void TranslationLogger(object state)
  548. {
  549. const int refreshRate = 1; // Seconds.
  550. int profiledFuncsToTranslateCount = (int)state;
  551. do
  552. {
  553. Logger.Info?.Print(LogClass.Ptc, $"{_translateCount} of {profiledFuncsToTranslateCount} functions translated");
  554. }
  555. while (!_loggerEvent.WaitOne(refreshRate * 1000));
  556. Logger.Info?.Print(LogClass.Ptc, $"{_translateCount} of {profiledFuncsToTranslateCount} functions translated");
  557. }
  558. internal static void WriteInfoCodeReloc(ulong address, ulong guestSize, bool highCq, PtcInfo ptcInfo)
  559. {
  560. lock (_lock)
  561. {
  562. // WriteInfo.
  563. _infosWriter.Write((ulong)address); // InfoEntry.Address
  564. _infosWriter.Write((ulong)guestSize); // InfoEntry.GuestSize
  565. _infosWriter.Write((bool)highCq); // InfoEntry.HighCq
  566. _infosWriter.Write((bool)false); // InfoEntry.Stubbed
  567. _infosWriter.Write((int)ptcInfo.CodeStream.Length); // InfoEntry.CodeLen
  568. _infosWriter.Write((int)ptcInfo.RelocEntriesCount); // InfoEntry.RelocEntriesCount
  569. // WriteCode.
  570. ptcInfo.CodeStream.WriteTo(_codesStream);
  571. // WriteReloc.
  572. ptcInfo.RelocStream.WriteTo(_relocsStream);
  573. // WriteUnwindInfo.
  574. ptcInfo.UnwindInfoStream.WriteTo(_unwindInfosStream);
  575. }
  576. }
  577. private static ulong GetFeatureInfo()
  578. {
  579. return (ulong)HardwareCapabilities.FeatureInfoEdx << 32 | (uint)HardwareCapabilities.FeatureInfoEcx;
  580. }
  581. private static uint GetOSPlatform()
  582. {
  583. uint osPlatform = 0u;
  584. osPlatform |= (RuntimeInformation.IsOSPlatform(OSPlatform.FreeBSD) ? 1u : 0u) << 0;
  585. osPlatform |= (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? 1u : 0u) << 1;
  586. osPlatform |= (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? 1u : 0u) << 2;
  587. osPlatform |= (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? 1u : 0u) << 3;
  588. return osPlatform;
  589. }
  590. private struct Header
  591. {
  592. public string Magic;
  593. public uint CacheFileVersion;
  594. public ulong FeatureInfo;
  595. public uint OSPlatform;
  596. public int InfosLen;
  597. public int CodesLen;
  598. public int RelocsLen;
  599. public int UnwindInfosLen;
  600. }
  601. private struct InfoEntry
  602. {
  603. public const int Stride = 26; // Bytes.
  604. public ulong Address;
  605. public ulong GuestSize;
  606. public bool HighCq;
  607. public bool Stubbed;
  608. public int CodeLen;
  609. public int RelocEntriesCount;
  610. }
  611. private static void Enable()
  612. {
  613. State = PtcState.Enabled;
  614. }
  615. public static void Continue()
  616. {
  617. if (State == PtcState.Enabled)
  618. {
  619. State = PtcState.Continuing;
  620. }
  621. }
  622. public static void Close()
  623. {
  624. if (State == PtcState.Enabled ||
  625. State == PtcState.Continuing)
  626. {
  627. State = PtcState.Closing;
  628. }
  629. }
  630. internal static void Disable()
  631. {
  632. State = PtcState.Disabled;
  633. }
  634. private static void Wait()
  635. {
  636. _waitEvent.WaitOne();
  637. }
  638. public static void Dispose()
  639. {
  640. if (!_disposed)
  641. {
  642. _disposed = true;
  643. Wait();
  644. _waitEvent.Dispose();
  645. _infosWriter.Dispose();
  646. _infosStream.Dispose();
  647. _codesStream.Dispose();
  648. _relocsStream.Dispose();
  649. _unwindInfosStream.Dispose();
  650. }
  651. }
  652. }
  653. }