Ptc.cs 29 KB

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