• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  ** Copyright 2011, 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 com.android.ide.eclipse.gldebugger;
18 
19 import com.android.ide.eclipse.gldebugger.DebuggerMessage.Message;
20 import com.android.ide.eclipse.gldebugger.DebuggerMessage.Message.DataType;
21 import com.android.ide.eclipse.gldebugger.DebuggerMessage.Message.Function;
22 import com.android.ide.eclipse.gldebugger.DebuggerMessage.Message.Prop;
23 import com.android.sdklib.util.SparseArray;
24 import com.android.sdklib.util.SparseIntArray;
25 import com.google.protobuf.ByteString;
26 
27 import org.eclipse.jface.viewers.ISelectionChangedListener;
28 import org.eclipse.jface.viewers.ITreeContentProvider;
29 import org.eclipse.jface.viewers.LabelProvider;
30 import org.eclipse.jface.viewers.SelectionChangedEvent;
31 import org.eclipse.jface.viewers.StructuredSelection;
32 import org.eclipse.jface.viewers.Viewer;
33 import org.eclipse.swt.graphics.Image;
34 import org.eclipse.swt.widgets.Display;
35 
36 import java.io.FileNotFoundException;
37 import java.io.IOException;
38 import java.io.RandomAccessFile;
39 import java.lang.reflect.Array;
40 import java.lang.reflect.Field;
41 import java.nio.ByteBuffer;
42 import java.nio.ByteOrder;
43 import java.util.ArrayList;
44 import java.util.Collection;
45 import java.util.Map;
46 import java.util.Set;
47 
48 class Frame {
49     public final long filePosition;
50     private int callsCount;
51 
52     final Context startContext;
53     private ArrayList<MessageData> calls = new ArrayList<MessageData>();
54 
Frame(final Context context, final long filePosition)55     Frame(final Context context, final long filePosition) {
56         this.startContext = context.clone();
57         this.filePosition = filePosition;
58     }
59 
add(final MessageData msgData)60     void add(final MessageData msgData) {
61         calls.add(msgData);
62     }
63 
increaseCallsCount()64     void increaseCallsCount() {
65         callsCount++;
66     }
67 
computeContext(final MessageData call)68     Context computeContext(final MessageData call) {
69         Context ctx = startContext.clone();
70         for (int i = 0; i < calls.size(); i++)
71             if (call == calls.get(i))
72                 return ctx;
73             else
74                 ctx.processMessage(calls.get(i).msg);
75         assert false;
76         return ctx;
77     }
78 
size()79     int size() {
80         return callsCount;
81     }
82 
get(final int i)83     MessageData get(final int i) {
84         return calls.get(i);
85     }
86 
get()87     ArrayList<MessageData> get() {
88         return calls;
89     }
90 
unload()91     void unload() {
92         if (calls == null)
93             return;
94         calls.clear();
95         calls = null;
96     }
97 
load(final RandomAccessFile file)98     void load(final RandomAccessFile file) {
99         if (calls != null && calls.size() == callsCount)
100             return;
101         try {
102             Context ctx = startContext.clone();
103             calls = new ArrayList<MessageData>(callsCount);
104             final long oriPosition = file.getFilePointer();
105             file.seek(filePosition);
106             for (int i = 0; i < callsCount; i++) {
107                 int len = file.readInt();
108                 if (GLFramesView.TARGET_BYTE_ORDER == ByteOrder.LITTLE_ENDIAN)
109                     len = Integer.reverseBytes(len);
110                 final byte[] data = new byte[len];
111                 file.read(data);
112                 Message msg = Message.parseFrom(data);
113                 ctx.processMessage(msg);
114                 final MessageData msgData = new MessageData(Display.getCurrent(), msg, ctx);
115                 calls.add(msgData);
116             }
117             file.seek(oriPosition);
118         } catch (IOException e) {
119             e.printStackTrace();
120             assert false;
121         }
122     }
123 }
124 
125 class DebugContext {
126     boolean uiUpdate = false;
127     final int contextId;
128     Context currentContext;
129     private ArrayList<Frame> frames = new ArrayList<Frame>(128);
130     private Frame lastFrame;
131     private Frame loadedFrame;
132     private RandomAccessFile file;
133 
DebugContext(final int contextId)134     DebugContext(final int contextId) {
135         this.contextId = contextId;
136         currentContext = new Context(contextId);
137         try {
138             file = new RandomAccessFile("0x" + Integer.toHexString(contextId) +
139                     ".gles2dbg", "rw");
140         } catch (FileNotFoundException e) {
141             e.printStackTrace();
142             assert false;
143         }
144     }
145 
146     /** write message to file; if frame not null, then increase its call count */
saveMessage(final Message msg, final RandomAccessFile file, Frame frame)147     void saveMessage(final Message msg, final RandomAccessFile file, Frame frame) {
148         synchronized (file) {
149             if (frame != null)
150                 frame.increaseCallsCount();
151             final byte[] data = msg.toByteArray();
152             final ByteBuffer len = ByteBuffer.allocate(4);
153             len.order(GLFramesView.TARGET_BYTE_ORDER);
154             len.putInt(data.length);
155             try {
156                 if (GLFramesView.TARGET_BYTE_ORDER == ByteOrder.BIG_ENDIAN)
157                     file.writeInt(data.length);
158                 else
159                     file.writeInt(Integer.reverseBytes(data.length));
160                 file.write(data);
161             } catch (IOException e) {
162                 e.printStackTrace();
163                 assert false;
164             }
165         }
166     }
167 
168     /**
169      * Caches new Message, and formats into MessageData for current frame; this
170      * function is called exactly once for each new Message
171      */
processMessage(final Message newMsg)172     void processMessage(final Message newMsg) {
173         Message msg = newMsg;
174         if (msg.getFunction() == Function.SETPROP) {
175             // GL impl. consts should have been sent before any GL call messages
176             assert frames.size() == 0;
177             assert lastFrame == null;
178             assert msg.getProp() == Prop.GLConstant;
179             switch (GLEnum.valueOf(msg.getArg0())) {
180                 case GL_MAX_VERTEX_ATTRIBS:
181                     currentContext.serverVertex = new GLServerVertex(msg.getArg1());
182                     break;
183                 case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS:
184                     currentContext.serverTexture = new GLServerTexture(currentContext,
185                             msg.getArg1());
186                     break;
187                 default:
188                     assert false;
189                     return;
190             }
191             saveMessage(msg, file, null);
192             return;
193         }
194 
195         if (lastFrame == null) {
196             // first real message after the GL impl. consts
197             synchronized (file) {
198                 try {
199                     lastFrame = new Frame(currentContext, file.getFilePointer());
200                 } catch (IOException e) {
201                     e.printStackTrace();
202                     assert false;
203                 }
204             }
205             synchronized (frames) {
206                 frames.add(lastFrame);
207             }
208             assert loadedFrame == null;
209             loadedFrame = lastFrame;
210         }
211         currentContext.processMessage(msg);
212         if (msg.hasDataType() && msg.getDataType() == DataType.ReferencedImage) {
213             // decode referenced image so it doesn't rely on context later on
214             final byte[] referenced = MessageProcessor.lzfDecompressChunks(msg.getData());
215             currentContext.readPixelRef = MessageProcessor.decodeReferencedImage(
216                         currentContext.readPixelRef, referenced);
217             final byte[] decoded = MessageProcessor.lzfCompressChunks(
218                         currentContext.readPixelRef, referenced.length);
219             msg = msg.toBuilder().setDataType(DataType.NonreferencedImage)
220                         .setData(ByteString.copyFrom(decoded)).build();
221         }
222         saveMessage(msg, file, lastFrame);
223         if (loadedFrame == lastFrame) {
224             // frame selected for view, so format MessageData
225             final MessageData msgData = new MessageData(Display.getCurrent(), msg, currentContext);
226             lastFrame.add(msgData);
227             uiUpdate = true;
228         }
229         if (msg.getFunction() != Function.eglSwapBuffers)
230             return;
231         synchronized (frames) {
232             if (loadedFrame != lastFrame)
233                 lastFrame.unload();
234             try {
235                 frames.add(lastFrame = new Frame(currentContext, file.getFilePointer()));
236                 // file.getChannel().force(false);
237                 uiUpdate = true;
238             } catch (IOException e) {
239                 e.printStackTrace();
240                 assert false;
241             }
242         }
243         return;
244     }
245 
getFrame(int index)246     Frame getFrame(int index) {
247         synchronized (frames) {
248             Frame newFrame = frames.get(index);
249             if (loadedFrame != null && loadedFrame != lastFrame && newFrame != loadedFrame) {
250                 loadedFrame.unload();
251                 uiUpdate = true;
252             }
253             loadedFrame = newFrame;
254             synchronized (file) {
255                 loadedFrame.load(file);
256             }
257             return loadedFrame;
258         }
259     }
260 
frameCount()261     int frameCount() {
262         synchronized (frames) {
263             return frames.size();
264         }
265     }
266 }
267 
268 /** aggregate of GL states */
269 public class Context implements Cloneable {
270     public final int contextId;
271     public ArrayList<Context> shares = new ArrayList<Context>(); // self too
272     public GLServerVertex serverVertex;
273     public GLServerShader serverShader = new GLServerShader(this);
274     public GLServerState serverState = new GLServerState(this);
275     public GLServerTexture serverTexture;
276 
277     byte[] readPixelRef = new byte[0];
278 
Context(int contextId)279     public Context(int contextId) {
280         this.contextId = contextId;
281         shares.add(this);
282     }
283 
284     @Override
clone()285     public Context clone() {
286         try {
287             Context copy = (Context) super.clone();
288             // FIXME: context sharing list clone
289             copy.shares = new ArrayList<Context>(1);
290             copy.shares.add(copy);
291             if (serverVertex != null)
292                 copy.serverVertex = serverVertex.clone();
293             copy.serverShader = serverShader.clone(copy);
294             copy.serverState = serverState.clone();
295             if (serverTexture != null)
296                 copy.serverTexture = serverTexture.clone(copy);
297             // don't need to clone readPixelsRef, since referenced images
298             // are decoded when they are encountered
299             return copy;
300         } catch (CloneNotSupportedException e) {
301             e.printStackTrace();
302             assert false;
303             return null;
304         }
305     }
306 
307     /** mainly updating states */
processMessage(Message msg)308     public void processMessage(Message msg) {
309         if (serverVertex.process(msg))
310             return;
311         if (serverShader.processMessage(msg))
312             return;
313         if (serverState.processMessage(msg))
314             return;
315         if (serverTexture.processMessage(msg))
316             return;
317     }
318 }
319 
320 class ContextViewProvider extends LabelProvider implements ITreeContentProvider,
321         ISelectionChangedListener {
322     Context context;
323     final GLFramesView sampleView;
324 
ContextViewProvider(final GLFramesView sampleView)325     ContextViewProvider(final GLFramesView sampleView) {
326         this.sampleView = sampleView;
327     }
328 
329     @Override
dispose()330     public void dispose() {
331     }
332 
333     @Override
getText(Object obj)334     public String getText(Object obj) {
335         if (obj == null)
336             return "null";
337         if (obj instanceof Entry) {
338             Entry entry = (Entry) obj;
339             String objStr = "null (or default)";
340             if (entry.obj != null) {
341                 objStr = entry.obj.toString();
342                 if (entry.obj instanceof Message)
343                     objStr = MessageFormatter.format((Message) entry.obj, false);
344             }
345             return entry.name + " = " + objStr;
346         }
347         return obj.toString();
348     }
349 
350     @Override
getImage(Object obj)351     public Image getImage(Object obj) {
352         if (!(obj instanceof Entry))
353             return null;
354         final Entry entry = (Entry) obj;
355         if (!(entry.obj instanceof Message))
356             return null;
357         final Message msg = (Message) entry.obj;
358         switch (msg.getFunction()) {
359             case glTexImage2D:
360             case glTexSubImage2D:
361             case glCopyTexImage2D:
362             case glCopyTexSubImage2D: {
363                 entry.image = new MessageData(Display.getCurrent(), msg, null).getImage();
364                 if (entry.image == null)
365                     return null;
366                 return new Image(Display.getCurrent(), entry.image.getImageData().scaledTo(96, 96));
367             }
368             default:
369                 return null;
370         }
371     }
372 
373     @Override
selectionChanged(SelectionChangedEvent event)374     public void selectionChanged(SelectionChangedEvent event) {
375         StructuredSelection selection = (StructuredSelection) event
376                 .getSelection();
377         if (null == selection)
378             return;
379         final Object obj = selection.getFirstElement();
380         if (!(obj instanceof Entry))
381             return;
382         final Entry entry = (Entry) obj;
383         if (entry.image == null)
384             return;
385         sampleView.tabFolder.setSelection(sampleView.tabItemImage);
386         sampleView.canvas.setBackgroundImage(entry.image);
387         sampleView.canvas.redraw();
388     }
389 
390     @Override
inputChanged(Viewer viewer, Object oldInput, Object newInput)391     public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
392         context = (Context) newInput;
393     }
394 
395     class Entry {
396         String name;
397         Object obj;
398         Image image;
399 
Entry(String name, Object obj)400         Entry(String name, Object obj) {
401             this.name = name;
402             this.obj = obj;
403         }
404     }
405 
406     @Override
getElements(Object inputElement)407     public Object[] getElements(Object inputElement) {
408         if (inputElement != context)
409             return null;
410         return getChildren(new Entry("Context", inputElement));
411     }
412 
413     @Override
getChildren(Object parentElement)414     public Object[] getChildren(Object parentElement) {
415         if (!(parentElement instanceof Entry))
416             return null;
417         Entry entry = (Entry) parentElement;
418         ArrayList<Object> children = new ArrayList<Object>();
419         if (entry.obj == context.serverState.enableDisables) {
420             for (int i = 0; i < context.serverState.enableDisables.size(); i++) {
421                 final int key = context.serverState.enableDisables.keyAt(i);
422                 final int value = context.serverState.enableDisables.valueAt(i);
423                 children.add(GLEnum.valueOf(key).name() + " = " + value);
424             }
425         } else if (entry.obj == context.serverState.integers) {
426             for (int i = 0; i < context.serverState.integers.size(); i++) {
427                 final int key = context.serverState.integers.keyAt(i);
428                 final Message val = context.serverState.integers.valueAt(i);
429                 if (val != null)
430                     children.add(GLEnum.valueOf(key).name() + " : " +
431                             MessageFormatter.format(val, false));
432                 else
433                     children.add(GLEnum.valueOf(key).name() + " : default");
434             }
435         } else if (entry.obj == context.serverState.lastSetter) {
436             for (int i = 0; i < context.serverState.lastSetter.size(); i++) {
437                 final int key = context.serverState.lastSetter.keyAt(i);
438                 final Message msg = context.serverState.lastSetter.valueAt(i);
439                 if (msg == null)
440                     children.add(Function.valueOf(key).name() + " : default");
441                 else
442                     children.add(Function.valueOf(key).name() + " : "
443                             + MessageFormatter.format(msg, false));
444             }
445         } else if (entry.obj instanceof SparseArray) {
446             SparseArray<?> sa = (SparseArray<?>) entry.obj;
447             for (int i = 0; i < sa.size(); i++)
448                 children.add(new Entry("[" + sa.keyAt(i) + "]", sa.valueAt(i)));
449         } else if (entry.obj instanceof Map) {
450             Set<?> set = ((Map<?, ?>) entry.obj).entrySet();
451             for (Object o : set) {
452                 Map.Entry e = (Map.Entry) o;
453                 children.add(new Entry(e.getKey().toString(), e.getValue()));
454             }
455         } else if (entry.obj instanceof SparseIntArray) {
456             SparseIntArray sa = (SparseIntArray) entry.obj;
457             for (int i = 0; i < sa.size(); i++)
458                 children.add("[" + sa.keyAt(i) + "] = " + sa.valueAt(i));
459         } else if (entry.obj instanceof Collection) {
460             Collection<?> collection = (Collection<?>) entry.obj;
461             for (Object o : collection)
462                 children.add(new Entry("[?]", o));
463         } else if (entry.obj.getClass().isArray()) {
464             for (int i = 0; i < Array.getLength(entry.obj); i++)
465                 children.add(new Entry("[" + i + "]", Array.get(entry.obj, i)));
466         } else {
467             Field[] fields = entry.obj.getClass().getFields();
468             for (Field f : fields) {
469                 try {
470                     children.add(new Entry(f.getName(), f.get(entry.obj)));
471                 } catch (IllegalArgumentException e) {
472                     e.printStackTrace();
473                 } catch (IllegalAccessException e) {
474                     e.printStackTrace();
475                 }
476             }
477         }
478         return children.toArray();
479     }
480 
481     @Override
getParent(Object element)482     public Object getParent(Object element) {
483         return null;
484     }
485 
486     @Override
hasChildren(Object element)487     public boolean hasChildren(Object element) {
488         if (element == null)
489             return false;
490         if (!(element instanceof Entry))
491             return false;
492         Object obj = ((Entry) element).obj;
493         if (obj == null)
494             return false;
495         if (obj instanceof SparseArray)
496             return ((SparseArray<?>) obj).size() > 0;
497         else if (obj instanceof SparseIntArray)
498             return ((SparseIntArray) obj).size() > 0;
499         else if (obj instanceof Collection)
500             return ((Collection<?>) obj).size() > 0;
501         else if (obj instanceof Map)
502             return ((Map<?, ?>) obj).size() > 0;
503         else if (obj.getClass().isArray())
504             return Array.getLength(obj) > 0;
505         else if (obj instanceof Message)
506             return false;
507         else if (isPrimitive(obj))
508             return false;
509         else if (obj.getClass().equals(String.class))
510             return false;
511         else if (obj.getClass().equals(Message.class))
512             return false;
513         else if (obj instanceof GLEnum)
514             return false;
515         return obj.getClass().getFields().length > 0;
516     }
517 
isPrimitive(final Object obj)518     static boolean isPrimitive(final Object obj) {
519         final Class<? extends Object> c = obj.getClass();
520         if (c.isPrimitive())
521             return true;
522         if (c == Integer.class)
523             return true;
524         if (c == Boolean.class)
525             return true;
526         if (c == Float.class)
527             return true;
528         if (c == Short.class)
529             return true;
530         return false;
531     }
532 }
533