• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package art;
18 
19 import org.apache.harmony.dalvik.ddmc.*;
20 import dalvik.system.VMDebug;
21 
22 import java.lang.reflect.Method;
23 import java.util.Arrays;
24 import java.util.function.*;
25 import java.util.zip.Adler32;
26 import java.nio.*;
27 
28 public class Test1940 {
29   public static final int DDMS_TYPE_INDEX = 0;
30   public static final int DDMS_LEN_INDEX = 4;
31   public static final int DDMS_HEADER_LENGTH = 8;
32   public static final int MY_DDMS_TYPE = 0xDEADBEEF;
33   public static final int MY_DDMS_RESPONSE_TYPE = 0xFADE7357;
34   public static final int MY_EMPTY_DDMS_TYPE = 0xABCDEF01;
35   public static final int MY_INVALID_DDMS_TYPE = 0x12345678;
36 
37   public static final boolean PRINT_ALL_CHUNKS = false;
38 
39   public static interface DdmHandler {
HandleChunk(int type, byte[] data)40     public void HandleChunk(int type, byte[] data) throws Exception;
41   }
42 
43   public static final class TestError extends Error {
TestError(String s)44     public TestError(String s) { super(s); }
45   }
46 
checkEq(Object a, Object b)47   private static void checkEq(Object a, Object b) {
48     if (!a.equals(b)) {
49       throw new TestError("Failure: " + a + " != " + b);
50     }
51   }
52 
chunkEq(Chunk a, Chunk b)53   private static boolean chunkEq(Chunk a, Chunk b) {
54     return a.type == b.type &&
55            a.offset == b.offset &&
56            a.length == b.length &&
57            Arrays.equals(a.data, b.data);
58   }
59 
printChunk(Chunk k)60   private static String printChunk(Chunk k) {
61     byte[] out = new byte[k.length];
62     System.arraycopy(k.data, k.offset, out, 0, k.length);
63     return String.format("Chunk(Type: 0x%X, Len: %d, data: %s)",
64         k.type, k.length, Arrays.toString(out));
65   }
66 
67   private static final class MyDdmHandler extends ChunkHandler {
onConnected()68     public void onConnected() {}
onDisconnected()69     public void onDisconnected() {}
handleChunk(Chunk req)70     public Chunk handleChunk(Chunk req) {
71       System.out.println("MyDdmHandler: Chunk received: " + printChunk(req));
72       if (req.type == MY_DDMS_TYPE) {
73         // For this test we will simply calculate the checksum
74         ByteBuffer b = ByteBuffer.wrap(new byte[8]);
75         Adler32 a = new Adler32();
76         a.update(req.data, req.offset, req.length);
77         b.order(ByteOrder.BIG_ENDIAN);
78         long val = a.getValue();
79         b.putLong(val);
80         System.out.printf("MyDdmHandler: Putting value 0x%X\n", val);
81         Chunk ret = new Chunk(MY_DDMS_RESPONSE_TYPE, b.array(), 0, 8);
82         System.out.println("MyDdmHandler: Chunk returned: " + printChunk(ret));
83         return ret;
84       } else if (req.type == MY_EMPTY_DDMS_TYPE) {
85         return new Chunk(MY_DDMS_RESPONSE_TYPE, new byte[0], 0, 0);
86       } else if (req.type == MY_INVALID_DDMS_TYPE) {
87         // This is a very invalid chunk.
88         return new Chunk(MY_DDMS_RESPONSE_TYPE, new byte[] { 0 }, /*offset*/ 12, /*length*/ 55);
89       } else {
90         throw new TestError("Unknown ddm request type: " + req.type);
91       }
92     }
93   }
94 
95   public static final ChunkHandler SINGLE_HANDLER = new MyDdmHandler();
96 
97   public static DdmHandler CURRENT_HANDLER;
98 
HandlePublish(int type, byte[] data)99   public static void HandlePublish(int type, byte[] data) throws Exception {
100     if (PRINT_ALL_CHUNKS) {
101       System.out.println(
102           "Unknown Chunk published: " + printChunk(new Chunk(type, data, 0, data.length)));
103     }
104     CURRENT_HANDLER.HandleChunk(type, data);
105   }
106 
107   // TYPE Thread Create
108   public static final int TYPE_THCR = 0x54484352;
109   // Type Thread name
110   public static final int TYPE_THNM = 0x54484E4D;
111   // Type Thread death.
112   public static final int TYPE_THDE = 0x54484445;
113   // Type Heap info
114   public static final int TYPE_HPIF = 0x48504946;
115   // Type Trace Results
116   public static final int TYPE_MPSE = 0x4D505345;
117 
IsFromThread(Thread t, byte[] data)118   public static boolean IsFromThread(Thread t, byte[] data) {
119     // DDMS always puts the thread-id as the first 4 bytes.
120     ByteBuffer b = ByteBuffer.wrap(data);
121     b.order(ByteOrder.BIG_ENDIAN);
122     return b.getInt() == (int) t.getId();
123   }
124 
125   public static final class AwaitChunkHandler implements DdmHandler {
126     public final Predicate<Chunk> needle;
127     public final DdmHandler chain;
128     private boolean found = false;
AwaitChunkHandler(Predicate<Chunk> needle, DdmHandler chain)129     public AwaitChunkHandler(Predicate<Chunk> needle, DdmHandler chain) {
130       this.needle = needle;
131       this.chain = chain;
132     }
HandleChunk(int type, byte[] data)133     public void HandleChunk(int type, byte[] data) throws Exception {
134       chain.HandleChunk(type, data);
135       Chunk c = new Chunk(type, data, 0, data.length);
136       if (needle.test(c)) {
137         synchronized (this) {
138           found = true;
139           notifyAll();
140         }
141       }
142     }
Await()143     public synchronized void Await() throws Exception {
144       while (!found) {
145         wait();
146       }
147     }
148   }
149 
run()150   public static void run() throws Exception {
151     DdmHandler BaseHandler = (type, data) -> {
152       System.out.println("Chunk published: " + printChunk(new Chunk(type, data, 0, data.length)));
153     };
154     CURRENT_HANDLER = BaseHandler;
155     initializeTest();
156     Method publish = Test1940.class.getDeclaredMethod("HandlePublish",
157                                                       Integer.TYPE,
158                                                       new byte[0].getClass());
159     Thread listener = new Thread(() -> { Test1940.publishListen(publish); });
160     listener.setDaemon(true);
161     listener.start();
162     // Test sending chunk directly.
163     DdmServer.registerHandler(MY_DDMS_TYPE, SINGLE_HANDLER);
164     DdmServer.registerHandler(MY_EMPTY_DDMS_TYPE, SINGLE_HANDLER);
165     DdmServer.registerHandler(MY_INVALID_DDMS_TYPE, SINGLE_HANDLER);
166     DdmServer.registrationComplete();
167     byte[] data = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
168     System.out.println("Sending data " + Arrays.toString(data));
169     Chunk res = processChunk(data);
170     System.out.println("JVMTI returned chunk: " + printChunk(res));
171 
172     // Test sending an empty chunk.
173     System.out.println("Sending empty data array");
174     res = processChunk(new byte[0]);
175     System.out.println("JVMTI returned chunk: " + printChunk(res));
176 
177     // Test sending chunk through DdmServer#sendChunk
178     Chunk c = new Chunk(
179         MY_DDMS_TYPE, new byte[] { 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 }, 0, 8);
180     AwaitChunkHandler h = new AwaitChunkHandler((x) -> chunkEq(c, x), CURRENT_HANDLER);
181     CURRENT_HANDLER = h;
182     System.out.println("Sending chunk: " + printChunk(c));
183     DdmServer.sendChunk(c);
184     h.Await();
185     CURRENT_HANDLER = BaseHandler;
186 
187     // Test getting back an empty chunk.
188     data = new byte[] { 0x1 };
189     System.out.println(
190         "Sending data " + Arrays.toString(data) + " to chunk handler " + MY_EMPTY_DDMS_TYPE);
191     res = processChunk(new Chunk(MY_EMPTY_DDMS_TYPE, data, 0, 1));
192     System.out.println("JVMTI returned chunk: " + printChunk(res));
193 
194     // Test getting back an invalid chunk.
195     System.out.println(
196         "Sending data " + Arrays.toString(data) + " to chunk handler " + MY_INVALID_DDMS_TYPE);
197     try {
198       res = processChunk(new Chunk(MY_INVALID_DDMS_TYPE, data, 0, 1));
199       System.out.println("JVMTI returned chunk: " + printChunk(res));
200     } catch (RuntimeException e) {
201       System.out.println("Got error: " + e.getMessage());
202     }
203 
204     // Test thread chunks are sent.
205     final boolean[] types_seen = new boolean[] { false, false, false };
206     AwaitChunkHandler wait_thd= new AwaitChunkHandler(
207       (x) -> types_seen[0] && types_seen[1] && types_seen[2],
208       (type, cdata) -> {
209         switch (type) {
210           case TYPE_THCR:
211             types_seen[0] = true;
212             break;
213           case TYPE_THNM:
214             types_seen[1] = true;
215             break;
216           case TYPE_THDE:
217             types_seen[2] = true;
218             break;
219           default:
220             // We don't want to print other types.
221             break;
222         }
223       });
224     CURRENT_HANDLER = wait_thd;
225     DdmVmInternal.setThreadNotifyEnabled(true);
226     System.out.println("threadNotify started!");
227     final Thread thr = new Thread(() -> { return; }, "THREAD");
228     thr.start();
229     System.out.println("Target thread started!");
230     thr.join();
231     System.out.println("Target thread finished!");
232     DdmVmInternal.setThreadNotifyEnabled(false);
233     System.out.println("threadNotify Disabled!");
234     wait_thd.Await();
235     // Make sure we saw at least one of Thread-create, Thread name, & thread death.
236     if (!types_seen[0] || !types_seen[1] || !types_seen[2]) {
237       System.out.println("Didn't see expected chunks for thread creation! got: " +
238           Arrays.toString(types_seen));
239     } else {
240       System.out.println("Saw expected thread events.");
241     }
242 
243     // method Tracing
244     AwaitChunkHandler mpse = new AwaitChunkHandler((x) -> x.type == TYPE_MPSE, (type, cdata) -> {
245       // This chunk includes timing and thread information so we just check the type.
246       if (type == TYPE_MPSE) {
247         System.out.println("Expected chunk type published: " + type);
248       }
249     });
250     CURRENT_HANDLER = mpse;
251     VMDebug.startMethodTracingDdms(/*size: default*/0,
252                                    /*flags: none*/ 0,
253                                    /*sampling*/ false,
254                                    /*interval*/ 0);
255     doNothing();
256     doNothing();
257     doNothing();
258     doNothing();
259     VMDebug.stopMethodTracing();
260     mpse.Await();
261   }
262 
doNothing()263   private static void doNothing() {}
processChunk(byte[] val)264   private static Chunk processChunk(byte[] val) {
265     return processChunk(new Chunk(MY_DDMS_TYPE, val, 0, val.length));
266   }
267 
initializeTest()268   private static native void initializeTest();
processChunk(Chunk val)269   private static native Chunk processChunk(Chunk val);
publishListen(Method publish)270   private static native void publishListen(Method publish);
271 }
272