1
2
3
4
5 package oshi.util.platform.unix.solaris;
6
7 import java.nio.charset.StandardCharsets;
8 import java.util.ArrayList;
9 import java.util.Arrays;
10 import java.util.List;
11 import java.util.concurrent.locks.Lock;
12 import java.util.concurrent.locks.ReentrantLock;
13
14 import org.slf4j.Logger;
15 import org.slf4j.LoggerFactory;
16
17 import com.sun.jna.Native;
18 import com.sun.jna.Pointer;
19 import com.sun.jna.platform.unix.solaris.Kstat2;
20 import com.sun.jna.platform.unix.solaris.Kstat2.Kstat2Handle;
21 import com.sun.jna.platform.unix.solaris.Kstat2.Kstat2Map;
22 import com.sun.jna.platform.unix.solaris.Kstat2.Kstat2MatcherList;
23 import com.sun.jna.platform.unix.solaris.Kstat2StatusException;
24 import com.sun.jna.platform.unix.solaris.LibKstat;
25 import com.sun.jna.platform.unix.solaris.LibKstat.Kstat;
26 import com.sun.jna.platform.unix.solaris.LibKstat.KstatCtl;
27 import com.sun.jna.platform.unix.solaris.LibKstat.KstatNamed;
28
29 import oshi.annotation.concurrent.GuardedBy;
30 import oshi.annotation.concurrent.ThreadSafe;
31 import oshi.software.os.unix.solaris.SolarisOperatingSystem;
32 import oshi.util.FormatUtil;
33 import oshi.util.Util;
34
35
36
37
38 @ThreadSafe
39 public final class KstatUtil {
40
41 private static final Logger LOG = LoggerFactory.getLogger(KstatUtil.class);
42
43 private static final Lock CHAIN = new ReentrantLock();
44
45
46 @GuardedBy("CHAIN")
47 private static KstatCtl kstatCtl = null;
48
49 private KstatUtil() {
50 }
51
52
53
54
55
56
57
58
59
60
61 public static final class KstatChain implements AutoCloseable {
62
63 private final KstatCtl localCtlRef;
64
65 private KstatChain(KstatCtl ctl) {
66 this.localCtlRef = ctl;
67 update();
68 }
69
70
71
72
73
74
75
76
77
78
79
80 @GuardedBy("CHAIN")
81 public boolean read(Kstat ksp) {
82 int retry = 0;
83 while (0 > LibKstat.INSTANCE.kstat_read(localCtlRef, ksp, null)) {
84 if (LibKstat.EAGAIN != Native.getLastError() || 5 <= ++retry) {
85 if (LOG.isDebugEnabled()) {
86 LOG.debug("Failed to read kstat {}:{}:{}",
87 Native.toString(ksp.ks_module, StandardCharsets.US_ASCII), ksp.ks_instance,
88 Native.toString(ksp.ks_name, StandardCharsets.US_ASCII));
89 }
90 return false;
91 }
92 Util.sleep(8 << retry);
93 }
94 return true;
95 }
96
97
98
99
100
101
102
103
104
105
106
107
108 @GuardedBy("CHAIN")
109 public Kstat lookup(String module, int instance, String name) {
110 return LibKstat.INSTANCE.kstat_lookup(localCtlRef, module, instance, name);
111 }
112
113
114
115
116
117
118
119
120
121
122
123
124 @GuardedBy("CHAIN")
125 public List<Kstat> lookupAll(String module, int instance, String name) {
126 List<Kstat> kstats = new ArrayList<>();
127 for (Kstat ksp = LibKstat.INSTANCE.kstat_lookup(localCtlRef, module, instance, name); ksp != null; ksp = ksp
128 .next()) {
129 if ((module == null || module.equals(Native.toString(ksp.ks_module, StandardCharsets.US_ASCII)))
130 && (instance < 0 || instance == ksp.ks_instance)
131 && (name == null || name.equals(Native.toString(ksp.ks_name, StandardCharsets.US_ASCII)))) {
132 kstats.add(ksp);
133 }
134 }
135 return kstats;
136 }
137
138
139
140
141
142
143
144
145
146
147 @GuardedBy("CHAIN")
148 public int update() {
149 return LibKstat.INSTANCE.kstat_chain_update(localCtlRef);
150 }
151
152
153
154
155 @Override
156 public void close() {
157 CHAIN.unlock();
158 }
159 }
160
161
162
163
164
165
166
167 public static synchronized KstatChain openChain() {
168 CHAIN.lock();
169 if (kstatCtl == null) {
170 kstatCtl = LibKstat.INSTANCE.kstat_open();
171 }
172 return new KstatChain(kstatCtl);
173 }
174
175
176
177
178
179
180
181
182
183
184 public static String dataLookupString(Kstat ksp, String name) {
185 if (ksp.ks_type != LibKstat.KSTAT_TYPE_NAMED && ksp.ks_type != LibKstat.KSTAT_TYPE_TIMER) {
186 throw new IllegalArgumentException("Not a kstat_named or kstat_timer kstat.");
187 }
188 Pointer p = LibKstat.INSTANCE.kstat_data_lookup(ksp, name);
189 if (p == null) {
190 LOG.debug("Failed to lookup kstat value for key {}", name);
191 return "";
192 }
193 KstatNamed data = new KstatNamed(p);
194 switch (data.data_type) {
195 case LibKstat.KSTAT_DATA_CHAR:
196 return Native.toString(data.value.charc, StandardCharsets.UTF_8);
197 case LibKstat.KSTAT_DATA_INT32:
198 return Integer.toString(data.value.i32);
199 case LibKstat.KSTAT_DATA_UINT32:
200 return FormatUtil.toUnsignedString(data.value.ui32);
201 case LibKstat.KSTAT_DATA_INT64:
202 return Long.toString(data.value.i64);
203 case LibKstat.KSTAT_DATA_UINT64:
204 return FormatUtil.toUnsignedString(data.value.ui64);
205 case LibKstat.KSTAT_DATA_STRING:
206 return data.value.str.addr.getString(0);
207 default:
208 LOG.error("Unimplemented kstat data type {}", data.data_type);
209 return "";
210 }
211 }
212
213
214
215
216
217
218
219
220
221
222 public static long dataLookupLong(Kstat ksp, String name) {
223 if (ksp.ks_type != LibKstat.KSTAT_TYPE_NAMED && ksp.ks_type != LibKstat.KSTAT_TYPE_TIMER) {
224 throw new IllegalArgumentException("Not a kstat_named or kstat_timer kstat.");
225 }
226 Pointer p = LibKstat.INSTANCE.kstat_data_lookup(ksp, name);
227 if (p == null) {
228 if (LOG.isDebugEnabled()) {
229 LOG.debug("Failed lo lookup kstat value on {}:{}:{} for key {}",
230 Native.toString(ksp.ks_module, StandardCharsets.US_ASCII), ksp.ks_instance,
231 Native.toString(ksp.ks_name, StandardCharsets.US_ASCII), name);
232 }
233 return 0L;
234 }
235 KstatNamed data = new KstatNamed(p);
236 switch (data.data_type) {
237 case LibKstat.KSTAT_DATA_INT32:
238 return data.value.i32;
239 case LibKstat.KSTAT_DATA_UINT32:
240 return FormatUtil.getUnsignedInt(data.value.ui32);
241 case LibKstat.KSTAT_DATA_INT64:
242 return data.value.i64;
243 case LibKstat.KSTAT_DATA_UINT64:
244 return data.value.ui64;
245 default:
246 LOG.error("Unimplemented or non-numeric kstat data type {}", data.data_type);
247 return 0L;
248 }
249 }
250
251
252
253
254
255
256
257
258 public static Object[] queryKstat2(String mapStr, String... names) {
259 if (!SolarisOperatingSystem.HAS_KSTAT2) {
260 throw new UnsupportedOperationException(
261 "Kstat2 requires Solaris 11.4+. Use SolarisOperatingSystem#HAS_KSTAT2 to test this.");
262 }
263 Object[] result = new Object[names.length];
264 Kstat2MatcherList matchers = new Kstat2MatcherList();
265 KstatUtil.CHAIN.lock();
266 try {
267 matchers.addMatcher(Kstat2.KSTAT2_M_STRING, mapStr);
268 Kstat2Handle handle = new Kstat2Handle();
269 try {
270 Kstat2Map map = handle.lookupMap(mapStr);
271 for (int i = 0; i < names.length; i++) {
272 result[i] = map.getValue(names[i]);
273 }
274 } finally {
275 handle.close();
276 }
277 } catch (Kstat2StatusException e) {
278 LOG.debug("Failed to get stats on {} for names {}: {}", mapStr, Arrays.toString(names), e.getMessage());
279 } finally {
280 KstatUtil.CHAIN.unlock();
281 matchers.free();
282 }
283 return result;
284 }
285
286
287
288
289
290
291
292
293
294 public static List<Object[]> queryKstat2List(String beforeStr, String afterStr, String... names) {
295 if (!SolarisOperatingSystem.HAS_KSTAT2) {
296 throw new UnsupportedOperationException(
297 "Kstat2 requires Solaris 11.4+. Use SolarisOperatingSystem#HAS_KSTAT2 to test this.");
298 }
299 List<Object[]> results = new ArrayList<>();
300 int s = 0;
301 Kstat2MatcherList matchers = new Kstat2MatcherList();
302 KstatUtil.CHAIN.lock();
303 try {
304 matchers.addMatcher(Kstat2.KSTAT2_M_GLOB, beforeStr + "*" + afterStr);
305 Kstat2Handle handle = new Kstat2Handle();
306 try {
307 for (s = 0; s < Integer.MAX_VALUE; s++) {
308 Object[] result = new Object[names.length];
309 Kstat2Map map = handle.lookupMap(beforeStr + s + afterStr);
310 for (int i = 0; i < names.length; i++) {
311 result[i] = map.getValue(names[i]);
312 }
313 results.add(result);
314 }
315 } finally {
316 handle.close();
317 }
318 } catch (Kstat2StatusException e) {
319
320 LOG.debug("Failed to get stats on {}{}{} for names {}: {}", beforeStr, s, afterStr, Arrays.toString(names),
321 e.getMessage());
322 } finally {
323 KstatUtil.CHAIN.unlock();
324 matchers.free();
325 }
326 return results;
327 }
328 }