• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2019 Google LLC
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package io.perfmark.java9;
18 
19 import io.perfmark.impl.Generator;
20 import io.perfmark.impl.Mark;
21 import io.perfmark.impl.MarkHolder;
22 import java.lang.invoke.MethodHandles;
23 import java.lang.invoke.VarHandle;
24 import java.util.ArrayDeque;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Collections;
28 import java.util.ConcurrentModificationException;
29 import java.util.Deque;
30 import java.util.List;
31 
32 /** VarHandleMarkHolder is a MarkHolder optimized for wait free writes and few reads. */
33 final class VarHandleMarkHolder extends MarkHolder {
34   private static final long GEN_MASK = (1 << Generator.GEN_OFFSET) - 1;
35   private static final long START_OP = 1; // Mark.Operation.TASK_START.ordinal();
36   private static final long START_S_OP = 2;
37   private static final long START_T_OP = 3; // Mark.Operation.TASK_START_T.ordinal();
38   private static final long STOP_OP = 4; // Mark.Operation.TASK_END.ordinal();
39   private static final long STOP_V_OP = 5;
40   private static final long STOP_T_OP = 6; // Mark.Operation.TASK_END_T.ordinal();
41   private static final long STOP_S_OP = 7;
42   private static final long EVENT_OP = 8; // Mark.Operation.EVENT.ordinal();
43   private static final long EVENT_T_OP = 9; // Mark.Operation.EVENT_T.ordinal();
44   private static final long EVENT_S_OP = 10;
45   private static final long LINK_OP = 11; // Mark.Operation.LINK.ordinal();
46   private static final long ATTACH_T_OP = 12; // Mark.Operation.ATTACH_TAG.ordinal();
47   private static final long ATTACH_SS_OP = 13;
48   private static final long ATTACH_SN_OP = 14;
49   private static final long ATTACH_SNN_OP = 15;
50 
51   private static final VarHandle IDX;
52   private static final VarHandle STRINGS;
53   private static final VarHandle LONGS;
54 
55   static {
56     try {
57       IDX = MethodHandles.lookup().findVarHandle(VarHandleMarkHolder.class, "idx", long.class);
58       STRINGS = MethodHandles.arrayElementVarHandle(String[].class);
59       LONGS = MethodHandles.arrayElementVarHandle(long[].class);
60     } catch (NoSuchFieldException | IllegalAccessException e) {
61       throw new RuntimeException(e);
62     }
63   }
64 
65   private final int maxEvents;
66   private final long maxEventsMax;
67 
68   // where to write to next
69   @SuppressWarnings("unused") // Used Reflectively
70   private volatile long idx;
71 
72   private final String[] taskNames;
73   private final String[] tagNames;
74   private final long[] tagIds;
75   private final long[] nanoTimes;
76   private final long[] genOps;
77 
VarHandleMarkHolder()78   VarHandleMarkHolder() {
79     this(32768);
80   }
81 
VarHandleMarkHolder(int maxEvents)82   VarHandleMarkHolder(int maxEvents) {
83     if (((maxEvents - 1) & maxEvents) != 0) {
84       throw new IllegalArgumentException(maxEvents + " is not a power of two");
85     }
86     if (maxEvents <= 0) {
87       throw new IllegalArgumentException(maxEvents + " is not positive");
88     }
89     this.maxEvents = maxEvents;
90     this.maxEventsMax = maxEvents - 1L;
91     this.taskNames = new String[maxEvents];
92     this.tagNames = new String[maxEvents];
93     this.tagIds = new long[maxEvents];
94     this.nanoTimes = new long[maxEvents];
95     this.genOps = new long[maxEvents];
96   }
97 
98   @Override
start(long gen, String taskName, String tagName, long tagId, long nanoTime)99   public void start(long gen, String taskName, String tagName, long tagId, long nanoTime) {
100     long localIdx = (long) IDX.get(this);
101     int i = (int) (localIdx & maxEventsMax);
102     STRINGS.setOpaque(taskNames, i, taskName);
103     STRINGS.setOpaque(tagNames, i, tagName);
104     LONGS.setOpaque(tagIds, i, tagId);
105     LONGS.setOpaque(nanoTimes, i, nanoTime);
106     LONGS.setOpaque(genOps, i, gen + START_T_OP);
107     IDX.setRelease(this, localIdx + 1);
108     VarHandle.storeStoreFence();
109   }
110 
111   @Override
start(long gen, String taskName, long nanoTime)112   public void start(long gen, String taskName, long nanoTime) {
113     long localIdx = (long) IDX.get(this);
114     int i = (int) (localIdx & maxEventsMax);
115     STRINGS.setOpaque(taskNames, i, taskName);
116     LONGS.setOpaque(nanoTimes, i, nanoTime);
117     LONGS.setOpaque(genOps, i, gen + START_OP);
118     IDX.setRelease(this, localIdx + 1);
119     VarHandle.storeStoreFence();
120   }
121 
122   @Override
start(long gen, String taskName, String subTaskName, long nanoTime)123   public void start(long gen, String taskName, String subTaskName, long nanoTime) {
124     long localIdx = (long) IDX.get(this);
125     int i = (int) (localIdx & maxEventsMax);
126     STRINGS.setOpaque(taskNames, i, taskName);
127     STRINGS.setOpaque(tagNames, i, subTaskName);
128     LONGS.setOpaque(nanoTimes, i, nanoTime);
129     LONGS.setOpaque(genOps, i, gen + START_S_OP);
130     IDX.setRelease(this, localIdx + 1);
131     VarHandle.storeStoreFence();
132   }
133 
134   @Override
link(long gen, long linkId)135   public void link(long gen, long linkId) {
136     long localIdx = (long) IDX.get(this);
137     int i = (int) (localIdx & maxEventsMax);
138     LONGS.setOpaque(tagIds, i, linkId);
139     LONGS.setOpaque(genOps, i, gen + LINK_OP);
140     IDX.setRelease(this, localIdx + 1);
141     VarHandle.storeStoreFence();
142   }
143 
144   @Override
stop(long gen, long nanoTime)145   public void stop(long gen, long nanoTime) {
146     long localIdx = (long) IDX.get(this);
147     int i = (int) (localIdx & maxEventsMax);
148     LONGS.setOpaque(nanoTimes, i, nanoTime);
149     LONGS.setOpaque(genOps, i, gen + STOP_V_OP);
150     IDX.setRelease(this, localIdx + 1);
151     VarHandle.storeStoreFence();
152   }
153 
154   @Override
stop(long gen, String taskName, String tagName, long tagId, long nanoTime)155   public void stop(long gen, String taskName, String tagName, long tagId, long nanoTime) {
156     long localIdx = (long) IDX.get(this);
157     int i = (int) (localIdx & maxEventsMax);
158     STRINGS.setOpaque(taskNames, i, taskName);
159     STRINGS.setOpaque(tagNames, i, tagName);
160     LONGS.setOpaque(tagIds, i, tagId);
161     LONGS.setOpaque(nanoTimes, i, nanoTime);
162     LONGS.setOpaque(genOps, i, gen + STOP_T_OP);
163     IDX.setRelease(this, localIdx + 1);
164     VarHandle.storeStoreFence();
165   }
166 
167   @Override
stop(long gen, String taskName, long nanoTime)168   public void stop(long gen, String taskName, long nanoTime) {
169     long localIdx = (long) IDX.get(this);
170     int i = (int) (localIdx & maxEventsMax);
171     STRINGS.setOpaque(taskNames, i, taskName);
172     LONGS.setOpaque(nanoTimes, i, nanoTime);
173     LONGS.setOpaque(genOps, i, gen + STOP_OP);
174     IDX.setRelease(this, localIdx + 1);
175     VarHandle.storeStoreFence();
176   }
177 
178   @Override
stop(long gen, String taskName, String subTaskName, long nanoTime)179   public void stop(long gen, String taskName, String subTaskName, long nanoTime) {
180     long localIdx = (long) IDX.get(this);
181     int i = (int) (localIdx & maxEventsMax);
182     STRINGS.setOpaque(taskNames, i, taskName);
183     STRINGS.setOpaque(tagNames, i, subTaskName);
184     LONGS.setOpaque(nanoTimes, i, nanoTime);
185     LONGS.setOpaque(genOps, i, gen + STOP_S_OP);
186     IDX.setRelease(this, localIdx + 1);
187     VarHandle.storeStoreFence();
188   }
189 
190   @Override
event(long gen, String eventName, String tagName, long tagId, long nanoTime)191   public void event(long gen, String eventName, String tagName, long tagId, long nanoTime) {
192     long localIdx = (long) IDX.get(this);
193     int i = (int) (localIdx & maxEventsMax);
194     STRINGS.setOpaque(taskNames, i, eventName);
195     STRINGS.setOpaque(tagNames, i, tagName);
196     LONGS.setOpaque(tagIds, i, tagId);
197     LONGS.setOpaque(nanoTimes, i, nanoTime);
198     LONGS.setOpaque(genOps, i, gen + EVENT_T_OP);
199     IDX.setRelease(this, localIdx + 1);
200     VarHandle.storeStoreFence();
201   }
202 
203   @Override
event(long gen, String eventName, long nanoTime)204   public void event(long gen, String eventName, long nanoTime) {
205     long localIdx = (long) IDX.get(this);
206     int i = (int) (localIdx & maxEventsMax);
207     STRINGS.setOpaque(taskNames, i, eventName);
208     LONGS.setOpaque(nanoTimes, i, nanoTime);
209     LONGS.setOpaque(genOps, i, gen + EVENT_OP);
210     IDX.setRelease(this, localIdx + 1);
211     VarHandle.storeStoreFence();
212   }
213 
214   @Override
event(long gen, String eventName, String subEventName, long nanoTime)215   public void event(long gen, String eventName, String subEventName, long nanoTime) {
216     long localIdx = (long) IDX.get(this);
217     int i = (int) (localIdx & maxEventsMax);
218     STRINGS.setOpaque(taskNames, i, eventName);
219     STRINGS.setOpaque(tagNames, i, subEventName);
220     LONGS.setOpaque(nanoTimes, i, nanoTime);
221     LONGS.setOpaque(genOps, i, gen + EVENT_S_OP);
222     IDX.setRelease(this, localIdx + 1);
223     VarHandle.storeStoreFence();
224   }
225 
226   @Override
attachTag(long gen, String tagName, long tagId)227   public void attachTag(long gen, String tagName, long tagId) {
228     long localIdx = (long) IDX.get(this);
229     int i = (int) (localIdx & maxEventsMax);
230     STRINGS.setOpaque(tagNames, i, tagName);
231     LONGS.setOpaque(tagIds, i, tagId);
232     LONGS.setOpaque(genOps, i, gen + ATTACH_T_OP);
233     IDX.setRelease(this, localIdx + 1);
234     VarHandle.storeStoreFence();
235   }
236 
237   @Override
attachKeyedTag(long gen, String name, long value)238   public void attachKeyedTag(long gen, String name, long value) {
239     long localIdx = (long) IDX.get(this);
240     int i = (int) (localIdx & maxEventsMax);
241     STRINGS.setOpaque(tagNames, i, name);
242     LONGS.setOpaque(tagIds, i, value);
243     LONGS.setOpaque(genOps, i, gen + ATTACH_SN_OP);
244     IDX.setRelease(this, localIdx + 1);
245     VarHandle.storeStoreFence();
246   }
247 
248   @Override
attachKeyedTag(long gen, String name, long value0, long value1)249   public void attachKeyedTag(long gen, String name, long value0, long value1) {
250     long localIdx = (long) IDX.get(this);
251     int i = (int) (localIdx & maxEventsMax);
252     STRINGS.setOpaque(tagNames, i, name);
253     LONGS.setOpaque(tagIds, i, value0);
254     LONGS.setOpaque(nanoTimes, i, value1);
255     LONGS.setOpaque(genOps, i, gen + ATTACH_SNN_OP);
256     IDX.setRelease(this, localIdx + 1);
257     VarHandle.storeStoreFence();
258   }
259 
260   @Override
attachKeyedTag(long gen, String name, String value)261   public void attachKeyedTag(long gen, String name, String value) {
262     long localIdx = (long) IDX.get(this);
263     int i = (int) (localIdx & maxEventsMax);
264     STRINGS.setOpaque(tagNames, i, name);
265     STRINGS.setOpaque(taskNames, i, value);
266     LONGS.setOpaque(genOps, i, gen + ATTACH_SS_OP);
267     IDX.setRelease(this, localIdx + 1);
268     VarHandle.storeStoreFence();
269   }
270 
271   @Override
resetForTest()272   public void resetForTest() {
273     Arrays.fill(taskNames, null);
274     Arrays.fill(tagNames, null);
275     Arrays.fill(tagIds, 0);
276     Arrays.fill(nanoTimes, 0);
277     Arrays.fill(genOps, 0);
278     IDX.setRelease(this, 0L);
279     VarHandle.storeStoreFence();
280   }
281 
282   @Override
read(boolean concurrentWrites)283   public List<Mark> read(boolean concurrentWrites) {
284     final String[] localTaskNames = new String[maxEvents];
285     final String[] localTagNames = new String[maxEvents];
286     final long[] localTagIds = new long[maxEvents];
287     final long[] localNanoTimes = new long[maxEvents];
288     final long[] localGenOps = new long[maxEvents];
289     long startIdx = (long) IDX.getOpaque(this);
290     VarHandle.loadLoadFence();
291     int size = (int) Math.min(startIdx, maxEvents);
292     for (int i = 0; i < size; i++) {
293       localTaskNames[i] = (String) STRINGS.getOpaque(taskNames, i);
294       localTagNames[i] = (String) STRINGS.getOpaque(tagNames, i);
295       localTagIds[i] = (long) LONGS.getOpaque(tagIds, i);
296       localNanoTimes[i] = (long) LONGS.getOpaque(nanoTimes, i);
297       localGenOps[i] = (long) LONGS.getOpaque(genOps, i);
298     }
299     VarHandle.loadLoadFence();
300     long endIdx = (long) IDX.getOpaque(this);
301     if (endIdx < startIdx) {
302       throw new AssertionError();
303     }
304     // If we are reading from ourselves (such as in a test), we can assume there isn't an in
305     // progress write modifying the oldest entry.  Additionally, if the writer has not yet
306     // wrapped around, the last entry cannot have been corrupted.
307     boolean tailValid = !concurrentWrites || endIdx < maxEvents - 1;
308     endIdx += !tailValid ? 1 : 0;
309     long eventsToDrop = endIdx - startIdx;
310     final Deque<Mark> marks = new ArrayDeque<>(size);
311     for (int i = 0; i < size - eventsToDrop; i++) {
312       int readIdx = (int) ((startIdx - i - 1) & maxEventsMax);
313       long gen = localGenOps[readIdx] & ~GEN_MASK;
314       int opVal = (int) (localGenOps[readIdx] & GEN_MASK);
315       switch (opVal) {
316         case (int) START_T_OP:
317           marks.addFirst(Mark.tag(gen, localTagNames[readIdx], localTagIds[readIdx]));
318           // fallthrough
319         case (int) START_OP:
320           marks.addFirst(Mark.taskStart(gen, localNanoTimes[readIdx], localTaskNames[readIdx]));
321           break;
322         case (int) START_S_OP:
323           marks.addFirst(
324               Mark.taskStart(
325                   gen, localNanoTimes[readIdx], localTaskNames[readIdx], localTagNames[readIdx]));
326           break;
327         case (int) STOP_V_OP:
328           marks.addFirst(Mark.taskEnd(gen, localNanoTimes[readIdx]));
329           break;
330         case (int) STOP_S_OP:
331           marks.addFirst(
332               Mark.taskEnd(
333                   gen, localNanoTimes[readIdx], localTaskNames[readIdx], localTagNames[readIdx]));
334           break;
335         case (int) STOP_OP:
336           marks.addFirst(Mark.taskEnd(gen, localNanoTimes[readIdx], localTaskNames[readIdx]));
337           break;
338         case (int) STOP_T_OP:
339           marks.addFirst(Mark.taskEnd(gen, localNanoTimes[readIdx], localTaskNames[readIdx]));
340           marks.addFirst(Mark.tag(gen, localTagNames[readIdx], localTagIds[readIdx]));
341           break;
342         case (int) EVENT_OP:
343           marks.addFirst(Mark.event(gen, localNanoTimes[readIdx], localTaskNames[readIdx]));
344           break;
345         case (int) EVENT_T_OP:
346           marks.addFirst(
347               Mark.event(
348                   gen,
349                   localNanoTimes[readIdx],
350                   localTaskNames[readIdx],
351                   localTagNames[readIdx],
352                   localTagIds[readIdx]));
353           break;
354         case (int) EVENT_S_OP:
355           marks.addFirst(
356               Mark.event(
357                   gen, localNanoTimes[readIdx], localTaskNames[readIdx], localTagNames[readIdx]));
358           break;
359         case (int) LINK_OP:
360           marks.addFirst(Mark.link(gen, localTagIds[readIdx]));
361           break;
362         case (int) ATTACH_T_OP:
363           marks.addFirst(Mark.tag(gen, localTagNames[readIdx], localTagIds[readIdx]));
364           break;
365         case (int) ATTACH_SS_OP:
366           marks.addFirst(Mark.keyedTag(gen, localTagNames[readIdx], localTaskNames[readIdx]));
367           break;
368         case (int) ATTACH_SN_OP:
369           marks.addFirst(Mark.keyedTag(gen, localTagNames[readIdx], localTagIds[readIdx]));
370           break;
371         case (int) ATTACH_SNN_OP:
372           marks.addFirst(
373               Mark.keyedTag(
374                   gen, localTagNames[readIdx], localTagIds[readIdx], localNanoTimes[readIdx]));
375           break;
376         default:
377           throw new ConcurrentModificationException("Read of storage was not threadsafe " + opVal);
378       }
379     }
380 
381     return Collections.unmodifiableList(new ArrayList<>(marks));
382   }
383 
384   @Override
385   public int maxMarks() {
386     return maxEvents;
387   }
388 }
389