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