17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package jdk.test.lib.containers.cgroup;
27
28 import java.io.IOException;
29 import java.nio.file.Files;
30 import java.nio.file.Path;
31 import java.nio.file.Paths;
32 import java.util.Arrays;
33 import java.util.List;
34 import java.util.concurrent.TimeUnit;
35 import java.util.stream.Collectors;
36
37 import jdk.internal.platform.Metrics;
38 import jdk.test.lib.Asserts;
39
40 public class MetricsTesterCgroupV2 implements CgroupMetricsTester {
41
42 private static final long UNLIMITED = -1;
43 private static final long NOT_SUPPORTED = -2;
44 private static final UnifiedController UNIFIED = new UnifiedController();
45 private static final String MAX = "max";
46 private static final int PER_CPU_SHARES = 1024;
47
48 private final long startSysVal;
49 private final long startUserVal;
50 private final long startUsage;
51
52 static class UnifiedController {
53
54 private static final String NAME = "unified";
55 private final String path;
56
57 UnifiedController() {
58 path = constructPath();
59 }
60
61 String getPath() {
62 return path;
63 }
136 Path filePath = Paths.get(UNIFIED.getPath(), file);
137 try {
138 return Files.lines(filePath).collect(Collectors.joining());
139 } catch (IOException e) {
140 return null;
141 }
142 }
143
144 private void fail(String metric, long oldVal, long newVal) {
145 CgroupMetricsTester.fail(UnifiedController.NAME, metric, oldVal, newVal);
146 }
147
148 private void fail(String metric, String oldVal, String newVal) {
149 CgroupMetricsTester.fail(UnifiedController.NAME, metric, oldVal, newVal);
150 }
151
152 private void warn(String metric, long oldVal, long newVal) {
153 CgroupMetricsTester.warn(UnifiedController.NAME, metric, oldVal, newVal);
154 }
155
156 private void verifyNotSupported(long metricVal) {
157 Asserts.assertEquals(metricVal, NOT_SUPPORTED, "Expected metric to be not supported!");
158 }
159
160 private void verifyNotSupported(double metricVal) {
161 if (!CgroupMetricsTester.compareWithErrorMargin(NOT_SUPPORTED, metricVal)) {
162 throw new RuntimeException("Metric not supported, got: " + metricVal +
163 " expected: " + NOT_SUPPORTED);
164 }
165 }
166
167 private void verifyPerCpuNotSupported(long[] perCpuUsage) {
168 Asserts.assertNull(perCpuUsage, "perCpuUsage expected to be not supported");
169 }
170
171 private long getCpuShares(String file) {
172 long rawVal = getLongValueFromFile(file);
173 if (rawVal == 0 || rawVal == 100) {
174 return UNLIMITED;
175 }
176 int shares = (int)rawVal;
177 // CPU shares (OCI) value needs to get translated into
178 // a proper Cgroups v2 value. See:
179 // https://github.com/containers/crun/blob/master/crun.1.md#cpu-controller
180 //
181 // Use the inverse of (x == OCI value, y == cgroupsv2 value):
182 // ((262142 * y - 1)/9999) + 2 = x
183 //
184 int x = 262142 * shares - 1;
185 double frac = x/9999.0;
186 x = ((int)frac) + 2;
187 if ( x <= PER_CPU_SHARES ) {
188 return PER_CPU_SHARES; // mimic cgroups v1
189 }
190 int f = x/PER_CPU_SHARES;
226 Metrics metrics = Metrics.systemMetrics();
227
228 // User Memory
229 long oldVal = metrics.getMemoryFailCount();
230 long newVal = getLongValueEntryFromFile("memory.events", "max");
231 if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
232 fail("memory.events[max]", oldVal, newVal);
233 }
234
235 oldVal = metrics.getMemoryLimit();
236 newVal = getLongLimitValueFromFile("memory.max");
237 if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
238 fail("memory.max", oldVal, newVal);
239 }
240
241 oldVal = metrics.getMemoryUsage();
242 newVal = getLongValueFromFile("memory.current");
243 if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
244 fail("memory.current", oldVal, newVal);
245 }
246 verifyNotSupported(metrics.getMemoryMaxUsage());
247
248 // Kernel memory
249 verifyNotSupported(metrics.getKernelMemoryFailCount());
250 verifyNotSupported(metrics.getKernelMemoryLimit());
251 verifyNotSupported(metrics.getKernelMemoryMaxUsage());
252 verifyNotSupported(metrics.getKernelMemoryUsage());
253
254 //TCP Memory
255 verifyNotSupported(metrics.getTcpMemoryFailCount());
256 verifyNotSupported(metrics.getTcpMemoryLimit());
257 verifyNotSupported(metrics.getTcpMemoryMaxUsage());
258
259 oldVal = metrics.getTcpMemoryUsage();
260 newVal = getLongValueEntryFromFile("memory.stat", "sock");
261 if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
262 fail("memory.stat[sock]", oldVal, newVal);
263 }
264
265 // Memory and Swap
266 verifyNotSupported(metrics.getMemoryAndSwapFailCount());
267 verifyNotSupported(metrics.getMemoryAndSwapMaxUsage());
268
269 oldVal = metrics.getMemoryAndSwapLimit();
270 newVal = getLongLimitValueFromFile("memory.swap.max");
271 if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
272 fail("memory.swap.max", oldVal, newVal);
273 }
274
275 oldVal = metrics.getMemoryAndSwapUsage();
276 newVal = getLongValueFromFile("memory.swap.current");
277 if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
278 fail("memory.swap.current", oldVal, newVal);
279 }
280
281 oldVal = metrics.getMemorySoftLimit();
282 newVal = getLongLimitValueFromFile("memory.high");
283 if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
284 fail("memory.high", oldVal, newVal);
285 }
286
287 Asserts.assertNull(metrics.isMemoryOOMKillEnabled(), "Not supported");
288 }
289
290 @Override
291 public void testCpuAccounting() {
292 Metrics metrics = Metrics.systemMetrics();
293 long oldVal = metrics.getCpuUsage();
294 long newVal = TimeUnit.MICROSECONDS.toNanos(getLongValueEntryFromFile("cpu.stat", "usage_usec"));
295
296 if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
297 warn("cpu.stat[usage_usec]", oldVal, newVal);
298 }
299
300 verifyPerCpuNotSupported(metrics.getPerCpuUsage());
301
302 oldVal = metrics.getCpuUserUsage();
303 newVal = TimeUnit.MICROSECONDS.toNanos(getLongValueEntryFromFile("cpu.stat", "user_usec"));
304 if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
305 warn("cpu.stat[user_usec]", oldVal, newVal);
306 }
307
308 oldVal = metrics.getCpuSystemUsage();
309 newVal = TimeUnit.MICROSECONDS.toNanos(getLongValueEntryFromFile("cpu.stat", "system_usec"));
310 if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
311 warn("cpu.stat[system_usec]", oldVal, newVal);
312 }
313 }
314
315 @Override
316 public void testCpuSchedulingMetrics() {
317 Metrics metrics = Metrics.systemMetrics();
318 long oldVal = metrics.getCpuPeriod();
319 long newVal = getCpuPeriodValueFromFile("cpu.max");
320 if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
321 fail("cpu.max[$PERIOD]", oldVal, newVal);
383 oldVal = Arrays.stream(cpus).boxed().toArray(Integer[]::new);
384 Arrays.sort(oldVal);
385 cpusstr = getStringVal("cpuset.mems");
386 newVal = CgroupMetricsTester.convertCpuSetsToArray(cpusstr);
387 Arrays.sort(newVal);
388 if (Arrays.compare(oldVal, newVal) != 0) {
389 fail("cpuset.mems", Arrays.toString(oldVal),
390 Arrays.toString(newVal));
391 }
392
393 cpus = mapNullToEmpty(metrics.getEffectiveCpuSetMems());
394 oldVal = Arrays.stream(cpus).boxed().toArray(Integer[]::new);
395 Arrays.sort(oldVal);
396 cpusstr = getStringVal("cpuset.mems.effective");
397 newVal = CgroupMetricsTester.convertCpuSetsToArray(cpusstr);
398 Arrays.sort(newVal);
399 if (Arrays.compare(oldVal, newVal) != 0) {
400 fail("cpuset.mems.effective", Arrays.toString(oldVal),
401 Arrays.toString(newVal));
402 }
403
404 verifyNotSupported(metrics.getCpuSetMemoryPressure());
405 Asserts.assertNull(metrics.isCpuSetMemoryPressureEnabled(), "Should be not supported");
406 }
407
408 private int[] mapNullToEmpty(int[] cpus) {
409 if (cpus == null) {
410 // Not available. For sake of testing continue with an
411 // empty array.
412 cpus = new int[0];
413 }
414 return cpus;
415 }
416
417 @Override
418 public void testCpuConsumption() {
419 Metrics metrics = Metrics.systemMetrics();
420 // make system call
421 long newSysVal = metrics.getCpuSystemUsage();
422 long newUserVal = metrics.getCpuUserUsage();
423 long newUsage = metrics.getCpuUsage();
424
425 verifyPerCpuNotSupported(metrics.getPerCpuUsage());
426
427 // system/user CPU usage counters may be slowly increasing.
428 // allow for equal values for a pass
429 if (newSysVal < startSysVal) {
430 fail("getCpuSystemUsage", newSysVal, startSysVal);
431 }
432
433 // system/user CPU usage counters may be slowly increasing.
434 // allow for equal values for a pass
435 if (newUserVal < startUserVal) {
436 fail("getCpuUserUsage", newUserVal, startUserVal);
437 }
438
439 if (newUsage <= startUsage) {
440 fail("getCpuUsage", newUsage, startUsage);
441 }
442 }
443
444 @Override
445 public void testMemoryUsage() {
446 Metrics metrics = Metrics.systemMetrics();
447 long memoryMaxUsage = metrics.getMemoryMaxUsage();
448 long memoryUsage = metrics.getMemoryUsage();
449 long newMemoryMaxUsage = 0, newMemoryUsage = 0;
450
451 // allocate memory in a loop and check more than once for new values
452 // otherwise we might occasionally see the effect of decreasing new memory
453 // values. For example because the system could free up memory
454 byte[][] bytes = new byte[32][];
455 for (int i = 0; i < 32; i++) {
456 bytes[i] = new byte[8*1024*1024];
457 newMemoryUsage = metrics.getMemoryUsage();
458 if (newMemoryUsage > memoryUsage) {
459 break;
460 }
461 }
462 newMemoryMaxUsage = metrics.getMemoryMaxUsage();
463
464 if (newMemoryMaxUsage < memoryMaxUsage) {
465 fail("getMemoryMaxUsage", memoryMaxUsage,
466 newMemoryMaxUsage);
467 }
468
469 if (newMemoryUsage < memoryUsage) {
470 fail("getMemoryUsage", memoryUsage, newMemoryUsage);
471 }
472 }
473
474 @Override
475 public void testMisc() {
476 testIOStat();
477 }
478
479 private void testIOStat() {
480 Metrics metrics = Metrics.systemMetrics();
481 long oldVal = metrics.getBlkIOServiceCount();
482 long newVal = getIoStatAccumulate(new String[] { "rios", "wios" });
483 if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
484 fail("io.stat->rios/wios: ", oldVal, newVal);
485 }
486
487 oldVal = metrics.getBlkIOServiced();
494 private long getIoStatAccumulate(String[] matchNames) {
495 try {
496 return Files.lines(Paths.get(UNIFIED.getPath(), "io.stat"))
497 .map(line -> {
498 long accumulator = 0;
499 String[] tokens = line.split("\\s+");
500 for (String t: tokens) {
501 String[] keyVal = t.split("=");
502 if (keyVal.length != 2) {
503 continue;
504 }
505 for (String match: matchNames) {
506 if (match.equals(keyVal[0])) {
507 accumulator += Long.parseLong(keyVal[1]);
508 }
509 }
510 }
511 return accumulator;
512 }).collect(Collectors.summingLong(e -> e));
513 } catch (IOException e) {
514 return Metrics.LONG_RETVAL_NOT_SUPPORTED;
515 }
516 }
517 }
|
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package jdk.test.lib.containers.cgroup;
27
28 import java.io.IOException;
29 import java.nio.file.Files;
30 import java.nio.file.Path;
31 import java.nio.file.Paths;
32 import java.util.Arrays;
33 import java.util.List;
34 import java.util.concurrent.TimeUnit;
35 import java.util.stream.Collectors;
36
37 import jdk.internal.platform.CgroupSubsystem;
38 import jdk.internal.platform.Metrics;
39
40 public class MetricsTesterCgroupV2 implements CgroupMetricsTester {
41
42 private static final long UNLIMITED = -1;
43 private static final UnifiedController UNIFIED = new UnifiedController();
44 private static final String MAX = "max";
45 private static final int PER_CPU_SHARES = 1024;
46
47 private final long startSysVal;
48 private final long startUserVal;
49 private final long startUsage;
50
51 static class UnifiedController {
52
53 private static final String NAME = "unified";
54 private final String path;
55
56 UnifiedController() {
57 path = constructPath();
58 }
59
60 String getPath() {
61 return path;
62 }
135 Path filePath = Paths.get(UNIFIED.getPath(), file);
136 try {
137 return Files.lines(filePath).collect(Collectors.joining());
138 } catch (IOException e) {
139 return null;
140 }
141 }
142
143 private void fail(String metric, long oldVal, long newVal) {
144 CgroupMetricsTester.fail(UnifiedController.NAME, metric, oldVal, newVal);
145 }
146
147 private void fail(String metric, String oldVal, String newVal) {
148 CgroupMetricsTester.fail(UnifiedController.NAME, metric, oldVal, newVal);
149 }
150
151 private void warn(String metric, long oldVal, long newVal) {
152 CgroupMetricsTester.warn(UnifiedController.NAME, metric, oldVal, newVal);
153 }
154
155 private long getCpuShares(String file) {
156 long rawVal = getLongValueFromFile(file);
157 if (rawVal == 0 || rawVal == 100) {
158 return UNLIMITED;
159 }
160 int shares = (int)rawVal;
161 // CPU shares (OCI) value needs to get translated into
162 // a proper Cgroups v2 value. See:
163 // https://github.com/containers/crun/blob/master/crun.1.md#cpu-controller
164 //
165 // Use the inverse of (x == OCI value, y == cgroupsv2 value):
166 // ((262142 * y - 1)/9999) + 2 = x
167 //
168 int x = 262142 * shares - 1;
169 double frac = x/9999.0;
170 x = ((int)frac) + 2;
171 if ( x <= PER_CPU_SHARES ) {
172 return PER_CPU_SHARES; // mimic cgroups v1
173 }
174 int f = x/PER_CPU_SHARES;
210 Metrics metrics = Metrics.systemMetrics();
211
212 // User Memory
213 long oldVal = metrics.getMemoryFailCount();
214 long newVal = getLongValueEntryFromFile("memory.events", "max");
215 if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
216 fail("memory.events[max]", oldVal, newVal);
217 }
218
219 oldVal = metrics.getMemoryLimit();
220 newVal = getLongLimitValueFromFile("memory.max");
221 if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
222 fail("memory.max", oldVal, newVal);
223 }
224
225 oldVal = metrics.getMemoryUsage();
226 newVal = getLongValueFromFile("memory.current");
227 if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
228 fail("memory.current", oldVal, newVal);
229 }
230
231 oldVal = metrics.getTcpMemoryUsage();
232 newVal = getLongValueEntryFromFile("memory.stat", "sock");
233 if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
234 fail("memory.stat[sock]", oldVal, newVal);
235 }
236
237 oldVal = metrics.getMemoryAndSwapLimit();
238 newVal = getLongLimitValueFromFile("memory.swap.max");
239 if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
240 fail("memory.swap.max", oldVal, newVal);
241 }
242
243 oldVal = metrics.getMemoryAndSwapUsage();
244 newVal = getLongValueFromFile("memory.swap.current");
245 if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
246 fail("memory.swap.current", oldVal, newVal);
247 }
248
249 oldVal = metrics.getMemorySoftLimit();
250 newVal = getLongLimitValueFromFile("memory.high");
251 if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
252 fail("memory.high", oldVal, newVal);
253 }
254
255 }
256
257 @Override
258 public void testCpuAccounting() {
259 Metrics metrics = Metrics.systemMetrics();
260 long oldVal = metrics.getCpuUsage();
261 long newVal = TimeUnit.MICROSECONDS.toNanos(getLongValueEntryFromFile("cpu.stat", "usage_usec"));
262
263 if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
264 warn("cpu.stat[usage_usec]", oldVal, newVal);
265 }
266
267 oldVal = metrics.getCpuUserUsage();
268 newVal = TimeUnit.MICROSECONDS.toNanos(getLongValueEntryFromFile("cpu.stat", "user_usec"));
269 if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
270 warn("cpu.stat[user_usec]", oldVal, newVal);
271 }
272
273 oldVal = metrics.getCpuSystemUsage();
274 newVal = TimeUnit.MICROSECONDS.toNanos(getLongValueEntryFromFile("cpu.stat", "system_usec"));
275 if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
276 warn("cpu.stat[system_usec]", oldVal, newVal);
277 }
278 }
279
280 @Override
281 public void testCpuSchedulingMetrics() {
282 Metrics metrics = Metrics.systemMetrics();
283 long oldVal = metrics.getCpuPeriod();
284 long newVal = getCpuPeriodValueFromFile("cpu.max");
285 if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
286 fail("cpu.max[$PERIOD]", oldVal, newVal);
348 oldVal = Arrays.stream(cpus).boxed().toArray(Integer[]::new);
349 Arrays.sort(oldVal);
350 cpusstr = getStringVal("cpuset.mems");
351 newVal = CgroupMetricsTester.convertCpuSetsToArray(cpusstr);
352 Arrays.sort(newVal);
353 if (Arrays.compare(oldVal, newVal) != 0) {
354 fail("cpuset.mems", Arrays.toString(oldVal),
355 Arrays.toString(newVal));
356 }
357
358 cpus = mapNullToEmpty(metrics.getEffectiveCpuSetMems());
359 oldVal = Arrays.stream(cpus).boxed().toArray(Integer[]::new);
360 Arrays.sort(oldVal);
361 cpusstr = getStringVal("cpuset.mems.effective");
362 newVal = CgroupMetricsTester.convertCpuSetsToArray(cpusstr);
363 Arrays.sort(newVal);
364 if (Arrays.compare(oldVal, newVal) != 0) {
365 fail("cpuset.mems.effective", Arrays.toString(oldVal),
366 Arrays.toString(newVal));
367 }
368 }
369
370 private int[] mapNullToEmpty(int[] cpus) {
371 if (cpus == null) {
372 // Not available. For sake of testing continue with an
373 // empty array.
374 cpus = new int[0];
375 }
376 return cpus;
377 }
378
379 @Override
380 public void testCpuConsumption() {
381 Metrics metrics = Metrics.systemMetrics();
382 // make system call
383 long newSysVal = metrics.getCpuSystemUsage();
384 long newUserVal = metrics.getCpuUserUsage();
385 long newUsage = metrics.getCpuUsage();
386
387 // system/user CPU usage counters may be slowly increasing.
388 // allow for equal values for a pass
389 if (newSysVal < startSysVal) {
390 fail("getCpuSystemUsage", newSysVal, startSysVal);
391 }
392
393 // system/user CPU usage counters may be slowly increasing.
394 // allow for equal values for a pass
395 if (newUserVal < startUserVal) {
396 fail("getCpuUserUsage", newUserVal, startUserVal);
397 }
398
399 if (newUsage <= startUsage) {
400 fail("getCpuUsage", newUsage, startUsage);
401 }
402 }
403
404 @Override
405 public void testMemoryUsage() {
406 Metrics metrics = Metrics.systemMetrics();
407 long memoryUsage = metrics.getMemoryUsage();
408 long newMemoryUsage = 0;
409
410 // allocate memory in a loop and check more than once for new values
411 // otherwise we might occasionally see the effect of decreasing new memory
412 // values. For example because the system could free up memory
413 byte[][] bytes = new byte[32][];
414 for (int i = 0; i < 32; i++) {
415 bytes[i] = new byte[8*1024*1024];
416 newMemoryUsage = metrics.getMemoryUsage();
417 if (newMemoryUsage > memoryUsage) {
418 break;
419 }
420 }
421
422 if (newMemoryUsage < memoryUsage) {
423 fail("getMemoryUsage", memoryUsage, newMemoryUsage);
424 }
425 }
426
427 @Override
428 public void testMisc() {
429 testIOStat();
430 }
431
432 private void testIOStat() {
433 Metrics metrics = Metrics.systemMetrics();
434 long oldVal = metrics.getBlkIOServiceCount();
435 long newVal = getIoStatAccumulate(new String[] { "rios", "wios" });
436 if (!CgroupMetricsTester.compareWithErrorMargin(oldVal, newVal)) {
437 fail("io.stat->rios/wios: ", oldVal, newVal);
438 }
439
440 oldVal = metrics.getBlkIOServiced();
447 private long getIoStatAccumulate(String[] matchNames) {
448 try {
449 return Files.lines(Paths.get(UNIFIED.getPath(), "io.stat"))
450 .map(line -> {
451 long accumulator = 0;
452 String[] tokens = line.split("\\s+");
453 for (String t: tokens) {
454 String[] keyVal = t.split("=");
455 if (keyVal.length != 2) {
456 continue;
457 }
458 for (String match: matchNames) {
459 if (match.equals(keyVal[0])) {
460 accumulator += Long.parseLong(keyVal[1]);
461 }
462 }
463 }
464 return accumulator;
465 }).collect(Collectors.summingLong(e -> e));
466 } catch (IOException e) {
467 return CgroupSubsystem.LONG_RETVAL_UNLIMITED;
468 }
469 }
470 }
|