Fix setting final_offset when flushing method traces
A bug was introduced accidentally when cleaning up method tracing code
when final_offset initialization was removed. This CL fixes by setting
it to the correct value.
Bug: 275117853
Test: art/test.py
Change-Id: Ifbce7fe9dc2a5340c81c73cd31d04cab49f4e9ec
diff --git a/runtime/trace.cc b/runtime/trace.cc
index e2a95f1..051a600 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -790,6 +790,10 @@
void Trace::FinishTracing() {
size_t final_offset = 0;
+ if (trace_output_mode_ != TraceOutputMode::kStreaming) {
+ final_offset = cur_offset_.load(std::memory_order_relaxed);
+ }
+
// Compute elapsed time.
uint64_t elapsed = GetMicroTime(GetTimestamp()) - start_time_;
@@ -1242,13 +1246,20 @@
std::ostream& os = *reinterpret_cast<std::ostream*>(arg);
std::string name;
t->GetThreadName(name);
- os << t->GetTid() << "\t" << name << "\n";
+ // We use only 16 bits to encode thread id. On Android, we don't expect to use more than
+ // 16-bits for a Tid. For 32-bit platforms it is always ensured we use less than 16 bits.
+ // See __check_max_thread_id in bionic for more details. Even on 64-bit the max threads
+ // is currently less than 65536.
+ // TODO(mythria): On host, we know thread ids can be greater than 16 bits. Consider adding
+ // a map similar to method ids.
+ DCHECK(!kIsTargetBuild || t->GetTid() < (1 << 16));
+ os << static_cast<uint16_t>(t->GetTid()) << "\t" << name << "\n";
}
void Trace::DumpThreadList(std::ostream& os) {
Thread* self = Thread::Current();
for (const auto& it : exited_threads_) {
- os << it.first << "\t" << it.second << "\n";
+ os << static_cast<uint16_t>(it.first) << "\t" << it.second << "\n";
}
Locks::thread_list_lock_->AssertNotHeld(self);
MutexLock mu(self, *Locks::thread_list_lock_);
diff --git a/test/2246-trace-stream/expected-stdout.txt b/test/2246-trace-stream/expected-stdout.txt
index c9d7405..90706b5 100644
--- a/test/2246-trace-stream/expected-stdout.txt
+++ b/test/2246-trace-stream/expected-stdout.txt
@@ -1,6 +1,7 @@
+***** streaming test *******
.>> TestThread2246 java.lang.Thread run ()V Thread.java
..>> TestThread2246 Main$$ExternalSyntheticLambda0 run ()V D8$$SyntheticClass
-...>> TestThread2246 Main lambda$main$0 ()V Main.java
+...>> TestThread2246 Main lambda$testTracing$0 ()V Main.java
....>> TestThread2246 Main <init> ()V Main.java
.....>> TestThread2246 java.lang.Object <init> ()V Object.java
.....<< TestThread2246 java.lang.Object <init> ()V Object.java
@@ -13,60 +14,143 @@
.....>> TestThread2246 Main callLeafFunction ()V Main.java
.....<< TestThread2246 Main callLeafFunction ()V Main.java
....<< TestThread2246 Main $noinline$doSomeWork ()V Main.java
-...<< TestThread2246 Main lambda$main$0 ()V Main.java
+...<< TestThread2246 Main lambda$testTracing$0 ()V Main.java
..<< TestThread2246 Main$$ExternalSyntheticLambda0 run ()V D8$$SyntheticClass
.<< TestThread2246 java.lang.Thread run ()V Thread.java
.>> main Main main ([Ljava/lang/String;)V Main.java
-..>> main Main$VMDebug startMethodTracing (Ljava/lang/String;Ljava/io/FileDescriptor;IIZIZ)V Main.java
-...>> main java.lang.reflect.Method invoke (Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; Method.java
-....>> main dalvik.system.VMDebug startMethodTracing (Ljava/lang/String;Ljava/io/FileDescriptor;IIZIZ)V VMDebug.java
-.....>> main dalvik.system.VMDebug startMethodTracingFd (Ljava/lang/String;IIIZIZ)V VMDebug.java
-.....<< main dalvik.system.VMDebug startMethodTracingFd (Ljava/lang/String;IIIZIZ)V VMDebug.java
-....<< main dalvik.system.VMDebug startMethodTracing (Ljava/lang/String;Ljava/io/FileDescriptor;IIZIZ)V VMDebug.java
-...<< main java.lang.reflect.Method invoke (Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; Method.java
-..<< main Main$VMDebug startMethodTracing (Ljava/lang/String;Ljava/io/FileDescriptor;IIZIZ)V Main.java
-..>> main java.lang.Thread start ()V Thread.java
-...>> main java.lang.ThreadGroup add (Ljava/lang/Thread;)V ThreadGroup.java
-...<< main java.lang.ThreadGroup add (Ljava/lang/Thread;)V ThreadGroup.java
-...>> main java.lang.Thread nativeCreate (Ljava/lang/Thread;JZ)V Thread.java
-...<< main java.lang.Thread nativeCreate (Ljava/lang/Thread;JZ)V Thread.java
-..<< main java.lang.Thread start ()V Thread.java
-..>> main java.lang.Thread join ()V Thread.java
-...>> main java.lang.Thread join (J)V Thread.java
-....>> main java.lang.System currentTimeMillis ()J System.java
-....<< main java.lang.System currentTimeMillis ()J System.java
-....>> main java.lang.Thread isAlive ()Z Thread.java
-....<< main java.lang.Thread isAlive ()Z Thread.java
-....>> main java.lang.Object wait (J)V Object.java
-.....>> main java.lang.Object wait (JI)V Object.java
-.....<< main java.lang.Object wait (JI)V Object.java
-....<< main java.lang.Object wait (J)V Object.java
-....>> main java.lang.Thread isAlive ()Z Thread.java
-....<< main java.lang.Thread isAlive ()Z Thread.java
-...<< main java.lang.Thread join (J)V Thread.java
-..<< main java.lang.Thread join ()V Thread.java
-..>> main Main $noinline$doSomeWork ()V Main.java
-...>> main Main callOuterFunction ()V Main.java
+..>> main Main testTracing (ZLBaseTraceParser;I)V Main.java
+...>> main Main$VMDebug startMethodTracing (Ljava/lang/String;Ljava/io/FileDescriptor;IIZIZ)V Main.java
+....>> main java.lang.reflect.Method invoke (Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; Method.java
+.....>> main dalvik.system.VMDebug startMethodTracing (Ljava/lang/String;Ljava/io/FileDescriptor;IIZIZ)V VMDebug.java
+......>> main dalvik.system.VMDebug startMethodTracingFd (Ljava/lang/String;IIIZIZ)V VMDebug.java
+......<< main dalvik.system.VMDebug startMethodTracingFd (Ljava/lang/String;IIIZIZ)V VMDebug.java
+.....<< main dalvik.system.VMDebug startMethodTracing (Ljava/lang/String;Ljava/io/FileDescriptor;IIZIZ)V VMDebug.java
+....<< main java.lang.reflect.Method invoke (Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; Method.java
+...<< main Main$VMDebug startMethodTracing (Ljava/lang/String;Ljava/io/FileDescriptor;IIZIZ)V Main.java
+...>> main java.lang.Thread start ()V Thread.java
+....>> main java.lang.ThreadGroup add (Ljava/lang/Thread;)V ThreadGroup.java
+....<< main java.lang.ThreadGroup add (Ljava/lang/Thread;)V ThreadGroup.java
+....>> main java.lang.Thread nativeCreate (Ljava/lang/Thread;JZ)V Thread.java
+....<< main java.lang.Thread nativeCreate (Ljava/lang/Thread;JZ)V Thread.java
+...<< main java.lang.Thread start ()V Thread.java
+...>> main java.lang.Thread join ()V Thread.java
+....>> main java.lang.Thread join (J)V Thread.java
+.....>> main java.lang.System currentTimeMillis ()J System.java
+.....<< main java.lang.System currentTimeMillis ()J System.java
+.....>> main java.lang.Thread isAlive ()Z Thread.java
+.....<< main java.lang.Thread isAlive ()Z Thread.java
+.....>> main java.lang.Object wait (J)V Object.java
+......>> main java.lang.Object wait (JI)V Object.java
+......<< main java.lang.Object wait (JI)V Object.java
+.....<< main java.lang.Object wait (J)V Object.java
+.....>> main java.lang.Thread isAlive ()Z Thread.java
+.....<< main java.lang.Thread isAlive ()Z Thread.java
+....<< main java.lang.Thread join (J)V Thread.java
+...<< main java.lang.Thread join ()V Thread.java
+...>> main Main $noinline$doSomeWork ()V Main.java
+....>> main Main callOuterFunction ()V Main.java
+.....>> main Main callLeafFunction ()V Main.java
+.....<< main Main callLeafFunction ()V Main.java
+....<< main Main callOuterFunction ()V Main.java
....>> main Main callLeafFunction ()V Main.java
....<< main Main callLeafFunction ()V Main.java
-...<< main Main callOuterFunction ()V Main.java
-...>> main Main callLeafFunction ()V Main.java
-...<< main Main callLeafFunction ()V Main.java
-..<< main Main $noinline$doSomeWork ()V Main.java
-..>> main Main doSomeWorkThrow ()V Main.java
-...>> main Main callThrowFunction ()V Main.java
-....>> main java.lang.Exception <init> (Ljava/lang/String;)V Exception.java
-.....>> main java.lang.Throwable <init> (Ljava/lang/String;)V Throwable.java
-......>> main java.lang.Object <init> ()V Object.java
-......<< main java.lang.Object <init> ()V Object.java
-......>> main java.util.Collections emptyList ()Ljava/util/List; Collections.java
-......<< main java.util.Collections emptyList ()Ljava/util/List; Collections.java
-......>> main java.lang.Throwable fillInStackTrace ()Ljava/lang/Throwable; Throwable.java
-.......>> main java.lang.Throwable nativeFillInStackTrace ()Ljava/lang/Object; Throwable.java
-.......<< main java.lang.Throwable nativeFillInStackTrace ()Ljava/lang/Object; Throwable.java
-......<< main java.lang.Throwable fillInStackTrace ()Ljava/lang/Throwable; Throwable.java
-.....<< main java.lang.Throwable <init> (Ljava/lang/String;)V Throwable.java
-....<< main java.lang.Exception <init> (Ljava/lang/String;)V Exception.java
-...<<E main Main callThrowFunction ()V Main.java
-..<< main Main doSomeWorkThrow ()V Main.java
-..>> main Main$VMDebug $noinline$stopMethodTracing ()V Main.java
+...<< main Main $noinline$doSomeWork ()V Main.java
+...>> main Main doSomeWorkThrow ()V Main.java
+....>> main Main callThrowFunction ()V Main.java
+.....>> main java.lang.Exception <init> (Ljava/lang/String;)V Exception.java
+......>> main java.lang.Throwable <init> (Ljava/lang/String;)V Throwable.java
+.......>> main java.lang.Object <init> ()V Object.java
+.......<< main java.lang.Object <init> ()V Object.java
+.......>> main java.util.Collections emptyList ()Ljava/util/List; Collections.java
+.......<< main java.util.Collections emptyList ()Ljava/util/List; Collections.java
+.......>> main java.lang.Throwable fillInStackTrace ()Ljava/lang/Throwable; Throwable.java
+........>> main java.lang.Throwable nativeFillInStackTrace ()Ljava/lang/Object; Throwable.java
+........<< main java.lang.Throwable nativeFillInStackTrace ()Ljava/lang/Object; Throwable.java
+.......<< main java.lang.Throwable fillInStackTrace ()Ljava/lang/Throwable; Throwable.java
+......<< main java.lang.Throwable <init> (Ljava/lang/String;)V Throwable.java
+.....<< main java.lang.Exception <init> (Ljava/lang/String;)V Exception.java
+....<<E main Main callThrowFunction ()V Main.java
+...<< main Main doSomeWorkThrow ()V Main.java
+...>> main Main$VMDebug $noinline$stopMethodTracing ()V Main.java
+***** non streaming test *******
+.>> main Main main ([Ljava/lang/String;)V Main.java
+..>> main Main testTracing (ZLBaseTraceParser;I)V Main.java
+...>> main Main$VMDebug startMethodTracing (Ljava/lang/String;Ljava/io/FileDescriptor;IIZIZ)V Main.java
+....>> main java.lang.reflect.Method invoke (Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; Method.java
+.....>> main dalvik.system.VMDebug startMethodTracing (Ljava/lang/String;Ljava/io/FileDescriptor;IIZIZ)V VMDebug.java
+......>> main dalvik.system.VMDebug startMethodTracingFd (Ljava/lang/String;IIIZIZ)V VMDebug.java
+......<< main dalvik.system.VMDebug startMethodTracingFd (Ljava/lang/String;IIIZIZ)V VMDebug.java
+.....<< main dalvik.system.VMDebug startMethodTracing (Ljava/lang/String;Ljava/io/FileDescriptor;IIZIZ)V VMDebug.java
+....<< main java.lang.reflect.Method invoke (Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; Method.java
+...<< main Main$VMDebug startMethodTracing (Ljava/lang/String;Ljava/io/FileDescriptor;IIZIZ)V Main.java
+...>> main java.lang.Thread start ()V Thread.java
+....>> main java.lang.ThreadGroup add (Ljava/lang/Thread;)V ThreadGroup.java
+....<< main java.lang.ThreadGroup add (Ljava/lang/Thread;)V ThreadGroup.java
+....>> main java.lang.Thread nativeCreate (Ljava/lang/Thread;JZ)V Thread.java
+....<< main java.lang.Thread nativeCreate (Ljava/lang/Thread;JZ)V Thread.java
+...<< main java.lang.Thread start ()V Thread.java
+...>> main java.lang.Thread join ()V Thread.java
+....>> main java.lang.Thread join (J)V Thread.java
+.....>> main java.lang.System currentTimeMillis ()J System.java
+.....<< main java.lang.System currentTimeMillis ()J System.java
+.....>> main java.lang.Thread isAlive ()Z Thread.java
+.....<< main java.lang.Thread isAlive ()Z Thread.java
+.....>> main java.lang.Object wait (J)V Object.java
+......>> main java.lang.Object wait (JI)V Object.java
+.>> TestThread2246 java.lang.Thread run ()V Thread.java
+..>> TestThread2246 Main$$ExternalSyntheticLambda0 run ()V D8$$SyntheticClass
+...>> TestThread2246 Main lambda$testTracing$0 ()V Main.java
+....>> TestThread2246 Main <init> ()V Main.java
+.....>> TestThread2246 java.lang.Object <init> ()V Object.java
+.....<< TestThread2246 java.lang.Object <init> ()V Object.java
+....<< TestThread2246 Main <init> ()V Main.java
+....>> TestThread2246 Main $noinline$doSomeWork ()V Main.java
+.....>> TestThread2246 Main callOuterFunction ()V Main.java
+......>> TestThread2246 Main callLeafFunction ()V Main.java
+......<< TestThread2246 Main callLeafFunction ()V Main.java
+.....<< TestThread2246 Main callOuterFunction ()V Main.java
+.....>> TestThread2246 Main callLeafFunction ()V Main.java
+.....<< TestThread2246 Main callLeafFunction ()V Main.java
+....<< TestThread2246 Main $noinline$doSomeWork ()V Main.java
+...<< TestThread2246 Main lambda$testTracing$0 ()V Main.java
+..<< TestThread2246 Main$$ExternalSyntheticLambda0 run ()V D8$$SyntheticClass
+.<< TestThread2246 java.lang.Thread run ()V Thread.java
+.>> TestThread2246 java.lang.ThreadGroup threadTerminated (Ljava/lang/Thread;)V ThreadGroup.java
+..>> TestThread2246 java.lang.ThreadGroup remove (Ljava/lang/Thread;)V ThreadGroup.java
+...>> TestThread2246 java.lang.System arraycopy (Ljava/lang/Object;ILjava/lang/Object;II)V System.java
+...<< TestThread2246 java.lang.System arraycopy (Ljava/lang/Object;ILjava/lang/Object;II)V System.java
+..<< TestThread2246 java.lang.ThreadGroup remove (Ljava/lang/Thread;)V ThreadGroup.java
+.<< TestThread2246 java.lang.ThreadGroup threadTerminated (Ljava/lang/Thread;)V ThreadGroup.java
+......<< main java.lang.Object wait (JI)V Object.java
+.....<< main java.lang.Object wait (J)V Object.java
+.....>> main java.lang.Thread isAlive ()Z Thread.java
+.....<< main java.lang.Thread isAlive ()Z Thread.java
+....<< main java.lang.Thread join (J)V Thread.java
+...<< main java.lang.Thread join ()V Thread.java
+...>> main Main $noinline$doSomeWork ()V Main.java
+....>> main Main callOuterFunction ()V Main.java
+.....>> main Main callLeafFunction ()V Main.java
+.....<< main Main callLeafFunction ()V Main.java
+....<< main Main callOuterFunction ()V Main.java
+....>> main Main callLeafFunction ()V Main.java
+....<< main Main callLeafFunction ()V Main.java
+...<< main Main $noinline$doSomeWork ()V Main.java
+...>> main Main doSomeWorkThrow ()V Main.java
+....>> main Main callThrowFunction ()V Main.java
+.....>> main java.lang.Exception <init> (Ljava/lang/String;)V Exception.java
+......>> main java.lang.Throwable <init> (Ljava/lang/String;)V Throwable.java
+.......>> main java.lang.Object <init> ()V Object.java
+.......<< main java.lang.Object <init> ()V Object.java
+.......>> main java.util.Collections emptyList ()Ljava/util/List; Collections.java
+.......<< main java.util.Collections emptyList ()Ljava/util/List; Collections.java
+.......>> main java.lang.Throwable fillInStackTrace ()Ljava/lang/Throwable; Throwable.java
+........>> main java.lang.Throwable nativeFillInStackTrace ()Ljava/lang/Object; Throwable.java
+........<< main java.lang.Throwable nativeFillInStackTrace ()Ljava/lang/Object; Throwable.java
+.......<< main java.lang.Throwable fillInStackTrace ()Ljava/lang/Throwable; Throwable.java
+......<< main java.lang.Throwable <init> (Ljava/lang/String;)V Throwable.java
+.....<< main java.lang.Exception <init> (Ljava/lang/String;)V Exception.java
+....<<E main Main callThrowFunction ()V Main.java
+...<< main Main doSomeWorkThrow ()V Main.java
+...>> main Main$VMDebug $noinline$stopMethodTracing ()V Main.java
+....>> main java.lang.reflect.Method invoke (Ljava/lang/Object;[Ljava/lang/Object;)Ljava/lang/Object; Method.java
+.....>> main dalvik.system.VMDebug stopMethodTracing ()V VMDebug.java
diff --git a/test/2246-trace-stream/src/BaseTraceParser.java b/test/2246-trace-stream/src/BaseTraceParser.java
new file mode 100644
index 0000000..6c739d3
--- /dev/null
+++ b/test/2246-trace-stream/src/BaseTraceParser.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.DataInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+
+abstract class BaseTraceParser {
+ public static final int MAGIC_NUMBER = 0x574f4c53;
+ public static final int DUAL_CLOCK_VERSION = 3;
+ public static final int STREAMING_DUAL_CLOCK_VERSION = 0xF3;
+ public static final String START_SECTION_ID = "*";
+ public static final String METHODS_SECTION_ID = "*methods";
+ public static final String THREADS_SECTION_ID = "*threads";
+ public static final String END_SECTION_ID = "*end";
+
+ public void InitializeParser(File file) throws IOException {
+ dataStream = new DataInputStream(new FileInputStream(file));
+ methodIdMap = new HashMap<Integer, String>();
+ threadIdMap = new HashMap<Integer, String>();
+ nestingLevelMap = new HashMap<Integer, Integer>();
+ }
+
+ public void closeFile() throws IOException {
+ dataStream.close();
+ }
+
+ public String readString(int numBytes) throws IOException {
+ byte[] buffer = new byte[numBytes];
+ dataStream.readFully(buffer);
+ return new String(buffer, StandardCharsets.UTF_8);
+ }
+
+ public String readLine() throws IOException {
+ StringBuilder sb = new StringBuilder();
+ char lineSeparator = '\n';
+ char c = (char)dataStream.readUnsignedByte();
+ while ( c != lineSeparator) {
+ sb.append(c);
+ c = (char)dataStream.readUnsignedByte();
+ }
+ return sb.toString();
+ }
+
+ public int readNumber(int numBytes) throws IOException {
+ int number = 0;
+ for (int i = 0; i < numBytes; i++) {
+ number += dataStream.readUnsignedByte() << (i * 8);
+ }
+ return number;
+ }
+
+ public void validateTraceHeader(int expectedVersion) throws Exception {
+ // Read 4-byte magicNumber.
+ int magicNumber = readNumber(4);
+ if (magicNumber != MAGIC_NUMBER) {
+ throw new Exception("Magic number doesn't match. Expected "
+ + Integer.toHexString(MAGIC_NUMBER) + " Got "
+ + Integer.toHexString(magicNumber));
+ }
+ // Read 2-byte version.
+ int version = readNumber(2);
+ if (version != expectedVersion) {
+ throw new Exception(
+ "Unexpected version. Expected " + expectedVersion + " Got " + version);
+ }
+ traceFormatVersion = version & 0xF;
+ // Read 2-byte headerLength length.
+ int headerLength = readNumber(2);
+ // Read 8-byte starting time - Ignore timestamps since they are not deterministic.
+ dataStream.skipBytes(8);
+ // 4 byte magicNumber + 2 byte version + 2 byte offset + 8 byte timestamp.
+ int numBytesRead = 16;
+ if (version >= DUAL_CLOCK_VERSION) {
+ // Read 2-byte record size.
+ // TODO(mythria): Check why this is needed. We can derive recordSize from version. Not
+ // sure why this is needed.
+ recordSize = readNumber(2);
+ numBytesRead += 2;
+ }
+ // Skip any padding.
+ if (headerLength > numBytesRead) {
+ dataStream.skipBytes(headerLength - numBytesRead);
+ }
+ }
+
+ public int GetEntryHeader() throws IOException {
+ // Read 2-byte thread-id. On host thread-ids can be greater than 16-bit.
+ int threadId = readNumber(2);
+ if (threadId != 0) {
+ return threadId;
+ }
+ // Read 1-byte header type
+ return readNumber(1);
+ }
+
+ public void ProcessMethodInfoEntry() throws IOException {
+ // Read 2-byte method info size
+ int headerLength = readNumber(2);
+ // Read header size data.
+ String methodInfo = readString(headerLength);
+ String[] tokens = methodInfo.split("\t", 2);
+ // Get methodId and record methodId -> methodName map.
+ int methodId = Integer.decode(tokens[0]);
+ String methodLine = tokens[1].replace('\t', ' ');
+ methodLine = methodLine.substring(0, methodLine.length() - 1);
+ methodIdMap.put(methodId, methodLine);
+ }
+
+ public void ProcessThreadInfoEntry() throws IOException {
+ // Read 2-byte thread id
+ int threadId = readNumber(2);
+ // Read 2-byte thread info size
+ int headerLength = readNumber(2);
+ // Read header size data.
+ String threadInfo = readString(headerLength);
+ threadIdMap.put(threadId, threadInfo);
+ }
+
+ public boolean ShouldIgnoreThread(int threadId) throws Exception {
+ if (threadIdMap.get(threadId).contains("Daemon")) {
+ return true;
+ }
+ return false;
+ }
+
+ public String eventTypeToString(int eventType, int threadId) {
+ if (!nestingLevelMap.containsKey(threadId)) {
+ nestingLevelMap.put(threadId, 0);
+ }
+
+ int nestingLevel = nestingLevelMap.get(threadId);
+ String str = "";
+ for (int i = 0; i < nestingLevel; i++) {
+ str += ".";
+ }
+ switch (eventType) {
+ case 0:
+ nestingLevel++;
+ str += ".>>";
+ break;
+ case 1:
+ nestingLevel--;
+ str += "<<";
+ break;
+ case 2:
+ nestingLevel--;
+ str += "<<E";
+ break;
+ default:
+ str += "??";
+ }
+ nestingLevelMap.put(threadId, nestingLevel);
+ return str;
+ }
+
+ public String ProcessEventEntry(int threadId) throws IOException {
+ // Read 4-byte method value
+ int methodAndEvent = readNumber(4);
+ int methodId = methodAndEvent & ~0x3;
+ int eventType = methodAndEvent & 0x3;
+
+ String str = eventTypeToString(eventType, threadId) + " " + threadIdMap.get(threadId)
+ + " " + methodIdMap.get(methodId);
+ // Depending on the version skip either one or two timestamps.
+ // TODO(mythria): Probably add a check that time stamps are always greater than initial
+ // timestamp.
+ int numBytesTimestamp = (traceFormatVersion == 2) ? 4 : 8;
+ dataStream.skipBytes(numBytesTimestamp);
+ return str;
+ }
+
+ public abstract void CheckTraceFileFormat(File traceFile, int expectedVersion)
+ throws Exception;
+
+ DataInputStream dataStream;
+ HashMap<Integer, String> methodIdMap;
+ HashMap<Integer, String> threadIdMap;
+ HashMap<Integer, Integer> nestingLevelMap;
+ int recordSize = 0;
+ int traceFormatVersion = 0;
+}
diff --git a/test/2246-trace-stream/src/Main.java b/test/2246-trace-stream/src/Main.java
index 37870f1..daada7d 100644
--- a/test/2246-trace-stream/src/Main.java
+++ b/test/2246-trace-stream/src/Main.java
@@ -31,6 +31,18 @@
System.out.println("This test is not supported on " + name);
return;
}
+ System.out.println("***** streaming test *******");
+ StreamTraceParser stream_parser = new StreamTraceParser();
+ testTracing(
+ /* streaming=*/true, stream_parser, BaseTraceParser.STREAMING_DUAL_CLOCK_VERSION);
+
+ System.out.println("***** non streaming test *******");
+ NonStreamTraceParser non_stream_parser = new NonStreamTraceParser();
+ testTracing(/* streaming=*/false, non_stream_parser, BaseTraceParser.DUAL_CLOCK_VERSION);
+ }
+
+ public static void testTracing(boolean streaming, BaseTraceParser parser, int expected_version)
+ throws Exception {
file = createTempFile();
FileOutputStream out_file = new FileOutputStream(file);
Main m = new Main();
@@ -43,14 +55,14 @@
VMDebug.$noinline$stopMethodTracing();
}
- VMDebug.startMethodTracing(file.getPath(), out_file.getFD(), 0, 0, false, 0, true);
+ VMDebug.startMethodTracing(file.getPath(), out_file.getFD(), 0, 0, false, 0, streaming);
t.start();
t.join();
m.$noinline$doSomeWork();
m.doSomeWorkThrow();
VMDebug.$noinline$stopMethodTracing();
out_file.close();
- m.CheckTraceFileFormat(file);
+ parser.CheckTraceFileFormat(file, expected_version);
} finally {
if (out_file != null) {
out_file.close();
@@ -58,39 +70,6 @@
}
}
- private void CheckTraceFileFormat(File trace_file) throws Exception {
- StreamTraceParser parser = new StreamTraceParser(trace_file);
- parser.validateTraceHeader(StreamTraceParser.TRACE_VERSION_DUAL_CLOCK);
- boolean has_entries = true;
- boolean seen_stop_tracing_method = false;
- while (has_entries) {
- int header_type = parser.GetEntryHeader();
- switch (header_type) {
- case 1:
- parser.ProcessMethodInfoEntry();
- break;
- case 2:
- parser.ProcessThreadInfoEntry();
- break;
- case 3:
- // TODO(mythria): Add test to also check format of trace summary.
- has_entries = false;
- break;
- default:
- String event_string = parser.ProcessEventEntry(header_type);
- // Ignore events after method tracing was stopped. The code that is executed
- // later could be non-deterministic.
- if (!seen_stop_tracing_method) {
- System.out.println(event_string);
- }
- if (event_string.contains("Main$VMDebug $noinline$stopMethodTracing")) {
- seen_stop_tracing_method = true;
- }
- }
- }
- parser.closeFile();
- }
-
private static File createTempFile() throws Exception {
try {
return File.createTempFile(TEMP_FILE_NAME_PREFIX, TEMP_FILE_NAME_SUFFIX);
diff --git a/test/2246-trace-stream/src/NonStreamTraceParser.java b/test/2246-trace-stream/src/NonStreamTraceParser.java
new file mode 100644
index 0000000..3219591
--- /dev/null
+++ b/test/2246-trace-stream/src/NonStreamTraceParser.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.File;
+import java.io.IOException;
+
+public class NonStreamTraceParser extends BaseTraceParser {
+
+ public void CheckTraceFileFormat(File file, int expectedVersion) throws Exception {
+ InitializeParser(file);
+
+ // On non-streaming formats, the file starts with information about options and threads and
+ // method information.
+ // Read version string and version.
+ String line = readLine();
+ if (!line.equals("*version")) {
+ throw new Exception("Trace doesn't start with version. Starts with: " + line);
+ }
+ int version = Integer.decode(readLine());
+ if (version != expectedVersion) {
+ throw new Exception("Unexpected version: " + version);
+ }
+
+ // Record numEntries and ignore next few options that provides some metadata.
+ line = readLine();
+ int numEntries = 0;
+ while (!line.startsWith(START_SECTION_ID)) {
+ if (line.startsWith("num-method-calls")) {
+ String[] tokens = line.split("=");
+ numEntries = Integer.decode(tokens[1]);
+ }
+ line = readLine();
+ }
+
+ // This should be threads.
+ if (!line.equals(THREADS_SECTION_ID)) {
+ throw new Exception("Missing information about threads " + line);
+ }
+
+ line = readLine();
+ while (!line.startsWith(START_SECTION_ID)) {
+ String[] threadInfo = line.split("\t");
+ threadIdMap.put(Integer.decode(threadInfo[0]), threadInfo[1]);
+ line = readLine();
+ }
+
+ // Parse methods
+ if (!line.equals(METHODS_SECTION_ID)) {
+ throw new Exception("Missing information about methods " + line);
+ }
+
+ line = readLine();
+ while (!line.startsWith(START_SECTION_ID)) {
+ String[] methodInfo = line.split("\t", 2);
+ methodIdMap.put(Integer.decode(methodInfo[0]), methodInfo[1]);
+ line = readLine();
+ }
+
+ // This should be end
+ if (!line.equals(END_SECTION_ID)) {
+ throw new Exception("Missing end after methods " + line);
+ }
+
+ // Validate the actual data.
+ validateTraceHeader(expectedVersion);
+ boolean hasEntries = true;
+ boolean seenStopTracingMethod = false;
+ for (int i = 0; i < numEntries; i++) {
+ int threadId = GetEntryHeader();
+ String eventString = ProcessEventEntry(threadId);
+ // Ignore daemons (ex: heap task daemon, reference queue daemon) because they may not
+ // be deterministic.
+ if (ShouldIgnoreThread(threadId)) {
+ continue;
+ }
+ // Ignore events after method tracing was stopped. The code that is executed
+ // later could be non-deterministic.
+ if (!seenStopTracingMethod) {
+ System.out.println(eventString);
+ }
+ if (eventString.contains("Main$VMDebug $noinline$stopMethodTracing")) {
+ seenStopTracingMethod = true;
+ }
+ }
+ closeFile();
+ }
+}
diff --git a/test/2246-trace-stream/src/StreamTraceParser.java b/test/2246-trace-stream/src/StreamTraceParser.java
index 8995342..32f86e3 100644
--- a/test/2246-trace-stream/src/StreamTraceParser.java
+++ b/test/2246-trace-stream/src/StreamTraceParser.java
@@ -14,153 +14,45 @@
* limitations under the License.
*/
-import java.io.DataInputStream;
import java.io.File;
-import java.io.FileInputStream;
import java.io.IOException;
-import java.nio.charset.StandardCharsets;
-import java.util.HashMap;
-public class StreamTraceParser {
- public static final int MAGIC_NUMBER = 0x574f4c53;
- public static final int DUAL_CLOCK_VERSION = 3;
- public static final int TRACE_VERSION_DUAL_CLOCK = 0xF3;
+public class StreamTraceParser extends BaseTraceParser {
- public StreamTraceParser(File file) throws IOException {
- dataStream = new DataInputStream(new FileInputStream(file));
- method_id_map = new HashMap<Integer, String>();
- thread_id_map = new HashMap<Integer, String>();
- }
+ public void CheckTraceFileFormat(File file, int expectedVersion) throws Exception {
+ InitializeParser(file);
- public void closeFile() throws IOException {
- dataStream.close();
- }
-
- public String readString(int num_bytes) throws IOException {
- byte[] buffer = new byte[num_bytes];
- dataStream.readFully(buffer);
- return new String(buffer, StandardCharsets.UTF_8);
- }
-
- public int readNumber(int num_bytes) throws IOException {
- int number = 0;
- for (int i = 0; i < num_bytes; i++) {
- number += dataStream.readUnsignedByte() << (i * 8);
+ validateTraceHeader(expectedVersion);
+ boolean hasEntries = true;
+ boolean seenStopTracingMethod = false;
+ while (hasEntries) {
+ int headerType = GetEntryHeader();
+ switch (headerType) {
+ case 1:
+ ProcessMethodInfoEntry();
+ break;
+ case 2:
+ ProcessThreadInfoEntry();
+ break;
+ case 3:
+ // TODO(mythria): Add test to also check format of trace summary.
+ hasEntries = false;
+ break;
+ default:
+ String eventString = ProcessEventEntry(headerType);
+ if (ShouldIgnoreThread(headerType)) {
+ break;
+ }
+ // Ignore events after method tracing was stopped. The code that is executed
+ // later could be non-deterministic.
+ if (!seenStopTracingMethod) {
+ System.out.println(eventString);
+ }
+ if (eventString.contains("Main$VMDebug $noinline$stopMethodTracing")) {
+ seenStopTracingMethod = true;
+ }
+ }
}
- return number;
+ closeFile();
}
-
- public void validateTraceHeader(int expected_version) throws Exception {
- // Read 4-byte magic_number
- int magic_number = readNumber(4);
- if (magic_number != MAGIC_NUMBER) {
- throw new Exception("Magic number doesn't match. Expected "
- + Integer.toHexString(MAGIC_NUMBER) + " Got "
- + Integer.toHexString(magic_number));
- }
- // Read 2-byte version
- int version = readNumber(2);
- if (version != expected_version) {
- throw new Exception(
- "Unexpected version. Expected " + expected_version + " Got " + version);
- }
- trace_format_version = version & 0xF;
- // Read 2-byte header_length length
- int header_length = readNumber(2);
- // Read 8-byte starting time - Ignore timestamps since they are not deterministic
- dataStream.skipBytes(8);
- // 4 byte magic_number + 2 byte version + 2 byte offset + 8 byte timestamp
- int num_bytes_read = 16;
- if (version >= DUAL_CLOCK_VERSION) {
- // Read 2-byte record size.
- // TODO(mythria): Check why this is needed. We can derive record_size from version. Not
- // sure why this is needed.
- record_size = readNumber(2);
- num_bytes_read += 2;
- }
- // Skip any padding
- if (header_length > num_bytes_read) {
- dataStream.skipBytes(header_length - num_bytes_read);
- }
- }
-
- public int GetEntryHeader() throws IOException {
- // Read 2-byte thread-id. On host thread-ids can be greater than 16-bit.
- int thread_id = readNumber(2);
- if (thread_id != 0) {
- return thread_id;
- }
- // Read 1-byte header type
- return readNumber(1);
- }
-
- public void ProcessMethodInfoEntry() throws IOException {
- // Read 2-byte method info size
- int header_length = readNumber(2);
- // Read header_size data.
- String method_info = readString(header_length);
- String[] tokens = method_info.split("\t", 2);
- // Get method_id and record method_id -> method_name map.
- int method_id = Integer.decode(tokens[0]);
- String method_line = tokens[1].replace('\t', ' ');
- method_line = method_line.substring(0, method_line.length() - 1);
- method_id_map.put(method_id, method_line);
- }
-
- public void ProcessThreadInfoEntry() throws IOException {
- // Read 2-byte thread id
- int thread_id = readNumber(2);
- // Read 2-byte thread info size
- int header_length = readNumber(2);
- // Read header_size data.
- String thread_info = readString(header_length);
- thread_id_map.put(thread_id, thread_info);
- }
-
- public String eventTypeToString(int event_type) {
- String str = "";
- for (int i = 0; i < nesting_level; i++) {
- str += ".";
- }
- switch (event_type) {
- case 0:
- nesting_level++;
- str += ".>>";
- break;
- case 1:
- nesting_level--;
- str += "<<";
- break;
- case 2:
- nesting_level--;
- str += "<<E";
- break;
- default:
- str += "??";
- }
- return str;
- }
-
- public String ProcessEventEntry(int thread_id) throws IOException {
- // Read 4-byte method value
- int method_and_event = readNumber(4);
- int method_id = method_and_event & ~0x3;
- int event_type = method_and_event & 0x3;
-
- String str = eventTypeToString(event_type) + " " + thread_id_map.get(thread_id) + " "
- + method_id_map.get(method_id);
- // Depending on the version skip either one or two timestamps.
- // TODO(mythria): Probably add a check that time stamps are always greater than initial
- // timestamp.
- int num_bytes_timestamp = (trace_format_version == 2) ? 4 : 8;
- dataStream.skipBytes(num_bytes_timestamp);
- return str;
- }
-
- DataInputStream dataStream;
- HashMap<Integer, String> method_id_map;
- HashMap<Integer, String> thread_id_map;
- int record_size = 0;
- int trace_format_version = 0;
- int nesting_level = 0;
}