• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.DataInputStream;
18 import java.io.File;
19 import java.io.FileInputStream;
20 import java.io.IOException;
21 import java.nio.charset.StandardCharsets;
22 import java.util.HashMap;
23 
24 abstract class BaseTraceParser {
25     public static final int MAGIC_NUMBER = 0x574f4c53;
26     public static final int DUAL_CLOCK_VERSION = 3;
27     public static final int STREAMING_DUAL_CLOCK_VERSION = 0xF3;
28     public static final String START_SECTION_ID = "*";
29     public static final String METHODS_SECTION_ID = "*methods";
30     public static final String THREADS_SECTION_ID = "*threads";
31     public static final String END_SECTION_ID = "*end";
32 
InitializeParser(File file)33     public void InitializeParser(File file) throws IOException {
34         dataStream = new DataInputStream(new FileInputStream(file));
35         methodIdMap = new HashMap<Integer, String>();
36         threadIdMap = new HashMap<Integer, String>();
37         nestingLevelMap = new HashMap<Integer, Integer>();
38         threadEventsMap = new HashMap<String, String>();
39     }
40 
closeFile()41     public void closeFile() throws IOException {
42         dataStream.close();
43     }
44 
readString(int numBytes)45     public String readString(int numBytes) throws IOException {
46         byte[] buffer = new byte[numBytes];
47         dataStream.readFully(buffer);
48         return new String(buffer, StandardCharsets.UTF_8);
49     }
50 
readLine()51     public String readLine() throws IOException {
52         StringBuilder sb = new StringBuilder();
53         char lineSeparator = '\n';
54         char c = (char)dataStream.readUnsignedByte();
55         while ( c != lineSeparator) {
56             sb.append(c);
57             c = (char)dataStream.readUnsignedByte();
58         }
59         return sb.toString();
60     }
61 
readNumber(int numBytes)62     public int readNumber(int numBytes) throws IOException {
63         int number = 0;
64         for (int i = 0; i < numBytes; i++) {
65             number += dataStream.readUnsignedByte() << (i * 8);
66         }
67         return number;
68     }
69 
validateTraceHeader(int expectedVersion)70     public void validateTraceHeader(int expectedVersion) throws Exception {
71         // Read 4-byte magicNumber.
72         int magicNumber = readNumber(4);
73         if (magicNumber != MAGIC_NUMBER) {
74             throw new Exception("Magic number doesn't match. Expected "
75                     + Integer.toHexString(MAGIC_NUMBER) + " Got "
76                     + Integer.toHexString(magicNumber));
77         }
78         // Read 2-byte version.
79         int version = readNumber(2);
80         if (version != expectedVersion) {
81             throw new Exception(
82                     "Unexpected version. Expected " + expectedVersion + " Got " + version);
83         }
84         traceFormatVersion = version & 0xF;
85         // Read 2-byte headerLength length.
86         int headerLength = readNumber(2);
87         // Read 8-byte starting time - Ignore timestamps since they are not deterministic.
88         dataStream.skipBytes(8);
89         // 4 byte magicNumber + 2 byte version + 2 byte offset + 8 byte timestamp.
90         int numBytesRead = 16;
91         if (version >= DUAL_CLOCK_VERSION) {
92             // Read 2-byte record size.
93             // TODO(mythria): Check why this is needed. We can derive recordSize from version. Not
94             // sure why this is needed.
95             recordSize = readNumber(2);
96             numBytesRead += 2;
97         }
98         // Skip any padding.
99         if (headerLength > numBytesRead) {
100             dataStream.skipBytes(headerLength - numBytesRead);
101         }
102     }
103 
GetEntryHeader()104     public int GetEntryHeader() throws IOException {
105         // Read 2-byte thread-id. On host thread-ids can be greater than 16-bit.
106         int threadId = readNumber(2);
107         if (threadId != 0) {
108             return threadId;
109         }
110         // Read 1-byte header type
111         return readNumber(1);
112     }
113 
ProcessMethodInfoEntry()114     public void ProcessMethodInfoEntry() throws IOException {
115         // Read 2-byte method info size
116         int headerLength = readNumber(2);
117         // Read header size data.
118         String methodInfo = readString(headerLength);
119         String[] tokens = methodInfo.split("\t", 2);
120         // Get methodId and record methodId -> methodName map.
121         int methodId = Integer.decode(tokens[0]);
122         String methodLine = tokens[1].replace('\t', ' ');
123         methodLine = methodLine.substring(0, methodLine.length() - 1);
124         methodIdMap.put(methodId, methodLine);
125     }
126 
ProcessThreadInfoEntry()127     public void ProcessThreadInfoEntry() throws IOException {
128         // Read 2-byte thread id
129         int threadId = readNumber(2);
130         // Read 2-byte thread info size
131         int headerLength = readNumber(2);
132         // Read header size data.
133         String threadInfo = readString(headerLength);
134         threadIdMap.put(threadId, threadInfo);
135     }
136 
ShouldIgnoreThread(int threadId)137     public boolean ShouldIgnoreThread(int threadId) throws Exception {
138         if (threadIdMap.get(threadId).contains("Daemon")) {
139             return true;
140         }
141         return false;
142     }
143 
eventTypeToString(int eventType, int threadId)144     public String eventTypeToString(int eventType, int threadId) {
145         if (!nestingLevelMap.containsKey(threadId)) {
146             nestingLevelMap.put(threadId, 0);
147         }
148 
149         int nestingLevel = nestingLevelMap.get(threadId);
150         String str = "";
151         for (int i = 0; i < nestingLevel; i++) {
152             str += ".";
153         }
154         switch (eventType) {
155             case 0:
156                 nestingLevel++;
157                 str += ".>>";
158                 break;
159             case 1:
160                 nestingLevel--;
161                 str += "<<";
162                 break;
163             case 2:
164                 nestingLevel--;
165                 str += "<<E";
166                 break;
167             default:
168                 str += "??";
169         }
170         nestingLevelMap.put(threadId, nestingLevel);
171         return str;
172     }
173 
ProcessEventEntry(int threadId)174     public String ProcessEventEntry(int threadId) throws IOException {
175         // Read 4-byte method value
176         int methodAndEvent = readNumber(4);
177         int methodId = methodAndEvent & ~0x3;
178         int eventType = methodAndEvent & 0x3;
179 
180         String str = eventTypeToString(eventType, threadId) + " " + threadIdMap.get(threadId)
181                 + " " + methodIdMap.get(methodId);
182         // Depending on the version skip either one or two timestamps.
183         // TODO(mythria): Probably add a check that time stamps are always greater than initial
184         // timestamp.
185         int numBytesTimestamp = (traceFormatVersion == 2) ? 4 : 8;
186         dataStream.skipBytes(numBytesTimestamp);
187         return str;
188     }
189 
UpdateThreadEvents(int threadId, String entry)190     public void UpdateThreadEvents(int threadId, String entry) {
191         String threadName = threadIdMap.get(threadId);
192         if (!threadEventsMap.containsKey(threadName)) {
193             threadEventsMap.put(threadName, entry);
194             return;
195         }
196         threadEventsMap.put(threadName, threadEventsMap.get(threadName) + "\n" + entry);
197     }
198 
CheckTraceFileFormat(File traceFile, int expectedVersion)199     public abstract void CheckTraceFileFormat(File traceFile, int expectedVersion)
200             throws Exception;
201 
202     DataInputStream dataStream;
203     HashMap<Integer, String> methodIdMap;
204     HashMap<Integer, String> threadIdMap;
205     HashMap<Integer, Integer> nestingLevelMap;
206     HashMap<String, String> threadEventsMap;
207     int recordSize = 0;
208     int traceFormatVersion = 0;
209 }
210