diff options
| -rw-r--r-- | runtime/trace.cc | 15 | ||||
| -rw-r--r-- | test/2246-trace-stream/expected-stdout.txt | 190 | ||||
| -rw-r--r-- | test/2246-trace-stream/src/BaseTraceParser.java | 198 | ||||
| -rw-r--r-- | test/2246-trace-stream/src/Main.java | 49 | ||||
| -rw-r--r-- | test/2246-trace-stream/src/NonStreamTraceParser.java | 100 | ||||
| -rw-r--r-- | test/2246-trace-stream/src/StreamTraceParser.java | 182 |
6 files changed, 499 insertions, 235 deletions
diff --git a/runtime/trace.cc b/runtime/trace.cc index e2a95f1ed5..051a600650 100644 --- a/runtime/trace.cc +++ b/runtime/trace.cc @@ -790,6 +790,10 @@ void Trace::DumpBuf(uint8_t* buf, size_t buf_size, TraceClockSource clock_source 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 @@ static void DumpThread(Thread* t, void* arg) { 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 c9d7405c8b..90706b50fc 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 0000000000..6c739d3a2b --- /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 37870f14c1..daada7d1b1 100644 --- a/test/2246-trace-stream/src/Main.java +++ b/test/2246-trace-stream/src/Main.java @@ -31,6 +31,18 @@ public class Main { 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 @@ public class Main { 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 @@ public class Main { } } - 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 0000000000..3219591d3f --- /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 8995342729..32f86e38e4 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 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 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); - } - return number; +public class StreamTraceParser extends BaseTraceParser { + + public void CheckTraceFileFormat(File file, int expectedVersion) throws Exception { + InitializeParser(file); + + 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; + } + } + } + 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; } |