|
1 /* |
|
2 * Copyright (c) 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.event.io; |
|
27 |
|
28 import java.io.File; |
|
29 import java.io.IOException; |
|
30 import java.io.RandomAccessFile; |
|
31 import java.time.Duration; |
|
32 import java.time.Instant; |
|
33 import java.util.ArrayList; |
|
34 import java.util.Comparator; |
|
35 import java.util.List; |
|
36 |
|
37 import jdk.jfr.Recording; |
|
38 import jdk.jfr.consumer.RecordedEvent; |
|
39 import jdk.test.lib.Asserts; |
|
40 import jdk.test.lib.jfr.Events; |
|
41 import jdk.test.lib.thread.TestThread; |
|
42 import jdk.test.lib.thread.XRun; |
|
43 |
|
44 |
|
45 /* |
|
46 * @test |
|
47 * @summary Verify the event time stamp and thread name |
|
48 * @key jfr |
|
49 * @library /test/lib /test/jdk |
|
50 * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:-UseFastUnorderedTimeStamps jdk.jfr.event.io.TestRandomAccessFileThread |
|
51 */ |
|
52 |
|
53 // TODO: This test should work without -XX:-UseFastUnorderedTimeStamps |
|
54 |
|
55 // The test uses 2 threads to read and write to a file. |
|
56 // The number of bytes in each read/write operation is increased by 1. |
|
57 // By looking at the number of bytes in each event, we know in what order |
|
58 // the events should arrive. This is used to verify the event time stamps. |
|
59 public class TestRandomAccessFileThread { |
|
60 private static final int OP_COUNT = 100; // Total number of read/write operations. |
|
61 private static volatile int writeCount = 0; // Number of writes executed. |
|
62 |
|
63 public static void main(String[] args) throws Throwable { |
|
64 File tmp = File.createTempFile("TestRandomAccessFileThread", ".tmp", new File(".")); |
|
65 tmp.deleteOnExit(); |
|
66 |
|
67 Recording recording = new Recording(); |
|
68 recording.enable(IOEvent.EVENT_FILE_READ).withThreshold(Duration.ofMillis(0)); |
|
69 recording.enable(IOEvent.EVENT_FILE_WRITE).withThreshold(Duration.ofMillis(0)); |
|
70 recording.start(); |
|
71 |
|
72 TestThread writerThread = new TestThread(new XRun() { |
|
73 @Override |
|
74 public void xrun() throws IOException { |
|
75 final byte[] buf = new byte[OP_COUNT]; |
|
76 for (int i = 0; i < buf.length; ++i) { |
|
77 buf[i] = (byte)((i + 'a') % 255); |
|
78 } |
|
79 try (RandomAccessFile raf = new RandomAccessFile(tmp, "rwd")) { |
|
80 for(int i = 0; i < OP_COUNT; ++i) { |
|
81 raf.write(buf, 0, i + 1); |
|
82 writeCount++; |
|
83 } |
|
84 } |
|
85 }}, "TestWriterThread"); |
|
86 |
|
87 TestThread readerThread = new TestThread(new XRun() { |
|
88 @Override |
|
89 public void xrun() throws IOException { |
|
90 try (RandomAccessFile raf = new RandomAccessFile(tmp, "r")) { |
|
91 byte[] buf = new byte[OP_COUNT]; |
|
92 for(int i = 0; i < OP_COUNT; ++i) { |
|
93 while (writeCount <= i) { |
|
94 // No more data to read. Wait for writer thread. |
|
95 Thread.yield(); |
|
96 } |
|
97 int expectedSize = i + 1; |
|
98 int actualSize = raf.read(buf, 0, expectedSize); |
|
99 Asserts.assertEquals(actualSize, expectedSize, "Wrong read size. Probably test error."); |
|
100 } |
|
101 } |
|
102 }}, "TestReaderThread"); |
|
103 |
|
104 readerThread.start(); |
|
105 writerThread.start(); |
|
106 writerThread.joinAndThrow(); |
|
107 readerThread.joinAndThrow(); |
|
108 recording.stop(); |
|
109 |
|
110 List<RecordedEvent> events = Events.fromRecording(recording); |
|
111 events.sort(new EventComparator()); |
|
112 |
|
113 List<RecordedEvent> readEvents = new ArrayList<>(); |
|
114 List<RecordedEvent> writeEvents = new ArrayList<>(); |
|
115 for (RecordedEvent event : events) { |
|
116 if (!isOurEvent(event, tmp)) { |
|
117 continue; |
|
118 } |
|
119 logEventSummary(event); |
|
120 if (Events.isEventType(event,IOEvent.EVENT_FILE_READ)) { |
|
121 readEvents.add(event); |
|
122 } else { |
|
123 writeEvents.add(event); |
|
124 } |
|
125 } |
|
126 |
|
127 verifyThread(readEvents, readerThread); |
|
128 verifyThread(writeEvents, writerThread); |
|
129 verifyBytes(readEvents, "bytesRead"); |
|
130 verifyBytes(writeEvents, "bytesWritten"); |
|
131 verifyTimes(readEvents); |
|
132 verifyTimes(writeEvents); |
|
133 verifyReadWriteTimes(readEvents, writeEvents); |
|
134 |
|
135 Asserts.assertEquals(readEvents.size(), OP_COUNT, "Wrong number of read events"); |
|
136 Asserts.assertEquals(writeEvents.size(), OP_COUNT, "Wrong number of write events"); |
|
137 } |
|
138 |
|
139 private static void logEventSummary(RecordedEvent event) { |
|
140 boolean isRead = Events.isEventType(event, IOEvent.EVENT_FILE_READ); |
|
141 String name = isRead ? "read " : "write"; |
|
142 String bytesField = isRead ? "bytesRead" : "bytesWritten"; |
|
143 long bytes = Events.assertField(event, bytesField).getValue(); |
|
144 long commit = Events.assertField(event, "startTime").getValue(); |
|
145 Instant start = event.getStartTime(); |
|
146 Instant end = event.getEndTime(); |
|
147 System.out.printf("%s: bytes=%d, commit=%d, start=%s, end=%s%n", name, bytes, commit, start, end); |
|
148 } |
|
149 |
|
150 private static void verifyThread(List<RecordedEvent> events, Thread thread) { |
|
151 events.stream().forEach(e -> Events.assertEventThread(e, thread)); |
|
152 } |
|
153 |
|
154 private static void verifyBytes(List<RecordedEvent> events, String fieldName) { |
|
155 long expectedBytes = 0; |
|
156 for (RecordedEvent event : events) { |
|
157 Events.assertField(event, fieldName).equal(++expectedBytes); |
|
158 } |
|
159 } |
|
160 |
|
161 // Verify that all times are increasing |
|
162 private static void verifyTimes(List<RecordedEvent> events) { |
|
163 RecordedEvent prev = null; |
|
164 for (RecordedEvent curr : events) { |
|
165 if (prev != null) { |
|
166 try { |
|
167 Asserts.assertGreaterThanOrEqual(curr.getStartTime(), prev.getStartTime(), "Wrong startTime"); |
|
168 Asserts.assertGreaterThanOrEqual(curr.getEndTime(), prev.getEndTime(), "Wrong endTime"); |
|
169 long commitPrev = Events.assertField(prev, "startTime").getValue(); |
|
170 long commitCurr = Events.assertField(curr, "startTime").getValue(); |
|
171 Asserts.assertGreaterThanOrEqual(commitCurr, commitPrev, "Wrong commitTime"); |
|
172 } catch (Exception e) { |
|
173 System.out.println("Error: " + e.getMessage()); |
|
174 System.out.println("Prev Event: " + prev); |
|
175 System.out.println("Curr Event: " + curr); |
|
176 throw e; |
|
177 } |
|
178 } |
|
179 prev = curr; |
|
180 } |
|
181 } |
|
182 |
|
183 // Verify that all times are increasing |
|
184 private static void verifyReadWriteTimes(List<RecordedEvent> readEvents, List<RecordedEvent> writeEvents) { |
|
185 List<RecordedEvent> events = new ArrayList<>(); |
|
186 events.addAll(readEvents); |
|
187 events.addAll(writeEvents); |
|
188 events.sort(new EventComparator()); |
|
189 |
|
190 int countRead = 0; |
|
191 int countWrite = 0; |
|
192 for (RecordedEvent event : events) { |
|
193 if (Events.isEventType(event, IOEvent.EVENT_FILE_READ)) { |
|
194 ++countRead; |
|
195 } else { |
|
196 ++countWrite; |
|
197 } |
|
198 // We can not read from the file before it has been written. |
|
199 // This check verifies that times of different threads are correct. |
|
200 // Since the read and write are from different threads, it is possible that the read |
|
201 // is committed before the same write. |
|
202 // But read operation may only be 1 step ahead of the write operation. |
|
203 Asserts.assertLessThanOrEqual(countRead, countWrite + 1, "read must be after write"); |
|
204 } |
|
205 } |
|
206 |
|
207 private static boolean isOurEvent(RecordedEvent event, File file) { |
|
208 if (!Events.isEventType(event, IOEvent.EVENT_FILE_READ) && |
|
209 !Events.isEventType(event, IOEvent.EVENT_FILE_WRITE)) { |
|
210 return false; |
|
211 } |
|
212 String path = Events.assertField(event, "path").getValue(); |
|
213 return file.getPath().equals(path); |
|
214 } |
|
215 |
|
216 private static class EventComparator implements Comparator<RecordedEvent> { |
|
217 @Override |
|
218 public int compare(RecordedEvent a, RecordedEvent b) { |
|
219 long commitA = Events.assertField(a, "startTime").getValue(); |
|
220 long commitB = Events.assertField(b, "startTime").getValue(); |
|
221 return Long.compare(commitA, commitB); |
|
222 } |
|
223 } |
|
224 |
|
225 } |