/* * Copyright (C) 2011 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. */ package com.android.ide.eclipse.gltrace.format; import com.android.ide.eclipse.gltrace.GLEnum; import com.android.ide.eclipse.gltrace.GLProtoBuf.GLMessage; import com.android.ide.eclipse.gltrace.GLProtoBuf.GLMessage.DataType; import com.android.ide.eclipse.gltrace.GLProtoBuf.GLMessage.DataType.Type; import java.util.List; import java.util.Map; /** * GLMessageFormatter is used to format and create a string representation for a {@link GLMessage}. * It is provided with a specification for all GL Functions. Using this information, each * GLMessage is parsed and formatted appropriately for display. */ public class GLMessageFormatter { private static final String GL_NO_ERROR = "GL_NO_ERROR"; private Map<String, GLAPISpec> mAPISpecs; private enum DataTypeContext { CONTEXT_ARGUMENT, CONTEXT_RETURNVALUE }; public GLMessageFormatter(Map<String, GLAPISpec> specs) { mAPISpecs = specs; } public String formatGLMessage(GLMessage glMessage) { GLAPISpec apiSpec = mAPISpecs.get(glMessage.getFunction().toString()); if (apiSpec == null) { return glMessage.getFunction().toString(); } return formatCall(apiSpec, glMessage) + formatReturnValue(apiSpec, glMessage); } private String formatReturnValue(GLAPISpec apiSpec, GLMessage glMessage) { if (apiSpec.getReturnValue().getDataType() == Type.VOID) { return ""; } GLDataTypeSpec returnSpec = apiSpec.getReturnValue(); return String.format(" = (%s) %s", returnSpec.getCType(), //$NON-NLS-1$ formatDataValue(glMessage.getReturnValue(), returnSpec, DataTypeContext.CONTEXT_RETURNVALUE)); } private String formatCall(GLAPISpec apiSpec, GLMessage glMessage) { return String.format("%s(%s)", apiSpec.getFunction(), //$NON-NLS-1$ formatArgs(glMessage, apiSpec.getArgs())); } private String formatArgs(GLMessage glMessage, List<GLDataTypeSpec> argSpecs) { int sizeEstimate = 10 + argSpecs.size() * 5; StringBuilder sb = new StringBuilder(sizeEstimate); for (int i = 0; i < argSpecs.size(); i++) { GLDataTypeSpec argSpec = argSpecs.get(i); if (argSpec.getDataType() == Type.VOID && !argSpec.isPointer()) { sb.append("void"); //$NON-NLS-1$ } else { sb.append(argSpec.getArgName()); sb.append(" = "); //$NON-NLS-1$ sb.append(formatDataValue(glMessage.getArgs(i), argSpec, DataTypeContext.CONTEXT_ARGUMENT)); } if (i < argSpecs.size() - 1) { sb.append(", "); //$NON-NLS-1$ } } return sb.toString(); } private String formatDataValue(DataType var, GLDataTypeSpec typeSpec, DataTypeContext context) { if (typeSpec.isPointer()) { return formatPointer(var, typeSpec.getDataType()); } switch (typeSpec.getDataType()) { case VOID: return ""; case BOOL: return Boolean.toString(var.getBoolValue(0)); case FLOAT: return String.format("%f", var.getFloatValue(0)); //$NON-NLS-1$ case INT: return Integer.toString(var.getIntValue(0)); case ENUM: if (var.getIntValue(0) == 0 && context == DataTypeContext.CONTEXT_RETURNVALUE) { return GL_NO_ERROR; } else { return GLEnum.valueOf(var.getIntValue(0)).toString(); } default: return "(unknown type)"; //$NON-NLS-1$ } } private String formatPointer(DataType var, Type typeSpec) { if (var.getType() != typeSpec && !isEnumTypeWithIntData(var, typeSpec)) { // the type of the data in the message does not match expected specification. // in such a case, just print the data as a pointer and don't try to interpret it. if (var.getIntValueCount() > 0) { return String.format("0x%x", var.getIntValue(0)); //$NON-NLS-1$ } else { return "0x??"; //$NON-NLS-1$ } } // Display as array if possible switch (typeSpec) { case BOOL: return var.getBoolValueList().toString(); case FLOAT: return var.getFloatValueList().toString(); case INT: return var.getIntValueList().toString(); case CHAR: return var.getCharValueList().get(0).toStringUtf8(); case ENUM: List<Integer> vals = var.getIntValueList(); StringBuilder sb = new StringBuilder(vals.size() * 5); sb.append('['); for (Integer v: vals) { sb.append(GLEnum.valueOf(v.intValue())); } sb.append(']'); return sb.toString(); case VOID: if (var.getRawBytesList().size() > 0) { return String.format("[ %d bytes ]", var.getRawBytesList().get(0).size()); //$NON-NLS-1$ } return "[]"; //$NON-NLS-1$ } // We have a pointer, but we don't have the data pointed to. // Just format and return the pointer (points to device memory) if (var.getIntValue(0) == 0) { return "NULL"; //$NON-NLS-1$ } else { return String.format("0x%x", var.getIntValue(0)); //$NON-NLS-1$ } } private boolean isEnumTypeWithIntData(DataType var, Type typeSpec) { return var.getType() == Type.INT && typeSpec == Type.ENUM; } }