xref: /aosp_15_r20/external/perfmark/agent/src/test/java/io/perfmark/agent/PerfMarkTransformerTest.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.agent;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static org.junit.Assert.assertEquals;
21 
22 import com.google.common.truth.Truth;
23 import io.perfmark.PerfMark;
24 import io.perfmark.TaskCloseable;
25 import io.perfmark.impl.Mark;
26 import io.perfmark.impl.Storage;
27 import java.io.Closeable;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.lang.annotation.ElementType;
31 import java.lang.annotation.Target;
32 import java.lang.reflect.Constructor;
33 import java.util.HashMap;
34 import java.util.List;
35 import java.util.Map;
36 import org.junit.Ignore;
37 import org.junit.Test;
38 import org.junit.runner.RunWith;
39 import org.junit.runners.JUnit4;
40 
41 @RunWith(JUnit4.class)
42 public class PerfMarkTransformerTest {
43 
44   @Test
deriveFileName()45   public void deriveFileName() {
46     String file = PerfMarkTransformer.deriveFileName("io/perfmark/Clz");
47 
48     assertEquals("Clz.java", file);
49   }
50 
51   @Test
deriveFileName_innerClass()52   public void deriveFileName_innerClass() {
53     String file = PerfMarkTransformer.deriveFileName("io/perfmark/Clz$Inner");
54 
55     assertEquals("Clz.java", file);
56   }
57 
58   @Test
59   @Ignore
transform_autoAnnotate()60   public void transform_autoAnnotate() throws Exception {
61     // This test currently depends on the transformer treating this test class specially.
62     PerfMark.setEnabled(true);
63     Storage.clearLocalStorage();
64 
65     Class<?> clz = transformAndLoad(TransformerTestClasses.ClzAutoRecord.class);
66     Constructor<?> ctor = clz.getConstructor();
67     ctor.setAccessible(true);
68     ctor.newInstance();
69     List<Mark> marks = Storage.readForTest();
70     assertThat(marks).hasSize(2);
71   }
72 
73   @Test
transform_record()74   public void transform_record() throws Exception {
75     PerfMark.setEnabled(true);
76     Storage.clearLocalStorage();
77 
78     Class<?> clz = transformAndLoad(TransformerTestClasses.SomeRecord.class);
79     Constructor<?> ctor = clz.getConstructor(int.class);
80     ctor.setAccessible(true);
81     ctor.newInstance(2);
82     List<Mark> marks = Storage.readForTest();
83     assertThat(marks).hasSize(4);
84     assertEquals(marks.get(0).withTaskName("task"), marks.get(0));
85 
86     assertEquals(marks.get(1).getTagKey(), "PerfMark.startCallSite");
87     Truth.assertThat(marks.get(1).getTagStringValue()).contains("TransformerTestClasses$SomeRecord");
88     Truth.assertThat(marks.get(1).getTagStringValue()).contains("<init>");
89 
90     assertEquals(marks.get(2).getTagKey(), "PerfMark.stopCallSite");
91     Truth.assertThat(marks.get(2).getTagStringValue()).contains("TransformerTestClasses$SomeRecord");
92     Truth.assertThat(marks.get(2).getTagStringValue()).contains("<init>");
93 
94     assertEquals(marks.get(3).withTaskName("task"), marks.get(3));
95   }
96 
97   @Test
transform_lambda()98   public void transform_lambda() throws Exception {
99     PerfMark.setEnabled(true);
100     Storage.clearLocalStorage();
101 
102     Class<?> clz = transformAndLoad(TransformerTestClasses.ClzCtorLambda.class);
103     Constructor<?> ctor = clz.getConstructor();
104     ctor.setAccessible(true);
105     ctor.newInstance();
106     List<Mark> marks = Storage.readForTest();
107     assertThat(marks).hasSize(4);
108     assertEquals(marks.get(0).withTaskName("task"), marks.get(0));
109 
110     assertEquals(marks.get(1).getTagKey(), "PerfMark.startCallSite");
111     Truth.assertThat(marks.get(1).getTagStringValue()).contains("TransformerTestClasses$ClzCtorLambda");
112     Truth.assertThat(marks.get(1).getTagStringValue()).contains("lambda");
113 
114     assertEquals(marks.get(2).getTagKey(), "PerfMark.stopCallSite");
115     Truth.assertThat(marks.get(2).getTagStringValue()).contains("TransformerTestClasses$ClzCtorLambda");
116     Truth.assertThat(marks.get(2).getTagStringValue()).contains("lambda");
117 
118     assertEquals(marks.get(3).withTaskName("task"), marks.get(3));
119   }
120 
121   @Test
transform_methodRef()122   public void transform_methodRef() throws Exception {
123     PerfMark.setEnabled(true);
124     Storage.clearLocalStorage();
125 
126     Class<?> clz = transformAndLoad(TransformerTestClasses.ClzWithMethodRefs.class);
127     Constructor<?> ctor = clz.getConstructor();
128     ctor.setAccessible(true);
129     ctor.newInstance();
130     List<Mark> marks = Storage.readForTest();
131     assertThat(marks).hasSize(2);
132     // I'm not sure what to do with methodrefs, so just leave it alone for now.
133   }
134 
135   @Test
transform_interface()136   public void transform_interface() throws Exception {
137     PerfMark.setEnabled(true);
138     Storage.clearLocalStorage();
139 
140     Class<?> clz =
141         transformAndLoad(TransformerTestClasses.Bar.class, TransformerTestClasses.InterfaceWithDefaults.class);
142     Constructor<?> ctor = clz.getConstructor();
143     ctor.setAccessible(true);
144     ctor.newInstance();
145 
146     List<Mark> marks = Storage.readForTest();
147     assertThat(marks).hasSize(10);
148 
149     assertEquals(marks.get(0).withTaskName("task"), marks.get(0));
150 
151     assertEquals(marks.get(1).getTagKey(), "PerfMark.startCallSite");
152     Truth.assertThat(marks.get(1).getTagStringValue()).contains("TransformerTestClasses$InterfaceWithDefaults");
153     Truth.assertThat(marks.get(1).getTagStringValue()).contains("record");
154 
155     assertEquals(marks.get(2).getTagKey(), "PerfMark.stopCallSite");
156     Truth.assertThat(marks.get(2).getTagStringValue()).contains("TransformerTestClasses$InterfaceWithDefaults");
157     Truth.assertThat(marks.get(2).getTagStringValue()).contains("record");
158 
159     assertEquals(marks.get(3).withTaskName("task"), marks.get(3));
160 
161     assertEquals(marks.get(4).withTaskName("task"), marks.get(4));
162 
163     // Ignore the regular tag at 5
164 
165     assertEquals(marks.get(6).getTagKey(), "PerfMark.startCallSite");
166     Truth.assertThat(marks.get(6).getTagStringValue()).contains("TransformerTestClasses$InterfaceWithDefaults");
167     Truth.assertThat(marks.get(6).getTagStringValue()).contains("record");
168 
169     assertEquals(marks.get(7).getTagKey(), "PerfMark.stopCallSite");
170     Truth.assertThat(marks.get(7).getTagStringValue()).contains("TransformerTestClasses$InterfaceWithDefaults");
171     Truth.assertThat(marks.get(7).getTagStringValue()).contains("record");
172 
173     // Ignore the regular tag at 8
174 
175     assertEquals(marks.get(9).withTaskName("task"), marks.get(9));
176   }
177 
178   @Test
transform_link()179   public void transform_link() throws Exception {
180     PerfMark.setEnabled(true);
181     Storage.clearLocalStorage();
182 
183     Class<?> clz = transformAndLoad(TransformerTestClasses.ClzWithLinks.class);
184     Constructor<?> ctor = clz.getConstructor();
185     ctor.setAccessible(true);
186     ctor.newInstance();
187     List<Mark> marks = Storage.readForTest();
188     assertThat(marks).hasSize(6);
189 
190     assertEquals(marks.get(0).withTaskName("task"), marks.get(0));
191 
192     assertEquals(marks.get(1).getTagKey(), "PerfMark.startCallSite");
193     Truth.assertThat(marks.get(1).getTagStringValue()).contains("TransformerTestClasses$ClzWithLinks");
194     Truth.assertThat(marks.get(1).getTagStringValue()).contains("init");
195 
196     // assume links have not been modified
197 
198     assertEquals(marks.get(4).getTagKey(), "PerfMark.stopCallSite");
199     Truth.assertThat(marks.get(4).getTagStringValue()).contains("TransformerTestClasses$ClzWithLinks");
200     Truth.assertThat(marks.get(4).getTagStringValue()).contains("init");
201 
202     assertEquals(marks.get(5).withTaskName("task"), marks.get(5));
203   }
204 
205   @Test
transform_closeable()206   public void transform_closeable() throws Exception {
207     PerfMark.setEnabled(true);
208     Storage.clearLocalStorage();
209 
210     Class<?> clz = transformAndLoad(TransformerTestClasses.ClzWithCloseable.class);
211     Constructor<?> ctor = clz.getConstructor();
212     ctor.setAccessible(true);
213     ctor.newInstance();
214     List<Mark> marks = Storage.readForTest();
215     assertThat(marks).hasSize(4);
216 
217     assertEquals(marks.get(0).withTaskName("task"), marks.get(0));
218 
219     assertEquals(marks.get(1).getTagKey(), "PerfMark.startCallSite");
220     Truth.assertThat(marks.get(1).getTagStringValue()).contains("TransformerTestClasses$ClzWithCloseable");
221     Truth.assertThat(marks.get(1).getTagStringValue()).contains("init");
222 
223     assertEquals(marks.get(2).getTagKey(), "PerfMark.stopCallSite");
224     Truth.assertThat(marks.get(2).getTagStringValue()).contains("TransformerTestClasses$ClzWithCloseable");
225     Truth.assertThat(marks.get(2).getTagStringValue()).contains("init");
226 
227     assertEquals(Mark.Operation.TASK_END_N1S0, marks.get(3).getOperation());
228   }
229 
230   @Test
transform_wrongCloseable()231   public void transform_wrongCloseable() throws Exception {
232     // If the wrong static type is used, the agent won't be able to instrument it.  Add a test to document this
233     // behavior.
234     PerfMark.setEnabled(true);
235     Storage.clearLocalStorage();
236 
237     Class<?> clz = transformAndLoad(TransformerTestClasses.ClzWithWrongCloseable.class);
238     Constructor<?> ctor = clz.getConstructor();
239     ctor.setAccessible(true);
240     ctor.newInstance();
241     List<Mark> marks = Storage.readForTest();
242     assertThat(marks).hasSize(3);
243 
244     assertEquals(marks.get(0).withTaskName("task"), marks.get(0));
245 
246     assertEquals(marks.get(1).getTagKey(), "PerfMark.startCallSite");
247     Truth.assertThat(marks.get(1).getTagStringValue()).contains("TransformerTestClasses$ClzWithWrongCloseable");
248     Truth.assertThat(marks.get(1).getTagStringValue()).contains("init");
249 
250     assertEquals(Mark.Operation.TASK_END_N1S0, marks.get(2).getOperation());
251   }
252 
253   @Test
transform_wrongCloseable_autoCloseable()254   public void transform_wrongCloseable_autoCloseable() throws Exception {
255     // If the wrong static type is used, the agent won't be able to instrument it.  Add a test to document this
256     // behavior.
257     PerfMark.setEnabled(true);
258     Storage.clearLocalStorage();
259 
260     Class<? extends Closeable> clz = transformAndLoad(TaskCloseable.class).asSubclass(Closeable.class);
261     Constructor<? extends Closeable> ctor = clz.getDeclaredConstructor();
262     ctor.setAccessible(true);
263     Closeable closeable = ctor.newInstance();
264     closeable.close();
265     List<Mark> marks = Storage.readForTest();
266     assertThat(marks).hasSize(1);
267 
268     assertEquals(Mark.Operation.TASK_END_N1S0, marks.get(0).getOperation());
269   }
270 
271   @Test
transform_ctor()272   public void transform_ctor() throws Exception {
273     PerfMark.setEnabled(true);
274     Storage.clearLocalStorage();
275 
276     Class<?> clz = transformAndLoad(TransformerTestClasses.ClzWithCtor.class);
277     Constructor<?> ctor = clz.getConstructor();
278     ctor.setAccessible(true);
279     ctor.newInstance();
280     List<Mark> marks = Storage.readForTest();
281     assertThat(marks).hasSize(10);
282 
283     assertEquals(marks.get(0).withTaskName("task"), marks.get(0));
284 
285     assertEquals(marks.get(1).getTagKey(), "PerfMark.startCallSite");
286     Truth.assertThat(marks.get(1).getTagStringValue()).contains("TransformerTestClasses$ClzWithCtor");
287     Truth.assertThat(marks.get(1).getTagStringValue()).contains("init");
288 
289     assertEquals(marks.get(2).getTagKey(), "PerfMark.stopCallSite");
290     Truth.assertThat(marks.get(2).getTagStringValue()).contains("TransformerTestClasses$ClzWithCtor");
291     Truth.assertThat(marks.get(2).getTagStringValue()).contains("init");
292 
293     assertEquals(marks.get(3).withTaskName("task"), marks.get(3));
294 
295     assertEquals(marks.get(4).withTaskName("task"), marks.get(4));
296 
297     // Ignore the regular tag at 5
298 
299     assertEquals(marks.get(6).getTagKey(), "PerfMark.startCallSite");
300     Truth.assertThat(marks.get(6).getTagStringValue()).contains("TransformerTestClasses$ClzWithCtor");
301     Truth.assertThat(marks.get(6).getTagStringValue()).contains("init");
302 
303     assertEquals(marks.get(7).getTagKey(), "PerfMark.stopCallSite");
304     Truth.assertThat(marks.get(7).getTagStringValue()).contains("TransformerTestClasses$ClzWithCtor");
305     Truth.assertThat(marks.get(7).getTagStringValue()).contains("init");
306 
307     // Ignore the regular tag at 8
308 
309     assertEquals(marks.get(9).withTaskName("task"), marks.get(9));
310   }
311 
312   @Test
transform_init()313   public void transform_init() throws Exception {
314     PerfMark.setEnabled(true);
315     Storage.clearLocalStorage();
316 
317     Class<?> clz = transformAndLoad(TransformerTestClasses.ClzWithInit.class);
318     Constructor<?> ctor = clz.getDeclaredConstructor();
319     ctor.setAccessible(true);
320     ctor.newInstance();
321     List<Mark> marks = Storage.readForTest();
322     assertThat(marks).hasSize(10);
323 
324     assertEquals(marks.get(0).withTaskName("task"), marks.get(0));
325 
326     assertEquals(marks.get(1).getTagKey(), "PerfMark.startCallSite");
327     Truth.assertThat(marks.get(1).getTagStringValue()).contains("TransformerTestClasses$ClzWithInit");
328     Truth.assertThat(marks.get(1).getTagStringValue()).contains("init");
329 
330     assertEquals(marks.get(2).getTagKey(), "PerfMark.stopCallSite");
331     Truth.assertThat(marks.get(2).getTagStringValue()).contains("TransformerTestClasses$ClzWithInit");
332     Truth.assertThat(marks.get(2).getTagStringValue()).contains("init");
333 
334     assertEquals(marks.get(3).withTaskName("task"), marks.get(3));
335 
336     assertEquals(marks.get(4).withTaskName("task"), marks.get(4));
337 
338     // Ignore the regular tag at 5
339 
340     assertEquals(marks.get(6).getTagKey(), "PerfMark.startCallSite");
341     Truth.assertThat(marks.get(6).getTagStringValue()).contains("TransformerTestClasses$ClzWithInit");
342     Truth.assertThat(marks.get(6).getTagStringValue()).contains("init");
343 
344     assertEquals(marks.get(7).getTagKey(), "PerfMark.stopCallSite");
345     Truth.assertThat(marks.get(7).getTagStringValue()).contains("TransformerTestClasses$ClzWithInit");
346     Truth.assertThat(marks.get(7).getTagStringValue()).contains("init");
347 
348     // Ignore the regular tag at 8
349 
350     assertEquals(marks.get(9).withTaskName("task"), marks.get(9));
351   }
352 
353   @Test
transform_clinit()354   public void transform_clinit() throws Exception {
355     PerfMark.setEnabled(true);
356     Storage.clearLocalStorage();
357 
358     Class<?> clz = transformAndLoad(TransformerTestClasses.ClzWithClinit.class);
359     Constructor<?> ctor = clz.getDeclaredConstructor();
360     ctor.setAccessible(true);
361     ctor.newInstance();
362     List<Mark> marks = Storage.readForTest();
363     assertThat(marks).hasSize(10);
364 
365     assertEquals(marks.get(0).withTaskName("task"), marks.get(0));
366 
367     assertEquals(marks.get(1).getTagKey(), "PerfMark.startCallSite");
368     Truth.assertThat(marks.get(1).getTagStringValue()).contains("TransformerTestClasses$ClzWithClinit");
369     Truth.assertThat(marks.get(1).getTagStringValue()).contains("clinit");
370 
371     assertEquals(marks.get(2).getTagKey(), "PerfMark.stopCallSite");
372     Truth.assertThat(marks.get(2).getTagStringValue()).contains("TransformerTestClasses$ClzWithClinit");
373     Truth.assertThat(marks.get(2).getTagStringValue()).contains("clinit");
374 
375     assertEquals(marks.get(3).withTaskName("task"), marks.get(3));
376 
377     assertEquals(marks.get(4).withTaskName("task"), marks.get(4));
378 
379     // Ignore the regular tag at 5
380 
381     assertEquals(marks.get(6).getTagKey(), "PerfMark.startCallSite");
382     Truth.assertThat(marks.get(6).getTagStringValue()).contains("TransformerTestClasses$ClzWithClinit");
383     Truth.assertThat(marks.get(6).getTagStringValue()).contains("clinit");
384 
385     assertEquals(marks.get(7).getTagKey(), "PerfMark.stopCallSite");
386     Truth.assertThat(marks.get(7).getTagStringValue()).contains("TransformerTestClasses$ClzWithClinit");
387     Truth.assertThat(marks.get(7).getTagStringValue()).contains("clinit");
388 
389     // Ignore the regular tag at 8
390 
391     assertEquals(marks.get(9).withTaskName("task"), marks.get(9));
392   }
393 
394   @Test
transform_toplevel()395   public void transform_toplevel() throws Exception {
396     PerfMark.setEnabled(true);
397     Storage.clearLocalStorage();
398 
399     Class<?> clz = transformAndLoad(ClzFooter.class);
400     Constructor<?> ctor = clz.getDeclaredConstructor();
401     ctor.setAccessible(true);
402     ctor.newInstance();
403     List<Mark> marks = Storage.readForTest();
404     assertThat(marks).hasSize(10);
405 
406     assertEquals(marks.get(0).withTaskName("task"), marks.get(0));
407 
408     assertEquals(marks.get(1).getTagKey(), "PerfMark.startCallSite");
409     Truth.assertThat(marks.get(1).getTagStringValue()).contains("ClzFooter");
410     Truth.assertThat(marks.get(1).getTagStringValue()).contains("init");
411 
412     assertEquals(marks.get(2).getTagKey(), "PerfMark.stopCallSite");
413     Truth.assertThat(marks.get(2).getTagStringValue()).contains("ClzFooter");
414     Truth.assertThat(marks.get(2).getTagStringValue()).contains("init");
415 
416     assertEquals(marks.get(3).withTaskName("task"), marks.get(3));
417 
418     assertEquals(marks.get(4).withTaskName("task"), marks.get(4));
419 
420     // Ignore the regular tag at 5
421 
422     assertEquals(marks.get(6).getTagKey(), "PerfMark.startCallSite");
423     Truth.assertThat(marks.get(6).getTagStringValue()).contains("ClzFooter");
424     Truth.assertThat(marks.get(6).getTagStringValue()).contains("init");
425 
426     assertEquals(marks.get(7).getTagKey(), "PerfMark.stopCallSite");
427     Truth.assertThat(marks.get(7).getTagStringValue()).contains("ClzFooter");
428     Truth.assertThat(marks.get(7).getTagStringValue()).contains("init");
429 
430     // Ignore the regular tag at 8
431 
432     assertEquals(marks.get(9).withTaskName("task"), marks.get(9));
433   }
434 
435   @Test
transform_anonymousClass()436   public void transform_anonymousClass() throws Exception {
437     PerfMark.setEnabled(true);
438     Storage.clearLocalStorage();
439 
440     Class<?> clz = transformAndLoad(new Runnable() {
441       // avoid IntelliJ thinking this should be a lambda.
442       public volatile int a;
443       @Override
444       public void run() {
445         PerfMark.startTask("task");
446         PerfMark.stopTask("task");
447       }
448     }.getClass());
449     Constructor<?> ctor = clz.getDeclaredConstructor(PerfMarkTransformerTest.class);
450     ctor.setAccessible(true);
451     Runnable instance = (Runnable) ctor.newInstance(this);
452     instance.run();
453     List<Mark> marks = Storage.readForTest();
454     assertThat(marks).hasSize(4);
455 
456     assertEquals(marks.get(0).withTaskName("task"), marks.get(0));
457 
458     assertEquals(marks.get(1).getTagKey(), "PerfMark.startCallSite");
459     Truth.assertThat(marks.get(1).getTagStringValue()).contains("PerfMarkTransformerTest$");
460     Truth.assertThat(marks.get(1).getTagStringValue()).contains("run");
461 
462     assertEquals(marks.get(2).getTagKey(), "PerfMark.stopCallSite");
463     Truth.assertThat(marks.get(2).getTagStringValue()).contains("PerfMarkTransformerTest$");
464     Truth.assertThat(marks.get(2).getTagStringValue()).contains("run");
465 
466     assertEquals(marks.get(3).withTaskName("task"), marks.get(3));
467   }
468 
transformAndLoad(Class<?> toLoad, Class<?> ...extra)469   private static Class<?> transformAndLoad(Class<?> toLoad, Class<?> ...extra) throws IOException {
470     Map<String, Class<?>> toTransform = new HashMap<>();
471     for (Class<?>  clz : extra) {
472       toTransform.put(clz.getName(), clz);
473     }
474     toTransform.put(toLoad.getName(), toLoad);
475     try {
476       return new ClassLoader(toLoad.getClassLoader()) {
477 
478         @Override
479         protected Class<?> loadClass(String binaryClassName, boolean resolve) throws ClassNotFoundException {
480           Class<?> existing = toTransform.get(binaryClassName);
481           if (existing == null) {
482             return super.loadClass(binaryClassName, resolve);
483           }
484           String internalFullyQualifiedClassName = binaryClassName.replace('.', '/');
485           String resourceName = internalFullyQualifiedClassName + ".class";
486           byte[] data;
487           try (InputStream stream = getResourceAsStream(resourceName)) {
488             data = stream.readAllBytes();
489           } catch (IOException e) {
490             throw new RuntimeException(e);
491           }
492           byte[] newClassBytes =
493               new PerfMarkTransformer(null).transformInternal(
494                   this, internalFullyQualifiedClassName, existing, null, data);
495           if (newClassBytes == null) {
496             newClassBytes = data;
497           }
498           Class<?> newClass = defineClass(binaryClassName, newClassBytes, 0, newClassBytes.length);
499           if (resolve) {
500             resolveClass(newClass);
501           }
502           return newClass;
503         }
504       }.loadClass(toLoad.getName());
505     } catch (ClassNotFoundException e) {
506       throw new RuntimeException(e);
507     }
508   }
509 }
510