1 /*
2  * Copyright (C) 2024 The Android Open Source Project
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 import java.io.File;
18 import java.io.FileDescriptor;
19 import java.io.FileOutputStream;
20 import java.io.IOException;
21 import java.lang.reflect.Method;
22 
23 public class Main {
24     private static final String TEMP_FILE_NAME_PREFIX = "test";
25     private static final String TEMP_FILE_NAME_SUFFIX = ".trace";
26     private static final int WALL_CLOCK_FLAG = 0x010;
27     private static final int TRACE_OUTPUT_V2_FLAG = 0b010;
28     private static final int STREAMING_DUAL_CLOCK_VERSION = 1;
29     private static final int STREAMING_WALL_CLOCK_VERSION = 1;
30     private static File file;
31 
main(String[] args)32     public static void main(String[] args) throws Exception {
33         System.loadLibrary(args[0]);
34         String name = System.getProperty("java.vm.name");
35         if (!"Dalvik".equals(name)) {
36             System.out.println("This test is not supported on " + name);
37             return;
38         }
39 
40         ensureJitCompiled(Main.class, "$noinline$doSomeWorkJIT");
41 
42         System.out.println("***** streaming test - dual clock *******");
43         testTracing(
44                 /* streaming=*/true, /* flags= */ 0, STREAMING_DUAL_CLOCK_VERSION);
45 
46         System.out.println("***** streaming test - wall clock *******");
47         testTracing(
48                 /* streaming=*/true, /* flags= */ WALL_CLOCK_FLAG, STREAMING_WALL_CLOCK_VERSION);
49     }
50 
testTracing(boolean streaming, int flags, int expected_version)51     public static void testTracing(boolean streaming, int flags, int expected_version)
52             throws Exception {
53         Main m = new Main();
54         Thread t = new Thread(() -> {
55             try {
56                 file = createTempFile();
57                 FileOutputStream out_file = new FileOutputStream(file);
58                 VMDebug.startMethodTracingV2(
59                         file.getPath(), out_file.getFD(), 0, flags, false, 0, streaming);
60                 Main m1 = new Main();
61                 m1.$noinline$doSomeWork();
62                 // Call JITed code multiple times to flush out any issues with timestamps.
63                 for (int i = 0; i < 20; i++) {
64                     m.$noinline$doSomeWorkJIT();
65                 }
66                 VMDebug.$noinline$stopMethodTracing();
67                 out_file.close();
68                 dumpTrace(file.getAbsolutePath(), "TestThread2246");
69                 file.delete();
70             } catch (Exception e) {
71                 System.out.println("Exception in thread " + e);
72                 e.printStackTrace();
73             } finally {
74                 file.delete();
75             }
76         }, "TestThread2246");
77         try {
78             if (VMDebug.getMethodTracingMode() != 0) {
79                 VMDebug.$noinline$stopMethodTracing();
80             }
81 
82             t.start();
83             t.join();
84 
85             file = createTempFile();
86             FileOutputStream main_out_file = new FileOutputStream(file);
87             VMDebug.startMethodTracingV2(
88                     file.getPath(), main_out_file.getFD(), 0, flags, false, 0, streaming);
89             m.$noinline$doSomeWork();
90             // Call JITed code multiple times to flush out any issues with timestamps.
91             for (int i = 0; i < 20; i++) {
92                 m.$noinline$doSomeWorkJIT();
93             }
94             m.doSomeWorkThrow();
95             VMDebug.$noinline$stopMethodTracing();
96             main_out_file.close();
97             dumpTrace(file.getAbsolutePath(), "main");
98             file.delete();
99         } finally {
100             file.delete();
101         }
102     }
103 
createTempFile()104     private static File createTempFile() throws Exception {
105         try {
106             return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
107         } catch (IOException e) {
108             System.setProperty("java.io.tmpdir", "/data/local/tmp");
109             try {
110                 return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
111             } catch (IOException e2) {
112                 System.setProperty("java.io.tmpdir", "/sdcard");
113                 return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
114             }
115         }
116     }
117 
callOuterFunction()118     public void callOuterFunction() {
119         callLeafFunction();
120     }
121 
callLeafFunction()122     public void callLeafFunction() {}
123 
$noinline$doSomeWork()124     public void $noinline$doSomeWork() {
125         callOuterFunction();
126         callLeafFunction();
127     }
128 
$noinline$doSomeWorkJIT()129     public void $noinline$doSomeWorkJIT() {
130         callOuterFunction();
131         callLeafFunction();
132     }
133 
callThrowFunction()134     public void callThrowFunction() throws Exception {
135         throw new Exception("test");
136     }
137 
doSomeWorkThrow()138     public void doSomeWorkThrow() {
139         try {
140             callThrowFunction();
141         } catch (Exception e) {
142         }
143     }
144 
145     private static class VMDebug {
146         private static final Method startMethodTracingMethod;
147         private static final Method stopMethodTracingMethod;
148         private static final Method getMethodTracingModeMethod;
149         static {
150             try {
151                 Class<?> c = Class.forName("dalvik.system.VMDebug");
152                 startMethodTracingMethod = c.getDeclaredMethod("startMethodTracing", String.class,
153                         FileDescriptor.class, Integer.TYPE, Integer.TYPE, Boolean.TYPE,
154                         Integer.TYPE, Boolean.TYPE);
155                 stopMethodTracingMethod = c.getDeclaredMethod("stopMethodTracing");
156                 getMethodTracingModeMethod = c.getDeclaredMethod("getMethodTracingMode");
157             } catch (Exception e) {
158                 throw new RuntimeException(e);
159             }
160         }
161 
startMethodTracingV2(String filename, FileDescriptor fd, int bufferSize, int flags, boolean samplingEnabled, int intervalUs, boolean streaming)162         public static void startMethodTracingV2(String filename, FileDescriptor fd, int bufferSize,
163                 int flags, boolean samplingEnabled, int intervalUs, boolean streaming)
164                 throws Exception {
165             startMethodTracingMethod.invoke(null, filename, fd, bufferSize,
166                     flags | TRACE_OUTPUT_V2_FLAG, samplingEnabled, intervalUs, streaming);
167         }
$noinline$stopMethodTracing()168         public static void $noinline$stopMethodTracing() throws Exception {
169             stopMethodTracingMethod.invoke(null);
170         }
getMethodTracingMode()171         public static int getMethodTracingMode() throws Exception {
172             return (int) getMethodTracingModeMethod.invoke(null);
173         }
174     }
175 
ensureJitCompiled(Class<?> cls, String methodName)176     private static native void ensureJitCompiled(Class<?> cls, String methodName);
dumpTrace(String fileName, String threadName)177     private static native void dumpTrace(String fileName, String threadName);
178 }
179