1 /*
2  * Copyright (C) 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.gltrace.format;
18 
19 import com.android.ide.eclipse.gltrace.GLEnum;
20 import com.android.ide.eclipse.gltrace.GLProtoBuf.GLMessage;
21 import com.android.ide.eclipse.gltrace.GLProtoBuf.GLMessage.DataType;
22 import com.android.ide.eclipse.gltrace.GLProtoBuf.GLMessage.DataType.Type;
23 
24 import java.util.List;
25 import java.util.Map;
26 
27 /**
28  * GLMessageFormatter is used to format and create a string representation for a {@link GLMessage}.
29  * It is provided with a specification for all GL Functions. Using this information, each
30  * GLMessage is parsed and formatted appropriately for display.
31  */
32 public class GLMessageFormatter {
33     private static final String GL_NO_ERROR = "GL_NO_ERROR";
34     private Map<String, GLAPISpec> mAPISpecs;
35     private enum DataTypeContext { CONTEXT_ARGUMENT, CONTEXT_RETURNVALUE };
36 
GLMessageFormatter(Map<String, GLAPISpec> specs)37     public GLMessageFormatter(Map<String, GLAPISpec> specs) {
38         mAPISpecs = specs;
39     }
40 
formatGLMessage(GLMessage glMessage)41     public String formatGLMessage(GLMessage glMessage) {
42         GLAPISpec apiSpec = mAPISpecs.get(glMessage.getFunction().toString());
43         if (apiSpec == null) {
44             return glMessage.getFunction().toString();
45         }
46 
47         return formatCall(apiSpec, glMessage) + formatReturnValue(apiSpec, glMessage);
48     }
49 
formatReturnValue(GLAPISpec apiSpec, GLMessage glMessage)50     private String formatReturnValue(GLAPISpec apiSpec, GLMessage glMessage) {
51         if (apiSpec.getReturnValue().getDataType() == Type.VOID) {
52             return "";
53         }
54 
55         GLDataTypeSpec returnSpec = apiSpec.getReturnValue();
56         return String.format(" = (%s) %s", returnSpec.getCType(),   //$NON-NLS-1$
57                 formatDataValue(glMessage.getReturnValue(),
58                         returnSpec,
59                         DataTypeContext.CONTEXT_RETURNVALUE));
60     }
61 
formatCall(GLAPISpec apiSpec, GLMessage glMessage)62     private String formatCall(GLAPISpec apiSpec, GLMessage glMessage) {
63         return String.format("%s(%s)", apiSpec.getFunction(),       //$NON-NLS-1$
64                 formatArgs(glMessage, apiSpec.getArgs()));
65     }
66 
formatArgs(GLMessage glMessage, List<GLDataTypeSpec> argSpecs)67     private String formatArgs(GLMessage glMessage, List<GLDataTypeSpec> argSpecs) {
68         int sizeEstimate = 10 + argSpecs.size() * 5;
69         StringBuilder sb = new StringBuilder(sizeEstimate);
70 
71         for (int i = 0; i < argSpecs.size(); i++) {
72             GLDataTypeSpec argSpec = argSpecs.get(i);
73 
74             if (argSpec.getDataType() == Type.VOID && !argSpec.isPointer()) {
75                 sb.append("void");                                  //$NON-NLS-1$
76             } else {
77                 sb.append(argSpec.getArgName());
78                 sb.append(" = ");                                   //$NON-NLS-1$
79                 sb.append(formatDataValue(glMessage.getArgs(i),
80                                 argSpec,
81                                 DataTypeContext.CONTEXT_ARGUMENT));
82             }
83 
84             if (i < argSpecs.size() - 1) {
85                 sb.append(", ");                                    //$NON-NLS-1$
86             }
87         }
88 
89         return sb.toString();
90     }
91 
formatDataValue(DataType var, GLDataTypeSpec typeSpec, DataTypeContext context)92     private String formatDataValue(DataType var, GLDataTypeSpec typeSpec, DataTypeContext context) {
93         if (typeSpec.isPointer()) {
94             return formatPointer(var, typeSpec.getDataType());
95         }
96 
97         switch (typeSpec.getDataType()) {
98             case VOID:
99                 return "";
100             case BOOL:
101                 return Boolean.toString(var.getBoolValue(0));
102             case FLOAT:
103                 return String.format("%f", var.getFloatValue(0));   //$NON-NLS-1$
104             case INT:
105                 return Integer.toString(var.getIntValue(0));
106             case ENUM:
107                 if (var.getIntValue(0) == 0 && context == DataTypeContext.CONTEXT_RETURNVALUE) {
108                     return GL_NO_ERROR;
109                 } else {
110                     return GLEnum.valueOf(var.getIntValue(0)).toString();
111                 }
112             default:
113                 return "(unknown type)";                            //$NON-NLS-1$
114         }
115     }
116 
formatPointer(DataType var, Type typeSpec)117     private String formatPointer(DataType var, Type typeSpec) {
118         if (var.getType() != typeSpec && !isEnumTypeWithIntData(var, typeSpec)) {
119             // the type of the data in the message does not match expected specification.
120             // in such a case, just print the data as a pointer and don't try to interpret it.
121             if (var.getIntValueCount() > 0) {
122                 return String.format("0x%x", var.getIntValue(0));   //$NON-NLS-1$
123             } else {
124                 return "0x??";                                      //$NON-NLS-1$
125             }
126         }
127 
128         // Display as array if possible
129         switch (typeSpec) {
130             case BOOL:
131                 return var.getBoolValueList().toString();
132             case FLOAT:
133                 return var.getFloatValueList().toString();
134             case INT:
135                 return var.getIntValueList().toString();
136             case CHAR:
137                 return var.getCharValueList().get(0).toStringUtf8();
138             case ENUM:
139                 List<Integer> vals = var.getIntValueList();
140                 StringBuilder sb = new StringBuilder(vals.size() * 5);
141                 sb.append('[');
142                 for (Integer v: vals) {
143                     sb.append(GLEnum.valueOf(v.intValue()));
144                 }
145                 sb.append(']');
146                 return sb.toString();
147             case VOID:
148                 if (var.getRawBytesList().size() > 0) {
149                     return String.format("[ %d bytes ]", var.getRawBytesList().get(0).size()); //$NON-NLS-1$
150                 }
151                 return "[]"; //$NON-NLS-1$
152         }
153 
154         // We have a pointer, but we don't have the data pointed to.
155         // Just format and return the pointer (points to device memory)
156         if (var.getIntValue(0) == 0) {
157             return "NULL";                                          //$NON-NLS-1$
158         } else {
159             return String.format("0x%x", var.getIntValue(0));       //$NON-NLS-1$
160         }
161     }
162 
isEnumTypeWithIntData(DataType var, Type typeSpec)163     private boolean isEnumTypeWithIntData(DataType var, Type typeSpec) {
164         return var.getType() == Type.INT && typeSpec == Type.ENUM;
165     }
166 }
167