• 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.impl;
18 
19 import io.perfmark.Impl;
20 import io.perfmark.Link;
21 import io.perfmark.StringFunction;
22 import io.perfmark.Tag;
23 import java.util.concurrent.atomic.AtomicLong;
24 import java.util.logging.Level;
25 import java.util.logging.LogRecord;
26 import java.util.logging.Logger;
27 import javax.annotation.Nullable;
28 
29 final class SecretPerfMarkImpl {
30 
31   public static final class PerfMarkImpl extends Impl {
32     private static final int ENABLED_BIT_SPACE = 2;
33     private static final int GEN_TIMESTAMP_SPACE = 54;
34     private static final long MAX_MIBROS = (1L << GEN_TIMESTAMP_SPACE) - 1;
35     private static final Tag NO_TAG = packTag(Mark.NO_TAG_NAME, Mark.NO_TAG_ID);
36     private static final Link NO_LINK = packLink(Mark.NO_LINK_ID);
37     private static final long INCREMENT = 1L << Generator.GEN_OFFSET;
38 
39     private static final AtomicLong linkIdAlloc = new AtomicLong(1);
40     private static final Generator generator;
41 
42     // May be null if debugging is disabled.
43     private static final Object logger;
44 
45     /**
46      * This is the generation of the recorded tasks.  The bottom 8 bits [0-7] are reserved for opcode packing.
47      * Bit 9 [8] is used for detecting if PerfMark is enabled or not.  Bit 10 [9] is unused.  Bits 11-64 [10-647]
48      * are used for storing the time since Perfmark Was last / enabled or disabled.  The units are in nanoseconds/1024,
49      * or (inaccurately) called mibros (like micros, but power of 2 based).
50      */
51     private static long actualGeneration;
52 
53     static {
54       assert ENABLED_BIT_SPACE + Generator.GEN_OFFSET + GEN_TIMESTAMP_SPACE <= 64;
55       Generator gen = null;
56       Throwable[] problems = new Throwable[4];
57       // Avoid using a for-loop for this code, as it makes it easier for tools like Proguard to rewrite.
58       try {
59         Class<?> clz = Class.forName("io.perfmark.java7.SecretMethodHandleGenerator$MethodHandleGenerator");
60         gen = clz.asSubclass(Generator.class).getConstructor().newInstance();
61       } catch (Throwable t) {
62         problems[0] = t;
63       }
64       if (gen == null) {
65         try {
66           Class<?> clz = Class.forName("io.perfmark.java9.SecretVarHandleGenerator$VarHandleGenerator");
67           gen = clz.asSubclass(Generator.class).getConstructor().newInstance();
68         } catch (Throwable t) {
69           problems[1] = t;
70         }
71       }
72       if (gen == null) {
73         try {
74           Class<?> clz = Class.forName("io.perfmark.java6.SecretVolatileGenerator$VolatileGenerator");
75           gen = clz.asSubclass(Generator.class).getConstructor().newInstance();
76         } catch (Throwable t) {
77           problems[2] = t;
78         }
79       }
80       if (gen == null) {
81         generator = new NoopGenerator();
82       } else {
83         generator = gen;
84       }
85 
86       boolean startEnabled = false;
87       boolean startEnabledSuccess = false;
88       try {
89         if ((startEnabled = Boolean.getBoolean("io.perfmark.PerfMark.startEnabled"))) {
90           startEnabledSuccess = setEnabledQuiet(startEnabled, Generator.INIT_NANO_TIME);
91         }
92       } catch (Throwable t) {
93         problems[3] = t;
94       }
95 
96       Object log = null;
97       try {
98         if (Boolean.getBoolean("io.perfmark.PerfMark.debug")) {
99           Logger localLogger = Logger.getLogger(PerfMarkImpl.class.getName());
100           log = localLogger;
101           for (Throwable problem : problems) {
102             if (problem == null) {
103               continue;
104             }
localLogger.log(Level.FINE, "Error loading Generator", problem)105             localLogger.log(Level.FINE, "Error loading Generator", problem);
106           }
localLogger.log(Level.FINE, "Using {0}", new Object[] {generator.getClass().getName()})107           localLogger.log(Level.FINE, "Using {0}", new Object[] {generator.getClass().getName()});
logEnabledChange(startEnabled, startEnabledSuccess)108           logEnabledChange(startEnabled, startEnabledSuccess);
109         }
110       } catch (Throwable t) {
111         // ignore
112       }
113       logger = log;
114     }
115 
PerfMarkImpl(Tag key)116     public PerfMarkImpl(Tag key) {
117       super(key);
118     }
119 
120     @Override
setEnabled(boolean value)121     protected synchronized void setEnabled(boolean value) {
122       boolean changed = setEnabledQuiet(value, System.nanoTime());
123       logEnabledChange(value, changed);
124     }
125 
126     @Override
setEnabled(boolean value, boolean overload)127     protected synchronized boolean setEnabled(boolean value, boolean overload) {
128       boolean changed = setEnabledQuiet(value, System.nanoTime());
129       logEnabledChange(value, changed);
130       return changed;
131     }
132 
logEnabledChange(boolean value, boolean success)133     private static synchronized void logEnabledChange(boolean value, boolean success) {
134       if (success && logger != null) {
135         Logger localLogger = (Logger) logger;
136         if (localLogger.isLoggable(Level.FINE)) {
137           localLogger.fine((value ? "Enabling" : "Disabling") + " PerfMark recorder");
138         }
139       }
140     }
141 
142     /** Returns true if successfully changed. */
setEnabledQuiet(boolean value, long now)143     private static synchronized boolean setEnabledQuiet(boolean value, long now) {
144       if (isEnabled(actualGeneration) == value) {
145         return false;
146       }
147       if (actualGeneration == Generator.FAILURE) {
148         return false;
149       }
150       long nanoDiff = now - Generator.INIT_NANO_TIME;
151       generator.setGeneration(actualGeneration = nextGeneration(actualGeneration, nanoDiff));
152       return true;
153     }
154 
155     // VisibleForTesting
nextGeneration(final long currentGeneration, final long nanosSinceInit)156     static long nextGeneration(final long currentGeneration, final long nanosSinceInit) {
157       assert currentGeneration != Generator.FAILURE;
158       long currentMibros = mibrosFromGeneration(currentGeneration);
159       long mibrosSinceInit = Math.min(mibrosFromNanos(nanosSinceInit), MAX_MIBROS); // 54bits
160       boolean nextEnabled = !isEnabled(currentGeneration);
161       long nextMibros;
162       if (mibrosSinceInit > currentMibros) {
163         nextMibros = mibrosSinceInit;
164       } else {
165         nextMibros = currentMibros + (nextEnabled ? 1 : 0);
166       }
167       if (nextMibros > MAX_MIBROS || nextMibros < 0) {
168         return Generator.FAILURE;
169       }
170       long enabledMask = nextEnabled ? INCREMENT : 0;
171       long mibroMask = (nextMibros << (Generator.GEN_OFFSET + ENABLED_BIT_SPACE));
172       assert (enabledMask & mibroMask) == 0;
173       return mibroMask | enabledMask;
174     }
175 
mibrosFromGeneration(long currentGeneration)176     private static long mibrosFromGeneration(long currentGeneration) {
177       if (currentGeneration == Generator.FAILURE) {
178         throw new IllegalArgumentException();
179       }
180       return currentGeneration >>> (Generator.GEN_OFFSET + ENABLED_BIT_SPACE);
181     }
182 
mibrosFromNanos(long nanos)183     private static long mibrosFromNanos(long nanos) {
184       long remainder = ((1L<<(64 - GEN_TIMESTAMP_SPACE)) - 1) & nanos;
185       return (nanos >>> (64 - GEN_TIMESTAMP_SPACE))
186           + (remainder >= (1L<<(64 - GEN_TIMESTAMP_SPACE - 1)) ? 1 : 0);
187     }
188 
189     @Override
startTask(String taskName, Tag tag)190     protected void startTask(String taskName, Tag tag) {
191       final long gen = getGen();
192       if (!isEnabled(gen)) {
193         return;
194       }
195       Storage.startAnyway(gen, taskName, unpackTagName(tag), unpackTagId(tag));
196     }
197 
198     @Override
startTask(String taskName)199     protected void startTask(String taskName) {
200       final long gen = getGen();
201       if (!isEnabled(gen)) {
202         return;
203       }
204       Storage.startAnyway(gen, taskName);
205     }
206 
207     @Override
startTask(String taskName, String subTaskName)208     protected void startTask(String taskName, String subTaskName) {
209       final long gen = getGen();
210       if (!isEnabled(gen)) {
211         return;
212       }
213       Storage.startAnyway(gen, taskName, subTaskName);
214     }
215 
216     @Override
startTask(T taskNameObject, StringFunction<? super T> stringFunction)217     protected <T> void startTask(T taskNameObject, StringFunction<? super T> stringFunction) {
218       final long gen = getGen();
219       if (!isEnabled(gen)) {
220         return;
221       }
222       String taskName = deriveTaskValue(taskNameObject, stringFunction);
223       Storage.startAnyway(gen, taskName);
224     }
225 
226     @Override
stopTask()227     protected void stopTask() {
228       final long gen = getGen();
229       if (!isEnabled(gen)) {
230         return;
231       }
232       Storage.stopAnyway(gen);
233     }
234 
235     @Override
stopTask(String taskName, Tag tag)236     protected void stopTask(String taskName, Tag tag) {
237       final long gen = getGen();
238       if (!isEnabled(gen)) {
239         return;
240       }
241       Storage.stopAnyway(gen, taskName, unpackTagName(tag), unpackTagId(tag));
242     }
243 
244     @Override
stopTask(String taskName)245     protected void stopTask(String taskName) {
246       final long gen = getGen();
247       if (!isEnabled(gen)) {
248         return;
249       }
250       Storage.stopAnyway(gen, taskName);
251     }
252 
253     @Override
stopTask(String taskName, String subTaskName)254     protected void stopTask(String taskName, String subTaskName) {
255       final long gen = getGen();
256       if (!isEnabled(gen)) {
257         return;
258       }
259       Storage.stopAnyway(gen, taskName, subTaskName);
260     }
261 
262     @Override
event(String eventName, Tag tag)263     protected void event(String eventName, Tag tag) {
264       final long gen = getGen();
265       if (!isEnabled(gen)) {
266         return;
267       }
268       Storage.eventAnyway(gen, eventName, unpackTagName(tag), unpackTagId(tag));
269     }
270 
271     @Override
event(String eventName)272     protected void event(String eventName) {
273       final long gen = getGen();
274       if (!isEnabled(gen)) {
275         return;
276       }
277       Storage.eventAnyway(gen, eventName);
278     }
279 
280     @Override
event(String eventName, String subEventName)281     protected void event(String eventName, String subEventName) {
282       final long gen = getGen();
283       if (!isEnabled(gen)) {
284         return;
285       }
286       Storage.eventAnyway(gen, eventName, subEventName);
287     }
288 
289     @Override
attachTag(Tag tag)290     protected void attachTag(Tag tag) {
291       final long gen = getGen();
292       if (!isEnabled(gen)) {
293         return;
294       }
295       Storage.attachTagAnyway(gen, unpackTagName(tag), unpackTagId(tag));
296     }
297 
298     @Override
attachTag(String tagName, String tagValue)299     protected void attachTag(String tagName, String tagValue) {
300       final long gen = getGen();
301       if (!isEnabled(gen)) {
302         return;
303       }
304       Storage.attachKeyedTagAnyway(gen, tagName, tagValue);
305     }
306 
307     @Override
attachTag( String tagName, T tagObject, StringFunction<? super T> stringFunction)308     protected <T> void attachTag(
309         String tagName, T tagObject, StringFunction<? super T> stringFunction) {
310       final long gen = getGen();
311       if (!isEnabled(gen)) {
312         return;
313       }
314       String tagValue = deriveTagValue(tagName, tagObject, stringFunction);
315       Storage.attachKeyedTagAnyway(gen, tagName, tagValue);
316     }
317 
deriveTagValue( String tagName, T tagObject, StringFunction<? super T> stringFunction)318     static <T> String deriveTagValue(
319         String tagName, T tagObject, StringFunction<? super T> stringFunction) {
320       try {
321         return stringFunction.apply(tagObject);
322       } catch (Throwable t) {
323         handleTagValueFailure(tagName, tagObject, stringFunction, t);
324         return "PerfMarkTagError:" + t.getClass().getName();
325       }
326     }
327 
deriveTaskValue(T taskNameObject, StringFunction<? super T> stringFunction)328     static <T> String deriveTaskValue(T taskNameObject, StringFunction<? super T> stringFunction) {
329       try {
330         return stringFunction.apply(taskNameObject);
331       } catch (Throwable t) {
332         handleTaskNameFailure(taskNameObject, stringFunction, t);
333         return "PerfMarkTaskError:" + t.getClass().getName();
334       }
335     }
336 
handleTagValueFailure( String tagName, T tagObject, StringFunction<? super T> stringFunction, Throwable cause)337     static <T> void handleTagValueFailure(
338         String tagName, T tagObject, StringFunction<? super T> stringFunction, Throwable cause) {
339       if (logger == null) {
340         return;
341       }
342       Logger localLogger = (Logger) logger;
343       try {
344         if (localLogger.isLoggable(Level.FINE)) {
345           LogRecord lr =
346               new LogRecord(
347                   Level.FINE,
348                   "PerfMark.attachTag failed: tagName={0}, tagObject={1}, stringFunction={2}");
349           lr.setParameters(new Object[] {tagName, tagObject, stringFunction});
350           lr.setThrown(cause);
351           localLogger.log(lr);
352         }
353       } catch (Throwable t) {
354         // Need to be careful here.  It's possible that the Exception thrown may itself throw
355         // while trying to convert it to a String.  Instead, only pass the class name, which is
356         // safer than passing the whole object.
357         localLogger.log(
358             Level.FINE,
359             "PerfMark.attachTag failed for {0}: {1}",
360             new Object[] {tagName, t.getClass()});
361       }
362     }
363 
handleTaskNameFailure( T taskNameObject, StringFunction<? super T> stringFunction, Throwable cause)364     static <T> void handleTaskNameFailure(
365         T taskNameObject, StringFunction<? super T> stringFunction, Throwable cause) {
366       if (logger == null) {
367         return;
368       }
369       Logger localLogger = (Logger) logger;
370       try {
371         if (localLogger.isLoggable(Level.FINE)) {
372           LogRecord lr =
373               new LogRecord(
374                   Level.FINE, "PerfMark.startTask failed: taskObject={0}, stringFunction={1}");
375           lr.setParameters(new Object[] {taskNameObject, stringFunction});
376           lr.setThrown(cause);
377           localLogger.log(lr);
378         }
379       } catch (Throwable t) {
380         // Need to be careful here.  It's possible that the Exception thrown may itself throw
381         // while trying to convert it to a String.  Instead, only pass the class name, which is
382         // safer than passing the whole object.
383         localLogger.log(Level.FINE, "PerfMark.startTask failed for {0}", new Object[] {t.getClass()});
384       }
385     }
386 
387     @Override
attachTag(String tagName, long tagValue)388     protected void attachTag(String tagName, long tagValue) {
389       final long gen = getGen();
390       if (!isEnabled(gen)) {
391         return;
392       }
393       Storage.attachKeyedTagAnyway(gen, tagName, tagValue);
394     }
395 
396     @Override
attachTag(String tagName, long tagValue0, long tagValue1)397     protected void attachTag(String tagName, long tagValue0, long tagValue1) {
398       final long gen = getGen();
399       if (!isEnabled(gen)) {
400         return;
401       }
402       Storage.attachKeyedTagAnyway(gen, tagName, tagValue0, tagValue1);
403     }
404 
405     @Override
createTag(@ullable String tagName, long tagId)406     protected Tag createTag(@Nullable String tagName, long tagId) {
407       if (!isEnabled(getGen())) {
408         return NO_TAG;
409       }
410       return packTag(tagName, tagId);
411     }
412 
413     @Override
linkOut()414     protected Link linkOut() {
415       final long gen = getGen();
416       if (!isEnabled(gen)) {
417         return NO_LINK;
418       }
419       long linkId = linkIdAlloc.getAndIncrement();
420       Storage.linkAnyway(gen, linkId);
421       return packLink(linkId);
422     }
423 
424     @Override
linkIn(Link link)425     protected void linkIn(Link link) {
426       final long gen = getGen();
427       if (!isEnabled(gen)) {
428         return;
429       }
430       Storage.linkAnyway(gen, -unpackLinkId(link));
431     }
432 
getGen()433     private static long getGen() {
434       return generator.getGeneration();
435     }
436 
isEnabled(long gen)437     private static boolean isEnabled(long gen) {
438       return ((gen >>> Generator.GEN_OFFSET) & 0x1L) != 0L;
439     }
440   }
441 
SecretPerfMarkImpl()442   private SecretPerfMarkImpl() {}
443 }
444