/ src / Ryujinx.UI.Common / SystemInfo / MacOSSystemInfo.cs
MacOSSystemInfo.cs
  1  using Ryujinx.Common.Logging;
  2  using System;
  3  using System.Runtime.CompilerServices;
  4  using System.Runtime.InteropServices;
  5  using System.Runtime.Versioning;
  6  using System.Text;
  7  
  8  namespace Ryujinx.UI.Common.SystemInfo
  9  {
 10      [SupportedOSPlatform("macos")]
 11      partial class MacOSSystemInfo : SystemInfo
 12      {
 13          internal MacOSSystemInfo()
 14          {
 15              if (SysctlByName("kern.osversion", out string buildRevision) != 0)
 16              {
 17                  buildRevision = "Unknown Build";
 18              }
 19  
 20              OsDescription = $"macOS {Environment.OSVersion.Version} ({buildRevision}) ({RuntimeInformation.OSArchitecture})";
 21  
 22              string cpuName = GetCpuidCpuName();
 23  
 24              if (cpuName == null && SysctlByName("machdep.cpu.brand_string", out cpuName) != 0)
 25              {
 26                  cpuName = "Unknown";
 27              }
 28  
 29              ulong totalRAM = 0;
 30  
 31              if (SysctlByName("hw.memsize", ref totalRAM) != 0) // Bytes
 32              {
 33                  totalRAM = 0;
 34              }
 35  
 36              CpuName = $"{cpuName} ; {LogicalCoreCount} logical";
 37              RamTotal = totalRAM;
 38              RamAvailable = GetVMInfoAvailableMemory();
 39          }
 40  
 41          static ulong GetVMInfoAvailableMemory()
 42          {
 43              var port = mach_host_self();
 44  
 45              uint pageSize = 0;
 46              var result = host_page_size(port, ref pageSize);
 47  
 48              if (result != 0)
 49              {
 50                  Logger.Error?.Print(LogClass.Application, $"Failed to query Available RAM. host_page_size() error = {result}");
 51                  return 0;
 52              }
 53  
 54              const int Flavor = 4; // HOST_VM_INFO64
 55              uint count = (uint)(Marshal.SizeOf<VMStatistics64>() / sizeof(int)); // HOST_VM_INFO64_COUNT
 56              VMStatistics64 stats = new();
 57              result = host_statistics64(port, Flavor, ref stats, ref count);
 58  
 59              if (result != 0)
 60              {
 61                  Logger.Error?.Print(LogClass.Application, $"Failed to query Available RAM. host_statistics64() error = {result}");
 62                  return 0;
 63              }
 64  
 65              return (ulong)(stats.FreeCount + stats.InactiveCount) * pageSize;
 66          }
 67  
 68          private const string SystemLibraryName = "libSystem.dylib";
 69  
 70          [LibraryImport(SystemLibraryName, SetLastError = true)]
 71          private static partial int sysctlbyname([MarshalAs(UnmanagedType.LPStr)] string name, IntPtr oldValue, ref ulong oldSize, IntPtr newValue, ulong newValueSize);
 72  
 73          private static int SysctlByName(string name, IntPtr oldValue, ref ulong oldSize)
 74          {
 75              if (sysctlbyname(name, oldValue, ref oldSize, IntPtr.Zero, 0) == -1)
 76              {
 77                  int err = Marshal.GetLastWin32Error();
 78  
 79                  Logger.Error?.Print(LogClass.Application, $"Cannot retrieve '{name}'. Error Code {err}");
 80  
 81                  return err;
 82              }
 83  
 84              return 0;
 85          }
 86  
 87          private static int SysctlByName<T>(string name, ref T oldValue)
 88          {
 89              unsafe
 90              {
 91                  ulong oldValueSize = (ulong)Unsafe.SizeOf<T>();
 92  
 93                  return SysctlByName(name, (IntPtr)Unsafe.AsPointer(ref oldValue), ref oldValueSize);
 94              }
 95          }
 96  
 97          private static int SysctlByName(string name, out string oldValue)
 98          {
 99              oldValue = default;
100  
101              ulong strSize = 0;
102  
103              int res = SysctlByName(name, IntPtr.Zero, ref strSize);
104  
105              if (res == 0)
106              {
107                  byte[] rawData = new byte[strSize];
108  
109                  unsafe
110                  {
111                      fixed (byte* rawDataPtr = rawData)
112                      {
113                          res = SysctlByName(name, (IntPtr)rawDataPtr, ref strSize);
114                      }
115  
116                      if (res == 0)
117                      {
118                          oldValue = Encoding.ASCII.GetString(rawData);
119                      }
120                  }
121              }
122  
123              return res;
124          }
125  
126          [LibraryImport(SystemLibraryName, SetLastError = true)]
127          private static partial uint mach_host_self();
128  
129          [LibraryImport(SystemLibraryName, SetLastError = true)]
130          private static partial int host_page_size(uint host, ref uint out_page_size);
131  
132          [StructLayout(LayoutKind.Sequential, Pack = 8)]
133          struct VMStatistics64
134          {
135              public uint FreeCount;
136              public uint ActiveCount;
137              public uint InactiveCount;
138              public uint WireCount;
139              public ulong ZeroFillCount;
140              public ulong Reactivations;
141              public ulong Pageins;
142              public ulong Pageouts;
143              public ulong Faults;
144              public ulong CowFaults;
145              public ulong Lookups;
146              public ulong Hits;
147              public ulong Purges;
148              public uint PurgeableCount;
149              public uint SpeculativeCount;
150              public ulong Decompressions;
151              public ulong Compressions;
152              public ulong Swapins;
153              public ulong Swapouts;
154              public uint CompressorPageCount;
155              public uint ThrottledCount;
156              public uint ExternalPageCount;
157              public uint InternalPageCount;
158              public ulong TotalUncompressedPagesInCompressor;
159          }
160  
161          [LibraryImport(SystemLibraryName, SetLastError = true)]
162          private static partial int host_statistics64(uint hostPriv, int hostFlavor, ref VMStatistics64 hostInfo64Out, ref uint hostInfo64OutCnt);
163      }
164  }