1 /*
   2  * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  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.jfr.jcmd;
  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.time.Duration;
  33 import java.time.Instant;
  34 import java.time.LocalDateTime;
  35 import java.time.LocalTime;
  36 import java.time.ZoneOffset;
  37 import java.util.ArrayList;
  38 import java.util.Collections;
  39 import java.util.List;
  40 
  41 import jdk.jfr.Event;
  42 import jdk.jfr.Recording;
  43 import jdk.test.lib.Asserts;
  44 import jdk.test.lib.process.OutputAnalyzer;
  45 
  46 /**
  47  * @test
  48  * @summary The test verifies JFR.dump command
  49  * @key jfr
  50  * @requires vm.hasJFR
  51  * @library /test/lib /test/jdk
  52  * @run main/othervm jdk.jfr.jcmd.TestJcmdDumpLimited
  53  */
  54 public class TestJcmdDumpLimited {
  55 
  56     static class TestEvent extends Event {
  57         int id;
  58         int number;
  59     }
  60 
  61     static class TestRecording {
  62         Instant time;
  63         final Recording r;
  64         Path path;
  65         int size;
  66         int total;
  67         int id;
  68         Instant now;
  69 
  70         TestRecording(int id, int events) throws IOException, InterruptedException {
  71             r = new Recording();
  72             r.start();
  73             for (int i = 0; i < events; i++) {
  74                 TestEvent event = new TestEvent();
  75                 event.id = id;
  76                 event.number = i;
  77                 event.commit();
  78                 if (i == events / 2) {
  79                     time = Instant.now();
  80                 }
  81             }
  82             r.stop();
  83             Thread.sleep(1);
  84             path = Paths.get("dump-" + id + ".jfr");
  85             r.dump(path);
  86             size = (int) Files.size(path);
  87             this.id = id;
  88             this.now = Instant.now();
  89         }
  90 
  91         public void close() {
  92             r.close();
  93         }
  94     }
  95 
  96     private static long totalSize;
  97     private static long lastFiveSize;
  98     private static long firstFiveSize;
  99     private static long middleSize;
 100     private static long centerSize;
 101     private static long lastSize;
 102 
 103     private static Instant middle;
 104     private static Instant centerLeft;
 105     private static Instant centerRight;
 106 
 107     public static void main(String[] args) throws Exception {
 108 
 109         List<TestRecording> recs = new ArrayList<>();
 110 
 111         for (int i = 0; i < 9; i++) {
 112             recs.add(new TestRecording(i, 100));
 113         }
 114         int last = 0;
 115         List<TestRecording> reversed = new ArrayList<>(recs);
 116         Collections.reverse(reversed);
 117         for (TestRecording r : reversed) {
 118             r.total = r.size + last;
 119             last += r.size;
 120         }
 121 
 122         for (TestRecording r : recs) {
 123             System.out.println("Recording " + r.id + ": size=" + r.size + " (total=" + r.total + ", time=" + r.now + ")");
 124         }
 125 
 126         centerLeft = recs.get(3).time;
 127         middle = recs.get(4).time;
 128         centerRight = recs.get(5).time;
 129 
 130         totalSize = size(recs, 0, 9);
 131         lastFiveSize = size(recs, 4, 5);
 132         firstFiveSize = size(recs, 0, 5);
 133         middleSize = size(recs, 4, 1);
 134         centerSize = size(recs, 3, 3);
 135         lastSize =  size(recs, 8, 1);
 136 
 137         testDump();
 138         testDumpMaxSize();
 139         testDumpMaxSizeSmall();
 140         testDumpBegin();
 141         testDumpEnd();
 142         testDumpBeginEndInstant();
 143         testDumpBeginEndLocalDateTime();
 144         testDumpBeginEndLocalTime();
 145         testDumpBeginEndSame();
 146         testDumpMaxAge();
 147         testDumpBeginEndRelative();
 148         testDumpTooEarly();
 149         testDumpTooLate();
 150         testDumpBeginMaxAge();
 151         TestDumpEndMaxage();
 152         testDumpEndBegin();
 153         testDumpInvalidTime();
 154     }
 155 
 156     private static int size(List<TestRecording> recs, int skip, int limit) {
 157         return recs.stream().skip(skip).limit(limit).mapToInt(r -> r.size).sum();
 158     }
 159 
 160     private static void testDumpEndBegin() throws Exception {
 161         Path testEndBegin = Paths.get("testEndBegin.jfr");
 162         OutputAnalyzer output = JcmdHelper.jcmd("JFR.dump", "filename=" + testEndBegin.toFile().getAbsolutePath(), "begin=" + Instant.now(), "end=" + Instant.now().minusSeconds(200));
 163         output.shouldContain("Dump failed, begin must preceed end.");
 164         assertMissingFile(testEndBegin);
 165     }
 166 
 167     private static void TestDumpEndMaxage() throws Exception {
 168         Path testEndMaxAge = Paths.get("testEndMaxAge.jfr");
 169         OutputAnalyzer output = JcmdHelper.jcmd("JFR.dump", "filename=" + testEndMaxAge.toFile().getAbsolutePath(), "end=" + Instant.now(), "maxage=2h");
 170         output.shouldContain("Dump failed, maxage can't be combined with begin or end.");
 171         assertMissingFile(testEndMaxAge);
 172     }
 173 
 174     private static Path testDumpBeginMaxAge() throws Exception {
 175         Path testBeginMaxAge = Paths.get("testBeginMaxAge.jfr");
 176         OutputAnalyzer output = JcmdHelper.jcmd("JFR.dump", "filename=" + testBeginMaxAge.toFile().getAbsolutePath(), "begin=" + Instant.now().minusSeconds(100), "maxage=2h");
 177         output.shouldContain("Dump failed, maxage can't be combined with begin or end.");
 178         assertMissingFile(testBeginMaxAge);
 179         return testBeginMaxAge;
 180     }
 181 
 182     private static void testDumpTooLate() throws Exception {
 183         Path missing = Paths.get("missing2.jfr");
 184         OutputAnalyzer output = JcmdHelper.jcmd("JFR.dump", "filename=" + missing.toFile().getAbsolutePath(), "begin=" + Instant.now().plus(Duration.ofHours(1)),
 185                 "end=" + Instant.now().plus(Duration.ofHours(2)));
 186         output.shouldContain("Dump failed. No data found in the specified interval.");
 187         assertMissingFile(missing);
 188     }
 189 
 190     private static void testDumpTooEarly() throws Exception {
 191         Path missing = Paths.get("missing.jfr");
 192         OutputAnalyzer output = JcmdHelper.jcmd("JFR.dump", "filename=" + missing.toFile().getAbsolutePath(), "end=" + Instant.now().minus(Duration.ofHours(1)));
 193         output.shouldContain("Dump failed. No data found in the specified interval.");
 194         assertMissingFile(missing);
 195     }
 196 
 197     private static void testDumpBeginEndRelative() throws IOException {
 198         Path testBeginEndRelative = Paths.get("testBeginEndRelative.jfr");
 199         JcmdHelper.jcmd("JFR.dump", "filename=" + testBeginEndRelative.toFile().getAbsolutePath(), "begin=-3h", "end=-0s");
 200         Asserts.assertEquals(totalSize, Files.size(testBeginEndRelative), "Expected dump with begin=-3h end=0s to contain data from all recordings");
 201         Files.delete(testBeginEndRelative);
 202     }
 203 
 204     private static void testDumpMaxAge() throws IOException {
 205         Path testMaxAge = Paths.get("testMaxAge.jfr");
 206         JcmdHelper.jcmd("JFR.dump", "filename=" + testMaxAge.toFile().getAbsolutePath(), "maxage=2h");
 207         Asserts.assertEquals(totalSize, Files.size(testMaxAge), "Expected dump with maxage=2h  to contain data from all recordings");
 208         Files.delete(testMaxAge);
 209     }
 210 
 211     private static void testDumpBeginEndSame() throws IOException {
 212         Path testBeginEnd = Paths.get("testBeginEndSame.jfr");
 213         JcmdHelper.jcmd("JFR.dump", "filename=" + testBeginEnd.toFile().getAbsolutePath(), "begin=" + middle, "end=" + middle);
 214         Asserts.assertEquals(middleSize, Files.size(testBeginEnd), "Expected dump with begin=" + middle + "end=" + middle + " contain data from middle recording");
 215         Files.delete(testBeginEnd);
 216     }
 217 
 218     private static void testDumpBeginEndInstant() throws IOException {
 219         Path testBeginEnd = Paths.get("testBeginEndInstant.jfr");
 220         JcmdHelper.jcmd("JFR.dump", "filename=" + testBeginEnd.toFile().getAbsolutePath(), "begin=" + centerLeft, "end=" + centerRight);
 221         Asserts.assertEquals(centerSize, Files.size(testBeginEnd), "Expected dump with begin=" + centerLeft + " end=" + centerRight + " contain data from the 'center'-recordings");
 222         Files.delete(testBeginEnd);
 223     }
 224 
 225     private static void testDumpBeginEndLocalDateTime() throws IOException {
 226         LocalDateTime centerLeftLocal = LocalDateTime.ofInstant(centerLeft, ZoneOffset.systemDefault());
 227         LocalDateTime centerRightLocal = LocalDateTime.ofInstant(centerRight, ZoneOffset.systemDefault());
 228         Path testBeginEnd = Paths.get("testBeginEndLocalDateTime.jfr");
 229         JcmdHelper.jcmd("JFR.dump", "filename=" + testBeginEnd.toFile().getAbsolutePath(), "begin=" + centerLeftLocal, "end=" + centerRightLocal);
 230         Asserts.assertEquals(centerSize, Files.size(testBeginEnd), "Expected dump with begin=" + centerLeftLocal + " end=" + centerRightLocal + " contain data from the 'center'-recordings");
 231         Files.delete(testBeginEnd);
 232     }
 233 
 234     private static void testDumpBeginEndLocalTime() throws IOException {
 235         LocalTime centerLeftLocal = LocalTime.ofInstant(centerLeft, ZoneOffset.systemDefault());
 236         LocalTime centerRightLocal = LocalTime.ofInstant(centerRight, ZoneOffset.systemDefault());
 237         Path testBeginEnd = Paths.get("testBeginEndLocalTime.jfr");
 238         JcmdHelper.jcmd("JFR.dump", "filename=" + testBeginEnd.toFile().getAbsolutePath(), "begin=" + centerLeftLocal, "end=" + centerRightLocal);
 239         Asserts.assertEquals(centerSize, Files.size(testBeginEnd), "Expected dump with begin=" + centerLeftLocal + " end=" + centerRightLocal + " contain data from the 'center'-recordings");
 240         Files.delete(testBeginEnd);
 241     }
 242 
 243     private static void testDumpEnd() throws IOException {
 244         Path testEnd = Paths.get("testEnd.jfr");
 245         JcmdHelper.jcmd("JFR.dump", "filename=" + testEnd.toFile().getAbsolutePath(), "end=" + middle);
 246         Asserts.assertEquals(firstFiveSize, Files.size(testEnd), "Expected dump with end=" + middle + " to contain data from the five first recordings");
 247         Files.delete(testEnd);
 248     }
 249 
 250     private static void testDumpBegin() throws IOException {
 251         Path testBegin = Paths.get("testBegin.jfr");
 252         JcmdHelper.jcmd("JFR.dump", "filename=" + testBegin.toFile().getAbsolutePath(), "begin=" + middle);
 253         Asserts.assertEquals(lastFiveSize, Files.size(testBegin), "Expected dump with begin=" + middle + " to contain data from the last five recordings");
 254         Files.delete(testBegin);
 255     }
 256 
 257     private static void testDumpMaxSize() throws IOException {
 258         Path testMaxSize = Paths.get("testMaxSize.jfr");
 259         JcmdHelper.jcmd("JFR.dump", "filename=" + testMaxSize.toFile().getAbsolutePath(), "maxsize=" + lastFiveSize);
 260         Asserts.assertEquals(lastFiveSize, Files.size(testMaxSize), "Expected dump with maxsize=" + lastFiveSize + " to contain data from the last five recordings");
 261         Files.delete(testMaxSize);
 262     }
 263 
 264     private static void testDumpMaxSizeSmall() throws IOException {
 265         Path testMaxSizeSmall = Paths.get("testMaxSizeSmall.jfr");
 266         JcmdHelper.jcmd("JFR.dump", "filename=" + testMaxSizeSmall.toFile().getAbsolutePath(), "maxsize=1k");
 267         Asserts.assertEquals(lastSize, Files.size(testMaxSizeSmall), "Expected dump with maxsize=1k to contain data from the last recording");
 268         Files.delete(testMaxSizeSmall);
 269     }
 270 
 271     private static void testDump() throws IOException {
 272         Path all = Paths.get("all.jfr");
 273         OutputAnalyzer output = JcmdHelper.jcmd("JFR.dump", "filename=" + all.toFile().getAbsolutePath());
 274         JcmdAsserts.assertRecordingDumpedToFile(output, all.toFile());
 275         Asserts.assertEquals(totalSize, Files.size(all), "Expected dump to be sum of all recordings");
 276         Files.delete(all);
 277     }
 278 
 279     private static void testDumpInvalidTime() throws Exception {
 280         Path invalidTime = Paths.get("invalidTime.jfr");
 281         OutputAnalyzer output = JcmdHelper.jcmd("JFR.dump", "filename=" + invalidTime.toFile().getAbsolutePath(), "begin=4711");
 282         output.shouldContain("Dump failed, not a valid begin time.");
 283         assertMissingFile(invalidTime);
 284     }
 285 
 286     private static void assertMissingFile(Path missing) throws Exception {
 287         if (Files.exists(missing)) {
 288             throw new Exception("Unexpected dumpfile found");
 289         }
 290     }
 291 
 292 }