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;
 }