xref: /aosp_15_r20/art/test/2246-trace-stream/src/BaseTraceParser.java (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
1*795d594fSAndroid Build Coastguard Worker /*
2*795d594fSAndroid Build Coastguard Worker  * Copyright (C) 2023 The Android Open Source Project
3*795d594fSAndroid Build Coastguard Worker  *
4*795d594fSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*795d594fSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*795d594fSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*795d594fSAndroid Build Coastguard Worker  *
8*795d594fSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*795d594fSAndroid Build Coastguard Worker  *
10*795d594fSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*795d594fSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*795d594fSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*795d594fSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*795d594fSAndroid Build Coastguard Worker  * limitations under the License.
15*795d594fSAndroid Build Coastguard Worker  */
16*795d594fSAndroid Build Coastguard Worker 
17*795d594fSAndroid Build Coastguard Worker import java.io.DataInputStream;
18*795d594fSAndroid Build Coastguard Worker import java.io.File;
19*795d594fSAndroid Build Coastguard Worker import java.io.FileInputStream;
20*795d594fSAndroid Build Coastguard Worker import java.io.IOException;
21*795d594fSAndroid Build Coastguard Worker import java.nio.charset.StandardCharsets;
22*795d594fSAndroid Build Coastguard Worker import java.util.HashMap;
23*795d594fSAndroid Build Coastguard Worker import java.util.Set;
24*795d594fSAndroid Build Coastguard Worker 
25*795d594fSAndroid Build Coastguard Worker abstract class BaseTraceParser {
26*795d594fSAndroid Build Coastguard Worker     public static final int MAGIC_NUMBER = 0x574f4c53;
27*795d594fSAndroid Build Coastguard Worker     public static final int DUAL_CLOCK_VERSION = 3;
28*795d594fSAndroid Build Coastguard Worker     public static final int WALL_CLOCK_VERSION = 2;
29*795d594fSAndroid Build Coastguard Worker     public static final int STREAMING_DUAL_CLOCK_VERSION = 0xF3;
30*795d594fSAndroid Build Coastguard Worker     public static final int STREAMING_WALL_CLOCK_VERSION = 0xF2;
31*795d594fSAndroid Build Coastguard Worker     public static final String START_SECTION_ID = "*";
32*795d594fSAndroid Build Coastguard Worker     public static final String METHODS_SECTION_ID = "*methods";
33*795d594fSAndroid Build Coastguard Worker     public static final String THREADS_SECTION_ID = "*threads";
34*795d594fSAndroid Build Coastguard Worker     public static final String END_SECTION_ID = "*end";
35*795d594fSAndroid Build Coastguard Worker     public static final Set<String> ignoredMethods = Set.of(
36*795d594fSAndroid Build Coastguard Worker         "java.lang.ref.ReferenceQueue add (Ljava/lang/ref/Reference;)V ReferenceQueue.java");
37*795d594fSAndroid Build Coastguard Worker 
InitializeParser(File file)38*795d594fSAndroid Build Coastguard Worker     public void InitializeParser(File file) throws IOException {
39*795d594fSAndroid Build Coastguard Worker         dataStream = new DataInputStream(new FileInputStream(file));
40*795d594fSAndroid Build Coastguard Worker         methodIdMap = new HashMap<Integer, String>();
41*795d594fSAndroid Build Coastguard Worker         threadIdMap = new HashMap<Integer, String>();
42*795d594fSAndroid Build Coastguard Worker         nestingLevelMap = new HashMap<Integer, Integer>();
43*795d594fSAndroid Build Coastguard Worker         ignoredMethodNestingLevelMap = new HashMap<Integer, Integer>();
44*795d594fSAndroid Build Coastguard Worker         threadEventsMap = new HashMap<String, String>();
45*795d594fSAndroid Build Coastguard Worker         threadTimestamp1Map = new HashMap<Integer, Integer>();
46*795d594fSAndroid Build Coastguard Worker         threadTimestamp2Map = new HashMap<Integer, Integer>();
47*795d594fSAndroid Build Coastguard Worker     }
48*795d594fSAndroid Build Coastguard Worker 
closeFile()49*795d594fSAndroid Build Coastguard Worker     public void closeFile() throws IOException {
50*795d594fSAndroid Build Coastguard Worker         dataStream.close();
51*795d594fSAndroid Build Coastguard Worker     }
52*795d594fSAndroid Build Coastguard Worker 
readString(int numBytes)53*795d594fSAndroid Build Coastguard Worker     public String readString(int numBytes) throws IOException {
54*795d594fSAndroid Build Coastguard Worker         byte[] buffer = new byte[numBytes];
55*795d594fSAndroid Build Coastguard Worker         dataStream.readFully(buffer);
56*795d594fSAndroid Build Coastguard Worker         return new String(buffer, StandardCharsets.UTF_8);
57*795d594fSAndroid Build Coastguard Worker     }
58*795d594fSAndroid Build Coastguard Worker 
readLine()59*795d594fSAndroid Build Coastguard Worker     public String readLine() throws IOException {
60*795d594fSAndroid Build Coastguard Worker         StringBuilder sb = new StringBuilder();
61*795d594fSAndroid Build Coastguard Worker         char lineSeparator = '\n';
62*795d594fSAndroid Build Coastguard Worker         char c = (char)dataStream.readUnsignedByte();
63*795d594fSAndroid Build Coastguard Worker         while ( c != lineSeparator) {
64*795d594fSAndroid Build Coastguard Worker             sb.append(c);
65*795d594fSAndroid Build Coastguard Worker             c = (char)dataStream.readUnsignedByte();
66*795d594fSAndroid Build Coastguard Worker         }
67*795d594fSAndroid Build Coastguard Worker         return sb.toString();
68*795d594fSAndroid Build Coastguard Worker     }
69*795d594fSAndroid Build Coastguard Worker 
readNumber(int numBytes)70*795d594fSAndroid Build Coastguard Worker     public int readNumber(int numBytes) throws IOException {
71*795d594fSAndroid Build Coastguard Worker         int number = 0;
72*795d594fSAndroid Build Coastguard Worker         for (int i = 0; i < numBytes; i++) {
73*795d594fSAndroid Build Coastguard Worker             number += dataStream.readUnsignedByte() << (i * 8);
74*795d594fSAndroid Build Coastguard Worker         }
75*795d594fSAndroid Build Coastguard Worker         return number;
76*795d594fSAndroid Build Coastguard Worker     }
77*795d594fSAndroid Build Coastguard Worker 
validateTraceHeader(int expectedVersion)78*795d594fSAndroid Build Coastguard Worker     public void validateTraceHeader(int expectedVersion) throws Exception {
79*795d594fSAndroid Build Coastguard Worker         // Read 4-byte magicNumber.
80*795d594fSAndroid Build Coastguard Worker         int magicNumber = readNumber(4);
81*795d594fSAndroid Build Coastguard Worker         if (magicNumber != MAGIC_NUMBER) {
82*795d594fSAndroid Build Coastguard Worker             throw new Exception("Magic number doesn't match. Expected "
83*795d594fSAndroid Build Coastguard Worker                     + Integer.toHexString(MAGIC_NUMBER) + " Got "
84*795d594fSAndroid Build Coastguard Worker                     + Integer.toHexString(magicNumber));
85*795d594fSAndroid Build Coastguard Worker         }
86*795d594fSAndroid Build Coastguard Worker         // Read 2-byte version.
87*795d594fSAndroid Build Coastguard Worker         int version = readNumber(2);
88*795d594fSAndroid Build Coastguard Worker         if (version != expectedVersion) {
89*795d594fSAndroid Build Coastguard Worker             throw new Exception(
90*795d594fSAndroid Build Coastguard Worker                     "Unexpected version. Expected " + expectedVersion + " Got " + version);
91*795d594fSAndroid Build Coastguard Worker         }
92*795d594fSAndroid Build Coastguard Worker         traceFormatVersion = version & 0xF;
93*795d594fSAndroid Build Coastguard Worker         // Read 2-byte headerLength length.
94*795d594fSAndroid Build Coastguard Worker         int headerLength = readNumber(2);
95*795d594fSAndroid Build Coastguard Worker         // Read 8-byte starting time - Ignore timestamps since they are not deterministic.
96*795d594fSAndroid Build Coastguard Worker         dataStream.skipBytes(8);
97*795d594fSAndroid Build Coastguard Worker         // 4 byte magicNumber + 2 byte version + 2 byte offset + 8 byte timestamp.
98*795d594fSAndroid Build Coastguard Worker         int numBytesRead = 16;
99*795d594fSAndroid Build Coastguard Worker         if (version >= DUAL_CLOCK_VERSION) {
100*795d594fSAndroid Build Coastguard Worker             // Read 2-byte record size.
101*795d594fSAndroid Build Coastguard Worker             // TODO(mythria): Check why this is needed. We can derive recordSize from version. Not
102*795d594fSAndroid Build Coastguard Worker             // sure why this is needed.
103*795d594fSAndroid Build Coastguard Worker             recordSize = readNumber(2);
104*795d594fSAndroid Build Coastguard Worker             numBytesRead += 2;
105*795d594fSAndroid Build Coastguard Worker         }
106*795d594fSAndroid Build Coastguard Worker         // Skip any padding.
107*795d594fSAndroid Build Coastguard Worker         if (headerLength > numBytesRead) {
108*795d594fSAndroid Build Coastguard Worker             dataStream.skipBytes(headerLength - numBytesRead);
109*795d594fSAndroid Build Coastguard Worker         }
110*795d594fSAndroid Build Coastguard Worker     }
111*795d594fSAndroid Build Coastguard Worker 
GetThreadID()112*795d594fSAndroid Build Coastguard Worker     public int GetThreadID() throws IOException {
113*795d594fSAndroid Build Coastguard Worker         // Read 2-byte thread-id. On host thread-ids can be greater than 16-bit but it is truncated
114*795d594fSAndroid Build Coastguard Worker         // to 16-bits in the trace.
115*795d594fSAndroid Build Coastguard Worker         int threadId = readNumber(2);
116*795d594fSAndroid Build Coastguard Worker         return threadId;
117*795d594fSAndroid Build Coastguard Worker     }
118*795d594fSAndroid Build Coastguard Worker 
GetEntryHeader()119*795d594fSAndroid Build Coastguard Worker     public int GetEntryHeader() throws IOException {
120*795d594fSAndroid Build Coastguard Worker         // Read 1-byte header type
121*795d594fSAndroid Build Coastguard Worker         return readNumber(1);
122*795d594fSAndroid Build Coastguard Worker     }
123*795d594fSAndroid Build Coastguard Worker 
ProcessMethodInfoEntry()124*795d594fSAndroid Build Coastguard Worker     public void ProcessMethodInfoEntry() throws IOException {
125*795d594fSAndroid Build Coastguard Worker         // Read 2-byte method info size
126*795d594fSAndroid Build Coastguard Worker         int headerLength = readNumber(2);
127*795d594fSAndroid Build Coastguard Worker         // Read header size data.
128*795d594fSAndroid Build Coastguard Worker         String methodInfo = readString(headerLength);
129*795d594fSAndroid Build Coastguard Worker         String[] tokens = methodInfo.split("\t", 2);
130*795d594fSAndroid Build Coastguard Worker         // Get methodId and record methodId -> methodName map.
131*795d594fSAndroid Build Coastguard Worker         int methodId = Integer.decode(tokens[0]);
132*795d594fSAndroid Build Coastguard Worker         String methodLine = tokens[1].replace('\t', ' ');
133*795d594fSAndroid Build Coastguard Worker         methodLine = methodLine.substring(0, methodLine.length() - 1);
134*795d594fSAndroid Build Coastguard Worker         methodIdMap.put(methodId, methodLine);
135*795d594fSAndroid Build Coastguard Worker     }
136*795d594fSAndroid Build Coastguard Worker 
ProcessThreadInfoEntry()137*795d594fSAndroid Build Coastguard Worker     public void ProcessThreadInfoEntry() throws IOException {
138*795d594fSAndroid Build Coastguard Worker         // Read 2-byte thread id
139*795d594fSAndroid Build Coastguard Worker         int threadId = readNumber(2);
140*795d594fSAndroid Build Coastguard Worker         // Read 2-byte thread info size
141*795d594fSAndroid Build Coastguard Worker         int headerLength = readNumber(2);
142*795d594fSAndroid Build Coastguard Worker         // Read header size data.
143*795d594fSAndroid Build Coastguard Worker         String threadInfo = readString(headerLength);
144*795d594fSAndroid Build Coastguard Worker         threadIdMap.put(threadId, threadInfo);
145*795d594fSAndroid Build Coastguard Worker     }
146*795d594fSAndroid Build Coastguard Worker 
ShouldCheckThread(int threadId, String threadName)147*795d594fSAndroid Build Coastguard Worker     public boolean ShouldCheckThread(int threadId, String threadName) throws Exception {
148*795d594fSAndroid Build Coastguard Worker         if (!threadIdMap.containsKey(threadId)) {
149*795d594fSAndroid Build Coastguard Worker           System.out.println("no threadId -> name  mapping for thread " + threadId);
150*795d594fSAndroid Build Coastguard Worker           // TODO(b/279547877): Ideally we should throw here, since it isn't expected. Just
151*795d594fSAndroid Build Coastguard Worker           // continuing to get more logs from the bots to see what's happening here. The
152*795d594fSAndroid Build Coastguard Worker           // test will fail anyway because the expected output will be different.
153*795d594fSAndroid Build Coastguard Worker           return true;
154*795d594fSAndroid Build Coastguard Worker         }
155*795d594fSAndroid Build Coastguard Worker 
156*795d594fSAndroid Build Coastguard Worker         return threadIdMap.get(threadId).equals(threadName);
157*795d594fSAndroid Build Coastguard Worker     }
158*795d594fSAndroid Build Coastguard Worker 
eventTypeToString(int eventType, int threadId)159*795d594fSAndroid Build Coastguard Worker     public String eventTypeToString(int eventType, int threadId) {
160*795d594fSAndroid Build Coastguard Worker         if (!nestingLevelMap.containsKey(threadId)) {
161*795d594fSAndroid Build Coastguard Worker             nestingLevelMap.put(threadId, 0);
162*795d594fSAndroid Build Coastguard Worker         }
163*795d594fSAndroid Build Coastguard Worker 
164*795d594fSAndroid Build Coastguard Worker         int nestingLevel = nestingLevelMap.get(threadId);
165*795d594fSAndroid Build Coastguard Worker         String str = "";
166*795d594fSAndroid Build Coastguard Worker         for (int i = 0; i < nestingLevel; i++) {
167*795d594fSAndroid Build Coastguard Worker             str += ".";
168*795d594fSAndroid Build Coastguard Worker         }
169*795d594fSAndroid Build Coastguard Worker         switch (eventType) {
170*795d594fSAndroid Build Coastguard Worker             case 0:
171*795d594fSAndroid Build Coastguard Worker                 nestingLevel++;
172*795d594fSAndroid Build Coastguard Worker                 str += ".>>";
173*795d594fSAndroid Build Coastguard Worker                 break;
174*795d594fSAndroid Build Coastguard Worker             case 1:
175*795d594fSAndroid Build Coastguard Worker                 nestingLevel--;
176*795d594fSAndroid Build Coastguard Worker                 str += "<<";
177*795d594fSAndroid Build Coastguard Worker                 break;
178*795d594fSAndroid Build Coastguard Worker             case 2:
179*795d594fSAndroid Build Coastguard Worker                 nestingLevel--;
180*795d594fSAndroid Build Coastguard Worker                 str += "<<E";
181*795d594fSAndroid Build Coastguard Worker                 break;
182*795d594fSAndroid Build Coastguard Worker             default:
183*795d594fSAndroid Build Coastguard Worker                 str += "??";
184*795d594fSAndroid Build Coastguard Worker         }
185*795d594fSAndroid Build Coastguard Worker         nestingLevelMap.put(threadId, nestingLevel);
186*795d594fSAndroid Build Coastguard Worker         return str;
187*795d594fSAndroid Build Coastguard Worker     }
188*795d594fSAndroid Build Coastguard Worker 
CheckTimestamp(int timestamp, int threadId, HashMap<Integer, Integer> threadTimestampMap)189*795d594fSAndroid Build Coastguard Worker     public void CheckTimestamp(int timestamp, int threadId,
190*795d594fSAndroid Build Coastguard Worker             HashMap<Integer, Integer> threadTimestampMap) throws Exception {
191*795d594fSAndroid Build Coastguard Worker         if (threadTimestampMap.containsKey(threadId)) {
192*795d594fSAndroid Build Coastguard Worker             int oldTimestamp = threadTimestampMap.get(threadId);
193*795d594fSAndroid Build Coastguard Worker             if (timestamp < oldTimestamp) {
194*795d594fSAndroid Build Coastguard Worker                 throw new Exception("timestamps are not increasing current: " + timestamp
195*795d594fSAndroid Build Coastguard Worker                         + "  earlier: " + oldTimestamp);
196*795d594fSAndroid Build Coastguard Worker             }
197*795d594fSAndroid Build Coastguard Worker         }
198*795d594fSAndroid Build Coastguard Worker         threadTimestampMap.put(threadId, timestamp);
199*795d594fSAndroid Build Coastguard Worker     }
200*795d594fSAndroid Build Coastguard Worker 
ProcessEventEntry(int threadId)201*795d594fSAndroid Build Coastguard Worker     public String ProcessEventEntry(int threadId) throws IOException, Exception {
202*795d594fSAndroid Build Coastguard Worker         // Read 4-byte method value
203*795d594fSAndroid Build Coastguard Worker         int methodAndEvent = readNumber(4);
204*795d594fSAndroid Build Coastguard Worker         int methodId = methodAndEvent & ~0x3;
205*795d594fSAndroid Build Coastguard Worker         int eventType = methodAndEvent & 0x3;
206*795d594fSAndroid Build Coastguard Worker 
207*795d594fSAndroid Build Coastguard Worker         String methodName = methodIdMap.get(methodId);
208*795d594fSAndroid Build Coastguard Worker         int currNestingLevel = 0;
209*795d594fSAndroid Build Coastguard Worker         if (nestingLevelMap.containsKey(threadId)) {
210*795d594fSAndroid Build Coastguard Worker           currNestingLevel = nestingLevelMap.get(threadId);
211*795d594fSAndroid Build Coastguard Worker         }
212*795d594fSAndroid Build Coastguard Worker         boolean recordMethodEvent = true;
213*795d594fSAndroid Build Coastguard Worker         if (!ignoredMethodNestingLevelMap.containsKey(threadId)) {
214*795d594fSAndroid Build Coastguard Worker           if (ignoredMethods.contains(methodName)) {
215*795d594fSAndroid Build Coastguard Worker             // This should be an entry event.
216*795d594fSAndroid Build Coastguard Worker             if (eventType != 0) {
217*795d594fSAndroid Build Coastguard Worker               throw new Exception("Seeing an exit for an ignored event without an entry");
218*795d594fSAndroid Build Coastguard Worker             }
219*795d594fSAndroid Build Coastguard Worker             ignoredMethodNestingLevelMap.put(threadId, currNestingLevel);
220*795d594fSAndroid Build Coastguard Worker             recordMethodEvent = false;
221*795d594fSAndroid Build Coastguard Worker           }
222*795d594fSAndroid Build Coastguard Worker         } else {
223*795d594fSAndroid Build Coastguard Worker           // We need to ignore all calls until the ignored method exits.
224*795d594fSAndroid Build Coastguard Worker           int ignoredMethodDepth = ignoredMethodNestingLevelMap.get(threadId);
225*795d594fSAndroid Build Coastguard Worker           // If this is a method exit and we are at the right depth remove the entry so we start
226*795d594fSAndroid Build Coastguard Worker           // recording from the next event.
227*795d594fSAndroid Build Coastguard Worker           if (ignoredMethodDepth == currNestingLevel - 1 && eventType != 0) {
228*795d594fSAndroid Build Coastguard Worker             ignoredMethodNestingLevelMap.remove(threadId);
229*795d594fSAndroid Build Coastguard Worker             if (!ignoredMethods.contains(methodName)) {
230*795d594fSAndroid Build Coastguard Worker               throw new Exception("Unexpected method on exit. Mismatch on method entry and exit");
231*795d594fSAndroid Build Coastguard Worker             }
232*795d594fSAndroid Build Coastguard Worker           }
233*795d594fSAndroid Build Coastguard Worker           recordMethodEvent = false;
234*795d594fSAndroid Build Coastguard Worker         }
235*795d594fSAndroid Build Coastguard Worker         String str = eventTypeToString(eventType, threadId) + " " + threadIdMap.get(threadId)
236*795d594fSAndroid Build Coastguard Worker                 + " " + methodName;
237*795d594fSAndroid Build Coastguard Worker         // Depending on the version skip either one or two timestamps.
238*795d594fSAndroid Build Coastguard Worker         int timestamp1 = readNumber(4);
239*795d594fSAndroid Build Coastguard Worker         CheckTimestamp(timestamp1, threadId, threadTimestamp1Map);
240*795d594fSAndroid Build Coastguard Worker         if (traceFormatVersion != 2) {
241*795d594fSAndroid Build Coastguard Worker             // Read second timestamp
242*795d594fSAndroid Build Coastguard Worker             int timestamp2 = readNumber(4);
243*795d594fSAndroid Build Coastguard Worker             CheckTimestamp(timestamp2, threadId, threadTimestamp2Map);
244*795d594fSAndroid Build Coastguard Worker         }
245*795d594fSAndroid Build Coastguard Worker         return recordMethodEvent? str : null;
246*795d594fSAndroid Build Coastguard Worker     }
247*795d594fSAndroid Build Coastguard Worker 
UpdateThreadEvents(int threadId, String entry)248*795d594fSAndroid Build Coastguard Worker     public void UpdateThreadEvents(int threadId, String entry) {
249*795d594fSAndroid Build Coastguard Worker         String threadName = threadIdMap.get(threadId);
250*795d594fSAndroid Build Coastguard Worker         if (!threadEventsMap.containsKey(threadName)) {
251*795d594fSAndroid Build Coastguard Worker             threadEventsMap.put(threadName, entry);
252*795d594fSAndroid Build Coastguard Worker             return;
253*795d594fSAndroid Build Coastguard Worker         }
254*795d594fSAndroid Build Coastguard Worker         threadEventsMap.put(threadName, threadEventsMap.get(threadName) + "\n" + entry);
255*795d594fSAndroid Build Coastguard Worker     }
256*795d594fSAndroid Build Coastguard Worker 
CheckTraceFileFormat(File traceFile, int expectedVersion, String threadName)257*795d594fSAndroid Build Coastguard Worker     public abstract void CheckTraceFileFormat(File traceFile,
258*795d594fSAndroid Build Coastguard Worker         int expectedVersion, String threadName) throws Exception;
259*795d594fSAndroid Build Coastguard Worker 
260*795d594fSAndroid Build Coastguard Worker     DataInputStream dataStream;
261*795d594fSAndroid Build Coastguard Worker     HashMap<Integer, String> methodIdMap;
262*795d594fSAndroid Build Coastguard Worker     HashMap<Integer, String> threadIdMap;
263*795d594fSAndroid Build Coastguard Worker     HashMap<Integer, Integer> nestingLevelMap;
264*795d594fSAndroid Build Coastguard Worker     HashMap<Integer, Integer> ignoredMethodNestingLevelMap;
265*795d594fSAndroid Build Coastguard Worker     HashMap<String, String> threadEventsMap;
266*795d594fSAndroid Build Coastguard Worker     HashMap<Integer, Integer> threadTimestamp1Map;
267*795d594fSAndroid Build Coastguard Worker     HashMap<Integer, Integer> threadTimestamp2Map;
268*795d594fSAndroid Build Coastguard Worker     int recordSize = 0;
269*795d594fSAndroid Build Coastguard Worker     int traceFormatVersion = 0;
270*795d594fSAndroid Build Coastguard Worker }
271