View Javadoc
1   /*
2    * Copyright 2016-2024 The OSHI Project Contributors
3    * SPDX-License-Identifier: MIT
4    */
5   package oshi.hardware.platform.mac;
6   
7   import static oshi.util.Memoizer.defaultExpiration;
8   import static oshi.util.Memoizer.memoize;
9   
10  import java.util.ArrayList;
11  import java.util.List;
12  import java.util.function.Supplier;
13  
14  import org.slf4j.Logger;
15  import org.slf4j.LoggerFactory;
16  
17  import com.sun.jna.Native;
18  import com.sun.jna.platform.mac.SystemB;
19  
20  import oshi.annotation.concurrent.ThreadSafe;
21  import oshi.hardware.PhysicalMemory;
22  import oshi.hardware.VirtualMemory;
23  import oshi.hardware.common.AbstractGlobalMemory;
24  import oshi.jna.ByRef.CloseableIntByReference;
25  import oshi.jna.ByRef.CloseableLongByReference;
26  import oshi.jna.Struct.CloseableVMStatistics;
27  import oshi.util.Constants;
28  import oshi.util.ExecutingCommand;
29  import oshi.util.ParseUtil;
30  import oshi.util.platform.mac.SysctlUtil;
31  
32  /**
33   * Memory obtained by host_statistics (vm_stat) and sysctl.
34   */
35  @ThreadSafe
36  final class MacGlobalMemory extends AbstractGlobalMemory {
37  
38      private static final Logger LOG = LoggerFactory.getLogger(MacGlobalMemory.class);
39  
40      private final Supplier<Long> available = memoize(this::queryVmStats, defaultExpiration());
41  
42      private final Supplier<Long> total = memoize(MacGlobalMemory::queryPhysMem);
43  
44      private final Supplier<Long> pageSize = memoize(MacGlobalMemory::queryPageSize);
45  
46      private final Supplier<VirtualMemory> vm = memoize(this::createVirtualMemory);
47  
48      @Override
49      public long getAvailable() {
50          return available.get();
51      }
52  
53      @Override
54      public long getTotal() {
55          return total.get();
56      }
57  
58      @Override
59      public long getPageSize() {
60          return pageSize.get();
61      }
62  
63      @Override
64      public VirtualMemory getVirtualMemory() {
65          return vm.get();
66      }
67  
68      @Override
69      public List<PhysicalMemory> getPhysicalMemory() {
70          List<PhysicalMemory> pmList = new ArrayList<>();
71          List<String> sp = ExecutingCommand.runNative("system_profiler SPMemoryDataType");
72          int bank = 0;
73          String bankLabel = Constants.UNKNOWN;
74          long capacity = 0L;
75          long speed = 0L;
76          String manufacturer = Constants.UNKNOWN;
77          String memoryType = Constants.UNKNOWN;
78          String partNumber = Constants.UNKNOWN;
79          String serialNumber = Constants.UNKNOWN;
80          for (String line : sp) {
81              if (line.trim().startsWith("BANK")) {
82                  // Save previous bank
83                  if (bank++ > 0) {
84                      pmList.add(new PhysicalMemory(bankLabel, capacity, speed, manufacturer, memoryType,
85                              Constants.UNKNOWN, serialNumber));
86                  }
87                  bankLabel = line.trim();
88                  int colon = bankLabel.lastIndexOf(':');
89                  if (colon > 0) {
90                      bankLabel = bankLabel.substring(0, colon - 1);
91                  }
92              } else if (bank > 0) {
93                  String[] split = line.trim().split(":");
94                  if (split.length == 2) {
95                      switch (split[0]) {
96                      case "Size":
97                          capacity = ParseUtil.parseDecimalMemorySizeToBinary(split[1].trim());
98                          break;
99                      case "Type":
100                         memoryType = split[1].trim();
101                         break;
102                     case "Speed":
103                         speed = ParseUtil.parseHertz(split[1]);
104                         break;
105                     case "Manufacturer":
106                         manufacturer = split[1].trim();
107                         break;
108                     case "Part Number":
109                         partNumber = split[1].trim();
110                         break;
111                     case "Serial Number":
112                         serialNumber = split[1].trim();
113                         break;
114                     default:
115                         break;
116                     }
117                 }
118             }
119         }
120         pmList.add(new PhysicalMemory(bankLabel, capacity, speed, manufacturer, memoryType, partNumber, serialNumber));
121 
122         return pmList;
123     }
124 
125     private long queryVmStats() {
126         try (CloseableVMStatistics vmStats = new CloseableVMStatistics();
127                 CloseableIntByReference size = new CloseableIntByReference(vmStats.size() / SystemB.INT_SIZE)) {
128             if (0 != SystemB.INSTANCE.host_statistics(SystemB.INSTANCE.mach_host_self(), SystemB.HOST_VM_INFO, vmStats,
129                     size)) {
130                 LOG.error("Failed to get host VM info. Error code: {}", Native.getLastError());
131                 return 0L;
132             }
133             return (vmStats.free_count + vmStats.inactive_count) * getPageSize();
134         }
135     }
136 
137     private static long queryPhysMem() {
138         return SysctlUtil.sysctl("hw.memsize", 0L);
139     }
140 
141     private static long queryPageSize() {
142         try (CloseableLongByReference pPageSize = new CloseableLongByReference()) {
143             if (0 == SystemB.INSTANCE.host_page_size(SystemB.INSTANCE.mach_host_self(), pPageSize)) {
144                 return pPageSize.getValue();
145             }
146         }
147         LOG.error("Failed to get host page size. Error code: {}", Native.getLastError());
148         return 4098L;
149     }
150 
151     private VirtualMemory createVirtualMemory() {
152         return new MacVirtualMemory(this);
153     }
154 }