xref: /aosp_15_r20/external/perfmark/impl/src/main/java/io/perfmark/impl/Storage.java (revision 27e8546d0ef5f99cf83d5252272c7dd38d18d29a)
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 java.lang.ref.Reference;
20 import java.lang.ref.ReferenceQueue;
21 import java.lang.ref.SoftReference;
22 import java.lang.ref.WeakReference;
23 import java.lang.reflect.Method;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.concurrent.ConcurrentHashMap;
29 import java.util.concurrent.ConcurrentMap;
30 import java.util.concurrent.atomic.AtomicLong;
31 import java.util.concurrent.atomic.AtomicReference;
32 import javax.annotation.Nullable;
33 
34 /**
35  * Storage is responsible for storing and returning recorded marks. This is a low level class and
36  * not intended for use by users. Instead, the {@code TraceEventWriter} and {@code TraceEventViewer}
37  * classes provide easier to use APIs for accessing PerfMark data.
38  *
39  * <p>This code is <strong>NOT</strong> API stable, and may be removed in the future, or changed
40  * without notice.
41  */
42 public final class Storage {
43   static final AtomicLong markHolderIdAllocator = new AtomicLong(1);
44   // The order of initialization here matters.  If a logger invokes PerfMark, it will be re-entrant
45   // and need to use these static variables.
46   static final ConcurrentMap<Reference<Thread>, MarkHolderHandle> allMarkHolders =
47       new ConcurrentHashMap<Reference<Thread>, MarkHolderHandle>();
48   private static final LocalMarkHolder localMarkHolder = new MostlyThreadLocalMarkHolder();
49   private static final MarkHolderProvider markHolderProvider;
50   private static final ReferenceQueue<Thread> threadReferenceQueue = new ReferenceQueue<Thread>();
51   private static volatile long lastGlobalIndexClear = Generator.INIT_NANO_TIME - 1;
52 
53   static {
54     MarkHolderProvider provider = null;
55     Throwable[] problems = new Throwable[3];
56     try {
57       String markHolderOverride = System.getProperty("io.perfmark.PerfMark.markHolderProvider");
58       if (markHolderOverride != null && !markHolderOverride.isEmpty()) {
59         Class<?> clz = Class.forName(markHolderOverride);
60         provider = clz.asSubclass(MarkHolderProvider.class).getConstructor().newInstance();
61       }
62     } catch (Throwable t) {
63       problems[0] = t;
64     }
65     if (provider == null) {
66       try {
67         Class<?> clz =
68             Class.forName(
69                 "io.perfmark.java9.SecretVarHandleMarkHolderProvider$VarHandleMarkHolderProvider");
70         provider = clz.asSubclass(MarkHolderProvider.class).getConstructor().newInstance();
71       } catch (Throwable t) {
72         problems[1] = t;
73       }
74     }
75     if (provider == null) {
76       try {
77         Class<?> clz =
78             Class.forName(
79                 "io.perfmark.java6.SecretSynchronizedMarkHolderProvider$SynchronizedMarkHolderProvider");
80         provider = clz.asSubclass(MarkHolderProvider.class).getConstructor().newInstance();
81       } catch (Throwable t) {
82         problems[2] = t;
83       }
84     }
85     if (provider == null) {
86       markHolderProvider = new NoopMarkHolderProvider();
87     } else {
88       markHolderProvider = provider;
89     }
90     try {
91       if (Boolean.getBoolean("io.perfmark.PerfMark.debug")) {
92         // See the comment in io.perfmark.PerfMark for why this is invoked reflectively.
93         Class<?> logClass = Class.forName("java.util.logging.Logger");
94         Object logger = logClass.getMethod("getLogger", String.class).invoke(null, Storage.class.getName());
95         Class<?> levelClass = Class.forName("java.util.logging.Level");
96         Object level = levelClass.getField("FINE").get(null);
97         Method logProblemMethod = logClass.getMethod("log", levelClass, String.class, Throwable.class);
98 
99         for (Throwable problem : problems) {
100           if (problem == null) {
101             continue;
102           }
logProblemMethod.invoke(logger, level, "Error loading MarkHolderProvider", problem)103           logProblemMethod.invoke(logger, level, "Error loading MarkHolderProvider", problem);
104         }
105         Method logSuccessMethod = logClass.getMethod("log", levelClass, String.class, Object[].class);
logSuccessMethod.invoke(logger, level, "Using {0}", new Object[] {markHolderProvider.getClass().getName()})106         logSuccessMethod.invoke(logger, level, "Using {0}", new Object[] {markHolderProvider.getClass().getName()});
107       }
108     } catch (Throwable t) {
109       // ignore
110     }
111   }
112 
getInitNanoTime()113   public static long getInitNanoTime() {
114     return Generator.INIT_NANO_TIME;
115   }
116 
117   /**
118    * Returns a list of {@link MarkList}s across all reachable threads.
119    *
120    * @return all reachable MarkLists.
121    */
read()122   public static List<MarkList> read() {
123     long lastReset = lastGlobalIndexClear;
124     drainThreadQueue();
125     List<MarkList> markLists = new ArrayList<MarkList>(allMarkHolders.size());
126     for (Iterator<MarkHolderHandle> it = allMarkHolders.values().iterator(); it.hasNext();) {
127       MarkHolderHandle handle = it.next();
128       Thread writer = handle.threadRef().get();
129       if (writer == null) {
130         handle.softenMarkHolderReference();
131       }
132       MarkHolder markHolder = handle.markHolder();
133       if (markHolder == null) {
134         it.remove();
135         handle.clearSoftReference();
136         continue;
137       }
138       String threadName = handle.getAndUpdateThreadName();
139       long threadId = handle.getAndUpdateThreadId();
140       boolean concurrentWrites = !(Thread.currentThread() == writer || writer == null);
141       markLists.add(
142           MarkList.newBuilder()
143               .setMarks(markHolder.read(concurrentWrites))
144               .setThreadName(threadName)
145               .setThreadId(threadId)
146               .setMarkListId(handle.markHolderId)
147               .build());
148     }
149     return Collections.unmodifiableList(markLists);
150   }
151 
startAnyway(long gen, String taskName, @Nullable String tagName, long tagId)152   static void startAnyway(long gen, String taskName, @Nullable String tagName, long tagId) {
153     MarkHolder mh = localMarkHolder.acquire();
154     mh.start(gen, taskName, tagName, tagId, System.nanoTime());
155     localMarkHolder.release(mh);
156   }
157 
startAnyway(long gen, String taskName)158   static void startAnyway(long gen, String taskName) {
159     MarkHolder mh = localMarkHolder.acquire();
160     mh.start(gen, taskName, System.nanoTime());
161     localMarkHolder.release(mh);
162   }
163 
startAnyway(long gen, String taskName, String subTaskName)164   static void startAnyway(long gen, String taskName, String subTaskName) {
165     MarkHolder mh = localMarkHolder.acquire();
166     mh.start(gen, taskName, subTaskName, System.nanoTime());
167     localMarkHolder.release(mh);
168   }
169 
stopAnyway(long gen)170   static void stopAnyway(long gen) {
171     long nanoTime = System.nanoTime();
172     MarkHolder mh = localMarkHolder.acquire();
173     mh.stop(gen, nanoTime);
174     localMarkHolder.release(mh);
175   }
176 
stopAnyway(long gen, String taskName, @Nullable String tagName, long tagId)177   static void stopAnyway(long gen, String taskName, @Nullable String tagName, long tagId) {
178     long nanoTime = System.nanoTime();
179     MarkHolder mh = localMarkHolder.acquire();
180     mh.stop(gen, taskName, tagName, tagId, nanoTime);
181     localMarkHolder.release(mh);
182   }
183 
stopAnyway(long gen, String taskName)184   static void stopAnyway(long gen, String taskName) {
185     long nanoTime = System.nanoTime();
186     MarkHolder mh = localMarkHolder.acquire();
187     mh.stop(gen, taskName, nanoTime);
188     localMarkHolder.release(mh);
189   }
190 
stopAnyway(long gen, String taskName, String subTaskName)191   static void stopAnyway(long gen, String taskName, String subTaskName) {
192     long nanoTime = System.nanoTime();
193     MarkHolder mh = localMarkHolder.acquire();
194     mh.stop(gen, taskName, subTaskName, nanoTime);
195     localMarkHolder.release(mh);
196   }
197 
eventAnyway(long gen, String eventName, @Nullable String tagName, long tagId)198   static void eventAnyway(long gen, String eventName, @Nullable String tagName, long tagId) {
199     long nanoTime = System.nanoTime();
200     MarkHolder mh = localMarkHolder.acquire();
201     mh.event(gen, eventName, tagName, tagId, nanoTime);
202     localMarkHolder.release(mh);
203   }
204 
eventAnyway(long gen, String eventName)205   static void eventAnyway(long gen, String eventName) {
206     long nanoTime = System.nanoTime();
207     MarkHolder mh = localMarkHolder.acquire();
208     mh.event(gen, eventName, nanoTime);
209     localMarkHolder.release(mh);
210   }
211 
eventAnyway(long gen, String eventName, String subEventName)212   static void eventAnyway(long gen, String eventName, String subEventName) {
213     long nanoTime = System.nanoTime();
214     MarkHolder mh = localMarkHolder.acquire();
215     mh.event(gen, eventName, subEventName, nanoTime);
216     localMarkHolder.release(mh);
217   }
218 
linkAnyway(long gen, long linkId)219   static void linkAnyway(long gen, long linkId) {
220     MarkHolder mh = localMarkHolder.acquire();
221     mh.link(gen, linkId);
222     localMarkHolder.release(mh);
223   }
224 
attachTagAnyway(long gen, @Nullable String tagName, long tagId)225   static void attachTagAnyway(long gen, @Nullable String tagName, long tagId) {
226     MarkHolder mh = localMarkHolder.acquire();
227     mh.attachTag(gen, tagName, tagId);
228     localMarkHolder.release(mh);
229   }
230 
attachKeyedTagAnyway(long gen, @Nullable String tagName, String tagValue)231   static void attachKeyedTagAnyway(long gen, @Nullable String tagName, String tagValue) {
232     MarkHolder mh = localMarkHolder.acquire();
233     mh.attachKeyedTag(gen, tagName, tagValue);
234     localMarkHolder.release(mh);
235   }
236 
attachKeyedTagAnyway(long gen, @Nullable String tagName, long tagValue)237   static void attachKeyedTagAnyway(long gen, @Nullable String tagName, long tagValue) {
238     MarkHolder mh = localMarkHolder.acquire();
239     mh.attachKeyedTag(gen, tagName, tagValue);
240     localMarkHolder.release(mh);
241   }
242 
attachKeyedTagAnyway( long gen, @Nullable String tagName, long tagValue0, long tagValue1)243   static void attachKeyedTagAnyway(
244       long gen, @Nullable String tagName, long tagValue0, long tagValue1) {
245     MarkHolder mh = localMarkHolder.acquire();
246     mh.attachKeyedTag(gen, tagName, tagValue0, tagValue1);
247     localMarkHolder.release(mh);
248   }
249 
250   /**
251    * Removes all data for the calling Thread.  Other threads may Still have stored data.
252    */
clearLocalStorage()253   public static void clearLocalStorage() {
254     for (Iterator<MarkHolderHandle> it = allMarkHolders.values().iterator(); it.hasNext();)  {
255       MarkHolderHandle handle = it.next();
256       if (handle.threadRef.get() == Thread.currentThread()) {
257         it.remove();
258         handle.threadRef.clearInternal();
259         handle.softenMarkHolderReference();
260         handle.clearSoftReference();
261       }
262     }
263     localMarkHolder.clear();
264   }
265 
266   /**
267    * Removes the global Read index on all storage, but leaves local storage in place.  Because writer threads may still
268    * be writing to the same buffer (which they have a strong ref to), this function only removed data that is truly
269    * unwritable anymore.   In addition, it captures a timestamp to which marks to include when reading.  Thus, the data
270    * isn't fully removed.  To fully remove all data, each tracing thread must call {@link #clearLocalStorage}.
271    */
clearGlobalIndex()272   public static void clearGlobalIndex() {
273     lastGlobalIndexClear = System.nanoTime() - 1;
274     for (Iterator<MarkHolderHandle> it = allMarkHolders.values().iterator(); it.hasNext();) {
275       MarkHolderHandle handle = it.next();
276       handle.softenMarkHolderReference();
277       Thread writer = handle.threadRef().get();
278       if (writer == null) {
279         it.remove();
280         handle.clearSoftReference();
281       }
282     }
283   }
284 
drainThreadQueue()285   private static void drainThreadQueue() {
286     while (true) {
287       Reference<?> ref = threadReferenceQueue.poll();
288       if (ref == null) {
289         return;
290       }
291       MarkHolderHandle handle = allMarkHolders.get(ref);
292       if (handle != null) {
293         handle.softenMarkHolderReference();
294         if (handle.markHolder() == null) {
295           allMarkHolders.remove(ref);
296           handle.clearSoftReference();
297         }
298       }
299     }
300   }
301 
302   @Nullable
readForTest()303   public static MarkList readForTest() {
304     List<MarkList> lists = read();
305     for (MarkList list : lists) {
306       // This is slightly wrong as the thread ID could be reused.
307       if (list.getThreadId() == Thread.currentThread().getId()) {
308         return list;
309       }
310     }
311     return null;
312   }
313 
314   public static final class MarkHolderHandle {
315     private static final SoftReference<MarkHolder> EMPTY = new SoftReference<MarkHolder>(null);
316 
317     private final UnmodifiableWeakReference<Thread> threadRef;
318     private final AtomicReference<MarkHolder> markHolderRef;
319     private volatile SoftReference<MarkHolder> softMarkHolderRef;
320 
321     private volatile String threadName;
322     private volatile long threadId;
323     private final long markHolderId;
324 
MarkHolderHandle(Thread thread, MarkHolder markHolder, long markHolderId)325     MarkHolderHandle(Thread thread, MarkHolder markHolder, long markHolderId) {
326       this.threadRef = new UnmodifiableWeakReference<Thread>(thread, threadReferenceQueue);
327       this.markHolderRef = new AtomicReference<MarkHolder>(markHolder);
328       this.threadName = thread.getName();
329       this.threadId = thread.getId();
330       this.markHolderId = markHolderId;
331     }
332 
333     /**
334      * Returns the MarkHolder.  May return {@code null} if the Thread is gone.  If {@code null} is returned,
335      * then {@code getThreadRef().get() == null}.  If a non-{@code null} value is returned, the thread may be dead or
336      * alive.  Additionally, since the {@link #threadRef} may be externally cleared, it is not certain that the Thread
337      * is dead.
338      */
markHolder()339     public MarkHolder markHolder() {
340       MarkHolder markHolder = markHolderRef.get();
341       if (markHolder == null) {
342         markHolder = softMarkHolderRef.get();
343         assert markHolder != null || threadRef.get() == null;
344       }
345       return markHolder;
346     }
347 
348     /**
349      * Returns a weak reference to the Thread that created the MarkHolder.
350      */
threadRef()351     public WeakReference<? extends Thread> threadRef() {
352       return threadRef;
353     }
354 
softenMarkHolderReference()355     void softenMarkHolderReference() {
356       synchronized (markHolderRef) {
357         MarkHolder markHolder = markHolderRef.get();
358         if (markHolder != null) {
359           softMarkHolderRef = new SoftReference<MarkHolder>(markHolder);
360           markHolderRef.set(null);
361         }
362       }
363     }
364 
clearSoftReference()365     void clearSoftReference() {
366       Thread thread = threadRef.get();
367       if (thread != null) {
368         throw new IllegalStateException("Thread still alive " + thread);
369       }
370       synchronized (markHolderRef) {
371         MarkHolder markHolder = markHolderRef.get();
372         if (markHolder != null) {
373           throw new IllegalStateException("Handle not yet softened");
374         }
375         softMarkHolderRef.clear();
376         softMarkHolderRef = EMPTY;
377         threadName = null;
378         threadId = -255;
379       }
380     }
381 
getAndUpdateThreadName()382     String getAndUpdateThreadName() {
383       Thread t = threadRef.get();
384       String name;
385       if (t != null) {
386         threadName = (name = t.getName());
387       } else {
388         name = threadName;
389       }
390       return name;
391     }
392 
393     /**
394      * Some threads change their id over time, so we need to sync it if available.
395      */
getAndUpdateThreadId()396     long getAndUpdateThreadId() {
397       Thread t = threadRef.get();
398       long id;
399       if (t != null) {
400         threadId = (id = t.getId());
401       } else {
402         id = threadId;
403       }
404       return id;
405     }
406   }
407 
408   /**
409    * This class is needed to work around a race condition where a newly created MarkHolder could be GC'd before
410    * the caller of {@link #allocateMarkHolder} can consume the results.  The provided MarkHolder is strongly reachable.
411    */
412   public static final class MarkHolderAndHandle {
413     private final MarkHolder markHolder;
414     private final MarkHolderHandle handle;
415 
MarkHolderAndHandle(MarkHolder markHolder, MarkHolderHandle handle)416     MarkHolderAndHandle(MarkHolder markHolder, MarkHolderHandle handle) {
417       this.markHolder = markHolder;
418       this.handle = handle;
419 
420       MarkHolder tmp = handle.markHolder();
421       if (markHolder != tmp && tmp != null) {
422         throw new IllegalArgumentException("Holder Handle mismatch");
423       }
424     }
425 
markHolder()426     public MarkHolder markHolder() {
427       return markHolder;
428     }
429 
handle()430     public MarkHolderHandle handle() {
431       return handle;
432     }
433   }
434 
allocateMarkHolder()435   public static MarkHolderAndHandle allocateMarkHolder() {
436     drainThreadQueue();
437     long markHolderId = markHolderIdAllocator.getAndIncrement();
438     MarkHolder holder = markHolderProvider.create(markHolderId);
439     MarkHolderHandle handle = new MarkHolderHandle(Thread.currentThread(), holder, markHolderId);
440     allMarkHolders.put(handle.threadRef, handle);
441     return new MarkHolderAndHandle(holder, handle);
442   }
443 
444   private static final class UnmodifiableWeakReference<T> extends WeakReference<T> {
445 
UnmodifiableWeakReference(T referent, ReferenceQueue<T> q)446     UnmodifiableWeakReference(T referent, ReferenceQueue<T> q) {
447       super(referent, q);
448     }
449 
450     @Override
451     @Deprecated
452     @SuppressWarnings("InlineMeSuggester")
clear()453     public void clear() {}
454 
455     @Override
456     @Deprecated
457     @SuppressWarnings("InlineMeSuggester")
enqueue()458     public boolean enqueue() {
459       return false;
460     }
461 
clearInternal()462     void clearInternal() {
463       super.clear();
464     }
465   }
466 
Storage()467   private Storage() {}
468 }
469