diff options
Diffstat (limited to 'host/commands/emugen/ApiGen.cpp')
-rw-r--r-- | host/commands/emugen/ApiGen.cpp | 1584 |
1 files changed, 1584 insertions, 0 deletions
diff --git a/host/commands/emugen/ApiGen.cpp b/host/commands/emugen/ApiGen.cpp new file mode 100644 index 000000000..4b40dc677 --- /dev/null +++ b/host/commands/emugen/ApiGen.cpp @@ -0,0 +1,1584 @@ +/* +* 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. +*/ +#include "ApiGen.h" +#include "android/base/EnumFlags.h" +#include "EntryPoint.h" +#include <stdio.h> +#include <stdlib.h> +#include "strUtils.h" +#include <errno.h> +#include <sys/types.h> + +/* Define this to 1 to enable support for the 'isLarge' variable flag + * that instructs the encoder to send large data buffers by a direct + * write through the pipe (i.e. without copying it into a temporary + * buffer. This has definite performance benefits when using a QEMU Pipe. + * + * Set to 0 otherwise. + */ +#define WITH_LARGE_SUPPORT 1 + +// Set to 1 to ensure buffers passed to/from EGL/GL are properly aligned. +// This prevents crashes with certain backends (e.g. OSMesa). +#define USE_ALIGNED_BUFFERS 1 + +// Set these to 1 if you want to instrument either guest's or host's code for +// time-per-call printing. +#define INSTRUMENT_TIMING_GUEST 0 +#define INSTRUMENT_TIMING_HOST 0 + +// Set to 1 to print to logcat for every GL call encoded. +#define DLOG_ALL_ENCODES 0 + +// Set to 1 to check GL errors before and after every decoder call. +#define DECODER_CHECK_GL_ERRORS 0 + +EntryPoint * ApiGen::findEntryByName(const std::string & name) +{ + EntryPoint * entry = NULL; + + size_t n = this->size(); + for (size_t i = 0; i < n; i++) { + if (at(i).name() == name) { + entry = &(at(i)); + break; + } + } + return entry; +} + +void ApiGen::printHeader(FILE *fp) const +{ + fprintf(fp, "// Generated Code - DO NOT EDIT !!\n"); + fprintf(fp, "// generated by 'emugen'\n"); +} + +int ApiGen::genProcTypes(const std::string &filename, SideType side) +{ + FILE *fp = fopen(filename.c_str(), "wt"); + if (fp == NULL) { + perror(filename.c_str()); + return -1; + } + printHeader(fp); + + const char* basename = m_basename.c_str(); + + fprintf(fp, "#ifndef __%s_%s_proc_t_h\n", basename, sideString(side)); + fprintf(fp, "#define __%s_%s_proc_t_h\n", basename, sideString(side)); + fprintf(fp, "\n\n"); + fprintf(fp, "\n#include \"%s_types.h\"\n",basename); + fprintf(fp, "#ifndef %s_APIENTRY\n",basename); + fprintf(fp, "#define %s_APIENTRY \n",basename); + fprintf(fp, "#endif\n"); + + + for (size_t i = 0; i < size(); i++) { + EntryPoint *e = &at(i); + + fprintf(fp, "typedef "); + e->retval().printType(fp); + fprintf(fp, " (%s_APIENTRY *%s_%s_proc_t) (", basename, e->name().c_str(), sideString(side)); + if (side == CLIENT_SIDE) { fprintf(fp, "void * ctx"); } + if (e->customDecoder() && side == SERVER_SIDE) { fprintf(fp, "void *ctx"); } + + VarsArray & evars = e->vars(); + size_t n = evars.size(); + + for (size_t j = 0; j < n; j++) { + if (!evars[j].isVoid()) { + if (j != 0 || side == CLIENT_SIDE || (side == SERVER_SIDE && e->customDecoder())) fprintf(fp, ", "); + evars[j].printType(fp); + } + } + fprintf(fp, ");\n"); + + if (side == SERVER_SIDE && e->customDecoder() && !e->notApi()) { + fprintf(fp, "typedef "); + e->retval().printType(fp); + fprintf(fp, " (%s_APIENTRY *%s_dec_%s_proc_t) (", basename, e->name().c_str(), sideString(side)); + + VarsArray & evars = e->vars(); + size_t n = evars.size(); + + for (size_t j = 0; j < n; j++) { + if (!evars[j].isVoid()) { + if (j != 0) fprintf(fp, ", "); + evars[j].printType(fp); + } + } + fprintf(fp, ");\n"); + } + } + fprintf(fp, "\n\n#endif\n"); + return 0; +} + +int ApiGen::genFuncTable(const std::string &filename, SideType side) +{ + FILE *fp = fopen(filename.c_str(), "wt"); + if (fp == NULL) { + perror(filename.c_str()); + return -1; + } + printHeader(fp); + + fprintf(fp, "#ifndef __%s_%s_ftable_t_h\n", m_basename.c_str(), sideString(side)); + fprintf(fp, "#define __%s_%s_ftable_t_h\n", m_basename.c_str(), sideString(side)); + fprintf(fp, "\n\n"); + fprintf(fp, "static const struct _%s_funcs_by_name {\n", m_basename.c_str()); + fprintf(fp, + "\tconst char *name;\n" \ + "\tvoid *proc;\n" \ + "} %s_funcs_by_name[] = {\n", m_basename.c_str()); + + + for (size_t i = 0; i < size(); i++) { + EntryPoint *e = &at(i); + if (e->notApi()) continue; + fprintf(fp, "\t{\"%s\", (void*)%s},\n", e->name().c_str(), e->name().c_str()); + } + fprintf(fp, "};\n"); + fprintf(fp, "static const int %s_num_funcs = sizeof(%s_funcs_by_name) / sizeof(struct _%s_funcs_by_name);\n", + m_basename.c_str(), m_basename.c_str(), m_basename.c_str()); + fprintf(fp, "\n\n#endif\n"); + return 0; +} + +int ApiGen::genContext(const std::string & filename, SideType side) +{ + FILE *fp = fopen(filename.c_str(), "wt"); + if (fp == NULL) { + perror(filename.c_str()); + return -1; + } + printHeader(fp); + + fprintf(fp, "#ifndef __%s_%s_context_t_h\n", m_basename.c_str(), sideString(side)); + fprintf(fp, "#define __%s_%s_context_t_h\n", m_basename.c_str(), sideString(side)); + + fprintf(fp, "\n#include \"%s_%s_proc.h\"\n", + m_basename.c_str(), + side == CLIENT_SIDE ? "client" : "server"); + fprintf(fp, "\n#include \"%s_types.h\"\n", m_basename.c_str()); + + StringVec & contextHeaders = side == CLIENT_SIDE ? m_clientContextHeaders : m_serverContextHeaders; + for (size_t i = 0; i < contextHeaders.size(); i++) { + fprintf(fp, "#include %s\n", contextHeaders[i].c_str()); + } + fprintf(fp, "\n"); + + fprintf(fp, "\nstruct %s_%s_context_t {\n\n", + m_basename.c_str(), sideString(side)); + + // API entry points + for (size_t i = 0; i < size(); i++) { + EntryPoint *e = &at(i); + if (side == SERVER_SIDE && e->customDecoder() && !e->notApi()) { + fprintf(fp, "\t%s_dec_%s_proc_t %s;\n", e->name().c_str(), sideString(side), e->name().c_str()); + fprintf(fp, "\t%s_%s_proc_t %s_dec;\n", e->name().c_str(), sideString(side), e->name().c_str()); + } else { + fprintf(fp, "\t%s_%s_proc_t %s;\n", e->name().c_str(), sideString(side), e->name().c_str()); + } + } + + // virtual destructor + fprintf(fp, "\tvirtual ~%s_%s_context_t() {}\n", m_basename.c_str(), sideString(side)); + // accessor + if (side == CLIENT_SIDE || side == WRAPPER_SIDE) { + fprintf(fp, "\n\ttypedef %s_%s_context_t *CONTEXT_ACCESSOR_TYPE(void);\n", + m_basename.c_str(), sideString(side)); + fprintf(fp, "\tstatic void setContextAccessor(CONTEXT_ACCESSOR_TYPE *f);\n"); + } + + // init function + fprintf(fp, "\tint initDispatchByName( void *(*getProc)(const char *name, void *userData), void *userData);\n"); + + //client site set error virtual func + if (side == CLIENT_SIDE) { + fprintf(fp, "\tvirtual void setError(unsigned int error){ (void)error; };\n"); + fprintf(fp, "\tvirtual unsigned int getError(){ return 0; };\n"); + } + + fprintf(fp, "};\n"); + + fprintf(fp, "\n#endif\n"); + fclose(fp); + return 0; +} + +int ApiGen::genEntryPoints(const std::string & filename, SideType side) +{ + + if (side != CLIENT_SIDE && side != WRAPPER_SIDE) { + fprintf(stderr, "Entry points are only defined for Client and Wrapper components\n"); + return -999; + } + + + FILE *fp = fopen(filename.c_str(), "wt"); + if (fp == NULL) { + perror(filename.c_str()); + return errno; + } + + printHeader(fp); + fprintf(fp, "#include <stdio.h>\n"); + fprintf(fp, "#include <stdlib.h>\n"); + fprintf(fp, "#include \"%s_%s_context.h\"\n", m_basename.c_str(), sideString(side)); + fprintf(fp, "\n"); + + fprintf(fp, "extern \"C\" {\n"); + + for (size_t i = 0; i < size(); i++) { + fprintf(fp, "\t"); at(i).print(fp, false); fprintf(fp, ";\n"); + } + fprintf(fp, "};\n\n"); + + fprintf(fp, "#ifndef GET_CONTEXT\n"); + fprintf(fp, "static %s_%s_context_t::CONTEXT_ACCESSOR_TYPE *getCurrentContext = NULL;\n", + m_basename.c_str(), sideString(side)); + + fprintf(fp, + "void %s_%s_context_t::setContextAccessor(CONTEXT_ACCESSOR_TYPE *f) { getCurrentContext = f; }\n", + m_basename.c_str(), sideString(side)); + fprintf(fp, "#define GET_CONTEXT %s_%s_context_t * ctx = getCurrentContext()\n", + m_basename.c_str(), sideString(side)); + fprintf(fp, "#endif\n\n"); + + + for (size_t i = 0; i < size(); i++) { + EntryPoint *e = &at(i); + e->print(fp); + fprintf(fp, "{\n"); + fprintf(fp, "\tGET_CONTEXT;\n"); + + bool shouldReturn = !e->retval().isVoid(); + bool shouldCallWithContext = (side == CLIENT_SIDE); + //param check + if (shouldCallWithContext) { + for (size_t j=0; j<e->vars().size(); j++) { + if (e->vars()[j].paramCheckExpression() != "") + fprintf(fp, "\t%s\n", e->vars()[j].paramCheckExpression().c_str()); + } + } + fprintf(fp, "\t%sctx->%s(%s", + shouldReturn ? "return " : "", + e->name().c_str(), + shouldCallWithContext ? "ctx" : ""); + size_t nvars = e->vars().size(); + + for (size_t j = 0; j < nvars; j++) { + if (!e->vars()[j].isVoid()) { + fprintf(fp, "%s %s", + j != 0 || shouldCallWithContext ? "," : "", + e->vars()[j].name().c_str()); + } + } + fprintf(fp, ");\n"); + fprintf(fp, "}\n\n"); + } + fclose(fp); + return 0; +} + + +int ApiGen::genOpcodes(const std::string &filename) +{ + FILE *fp = fopen(filename.c_str(), "wt"); + if (fp == NULL) { + perror(filename.c_str()); + return errno; + } + + printHeader(fp); + fprintf(fp, "#ifndef __GUARD_%s_opcodes_h_\n", m_basename.c_str()); + fprintf(fp, "#define __GUARD_%s_opcodes_h_\n\n", m_basename.c_str()); + for (size_t i = 0; i < size(); i++) { + fprintf(fp, "#define OP_%s \t\t\t\t\t%u\n", at(i).name().c_str(), (unsigned int)i + m_baseOpcode); + } + fprintf(fp, "#define OP_last \t\t\t\t\t%u\n", (unsigned int)size() + m_baseOpcode); + fprintf(fp,"\n\n#endif\n"); + fclose(fp); + return 0; + +} +int ApiGen::genAttributesTemplate(const std::string &filename ) +{ + FILE *fp = fopen(filename.c_str(), "wt"); + if (fp == NULL) { + perror(filename.c_str()); + return -1; + } + + for (size_t i = 0; i < size(); i++) { + if (at(i).hasPointers()) { + fprintf(fp, "#"); + at(i).print(fp); + fprintf(fp, "%s\n\n", at(i).name().c_str()); + } + } + fclose(fp); + return 0; +} + +int ApiGen::genEncoderHeader(const std::string &filename) +{ + FILE *fp = fopen(filename.c_str(), "wt"); + if (fp == NULL) { + perror(filename.c_str()); + return -1; + } + + printHeader(fp); + std::string classname = m_basename + "_encoder_context_t"; + + fprintf(fp, "\n#ifndef GUARD_%s\n", classname.c_str()); + fprintf(fp, "#define GUARD_%s\n\n", classname.c_str()); + + fprintf(fp, "#include \"IOStream.h\"\n"); + fprintf(fp, "#include \"ChecksumCalculator.h\"\n"); + fprintf(fp, "#include \"%s_%s_context.h\"\n\n\n", m_basename.c_str(), sideString(CLIENT_SIDE)); + + for (size_t i = 0; i < m_encoderHeaders.size(); i++) { + fprintf(fp, "#include %s\n", m_encoderHeaders[i].c_str()); + } + fprintf(fp, "\n"); + + fprintf(fp, "struct %s : public %s_%s_context_t {\n\n", + classname.c_str(), m_basename.c_str(), sideString(CLIENT_SIDE)); + fprintf(fp, "\tIOStream *m_stream;\n"); + fprintf(fp, "\tChecksumCalculator *m_checksumCalculator;\n\n"); + + fprintf(fp, "\t%s(IOStream *stream, ChecksumCalculator *checksumCalculator);\n", classname.c_str()); + fprintf(fp, "\tvirtual uint64_t lockAndWriteDma(void* data, uint32_t sz) { return 0; }\n"); + fprintf(fp, "};\n\n"); + + fprintf(fp, "#endif // GUARD_%s\n", classname.c_str()); + + fclose(fp); + return 0; +} + +// Format the byte length expression for a given variable into a user-provided buffer +// If the variable type is not a pointer, this is simply its size as a decimal constant +// If the variable is a pointer, this will be an expression provided by the .attrib file +// through the 'len' attribute. +// +// Returns 1 if the variable is a pointer, 0 otherwise +// +enum class EncodingSizeFlags { + None = 0, + DmaPtrOnly = 1, + ExcludeOut = 2, + UseExistingVar = 4, +}; + +static int getVarEncodingSizeExpression( + Var& var, EntryPoint* e, char* buff, size_t bufflen, + EncodingSizeFlags flags) +{ + if (!var.isPointer()) { + snprintf(buff, bufflen, "%u", (unsigned int) var.type()->bytes()); + return 0; + } + + if ((flags & EncodingSizeFlags::DmaPtrOnly) != 0) { + snprintf(buff, bufflen, "8"); + } else if ((flags & EncodingSizeFlags::ExcludeOut) != 0 && + !(var.pointerDir() & Var::POINTER_IN)) { + snprintf(buff, bufflen, "0"); + } else if ((flags & EncodingSizeFlags::UseExistingVar) != 0) { + snprintf(buff, bufflen, "__size_%s", var.name().c_str()); + } else { + const char* lenExpr = var.lenExpression().c_str(); + const char* varname = var.name().c_str(); + if (e != NULL && lenExpr[0] == '\0') { + fprintf(stderr, "%s: data len is undefined for '%s'\n", + e->name().c_str(), varname); + } + if (var.nullAllowed()) { + snprintf(buff, bufflen, "((%s != NULL) ? %s : 0)", varname, lenExpr); + } else { + snprintf(buff, bufflen, "%s", lenExpr); + } + } + return 1; +} + +static int writeVarEncodingSize(Var& var, bool excludeOutVars, FILE* fp) +{ + int ret = 0; + if (!var.isPointer()) { + fprintf(fp, "%u", (unsigned int) var.type()->bytes()); + } else { + ret = 1; + if (var.isDMA()) { + fprintf(fp, "8"); + return ret; + } + + if (excludeOutVars && !(var.pointerDir() & Var::POINTER_IN)) { + fprintf(fp, "0"); + } else { + fprintf(fp, "__size_%s", var.name().c_str()); + } + } + return ret; +} + +static void writeVarEncodingExpression(Var& var, FILE* fp) +{ + const char* varname = var.name().c_str(); + + if (var.isPointer()) { + // encode a pointer header + if (var.isDMA()) { + fprintf(fp, "\t*(uint64_t *)(ptr) = ctx->lockAndWriteDma(%s, __size_%s); ptr += 8;\n", varname, varname); + } else { + fprintf(fp, "\t*(unsigned int *)(ptr) = __size_%s; ptr += 4;\n", varname); + + Var::PointerDir dir = var.pointerDir(); + if (dir == Var::POINTER_INOUT || dir == Var::POINTER_IN) { + if (var.nullAllowed()) { + fprintf(fp, "\tif (%s != NULL) ", varname); + } else { + fprintf(fp, "\t"); + } + + if (var.packExpression().size() != 0) { + fprintf(fp, "%s;", var.packExpression().c_str()); + } else { + fprintf(fp, "memcpy(ptr, %s, __size_%s);", + varname, varname); + } + + fprintf(fp, "ptr += __size_%s;\n", varname); + } + } + } else { + // encode a non pointer variable + if (!var.isVoid()) { + fprintf(fp, "\t\tmemcpy(ptr, &%s, %u); ptr += %u;\n", + varname, + (unsigned) var.type()->bytes(), + (unsigned) var.type()->bytes()); + } + } +} + +#if WITH_LARGE_SUPPORT +static void writeVarLargeEncodingExpression(Var& var, FILE* fp) +{ + const char* varname = var.name().c_str(); + + fprintf(fp, "\tstream->writeFully(&__size_%s,4);\n", varname); + fprintf(fp, "\tif (useChecksum) checksumCalculator->addBuffer(&__size_%s,4);\n", varname); + if (var.nullAllowed()) { + fprintf(fp, "\tif (%s != NULL) {\n", varname); + } + if (var.writeExpression() != "") { + fprintf(fp, "%s", var.writeExpression().c_str()); + } else { + fprintf(fp, "\t\tstream->writeFully(%s, __size_%s);\n", varname, varname); + fprintf(fp, "\t\tif (useChecksum) checksumCalculator->addBuffer(%s, __size_%s);\n", varname, varname); + } + if (var.nullAllowed()) fprintf(fp, "\t}\n"); +} +#endif /* WITH_LARGE_SUPPORT */ + +static void writeEncodingChecksumValidatorOnReturn(const char* funcName, FILE* fp) { + fprintf(fp, "\tif (useChecksum) {\n" + "\t\tunsigned char *checksumBufPtr = NULL;\n" + "\t\tunsigned char checksumBuf[ChecksumCalculator::kMaxChecksumSize];\n" + "\t\tif (checksumSize > 0) checksumBufPtr = &checksumBuf[0];\n" + "\t\tstream->readback(checksumBufPtr, checksumSize);\n" + "\t\tif (!checksumCalculator->validate(checksumBufPtr, checksumSize)) {\n" + "\t\t\tALOGE(\"%s: GL communication error, please report this issue to b.android.com.\\n\");\n" + "\t\t\tabort();\n" + "\t\t}\n" + "\t}\n", + funcName + ); +} + +static void addGuestTimePrinting(const EntryPoint* e, bool hasTimeBeforeReadback, + FILE* fp) { +#if INSTRUMENT_TIMING_GUEST + fprintf(fp, "\tclock_gettime(CLOCK_REALTIME, &ts1);\n"); + fprintf(fp, "\tlong timeDiff = ts1.tv_sec*1000000 + ts1.tv_nsec/1000 - (ts0.tv_sec*1000000 + ts0.tv_nsec/1000);\n"); + if (hasTimeBeforeReadback) { + fprintf(fp, "\tlong timeDiff2 = ts1.tv_sec*1000000 + ts1.tv_nsec/1000 - (ts2.tv_sec*1000000 + ts2.tv_nsec/1000);\n"); + fprintf(fp, "\tALOGW(\"%s: %%ld (%%ld) us\\n\", timeDiff, timeDiff2);\n", e->name().c_str()); + } else { + fprintf(fp, "\tALOGW(\"%s: %%ld us\\n\", timeDiff);\n", e->name().c_str()); + } +#endif +} + +int ApiGen::genEncoderImpl(const std::string &filename) +{ + FILE *fp = fopen(filename.c_str(), "wt"); + if (fp == NULL) { + perror(filename.c_str()); + return -1; + } + + printHeader(fp); + fprintf(fp, "\n\n"); + fprintf(fp, "#include <string.h>\n"); + fprintf(fp, "#include \"%s_opcodes.h\"\n\n", m_basename.c_str()); + fprintf(fp, "#include \"%s_enc.h\"\n\n\n", m_basename.c_str()); + fprintf(fp, "#include <vector>\n\n"); + fprintf(fp, "#include <stdio.h>\n\n"); + fprintf(fp, "namespace {\n\n"); + + // unsupport printout + fprintf(fp, + "void enc_unsupported()\n" + "{\n" + "\tALOGE(\"Function is unsupported\\n\");\n" + "}\n\n"); + + // entry points; + std::string classname = m_basename + "_encoder_context_t"; + + size_t n = size(); + for (size_t i = 0; i < n; i++) { + EntryPoint *e = &at(i); + + if (e->unsupported()) continue; + + e->print(fp, true, "_enc", /* classname + "::" */"", "void *self"); + fprintf(fp, "{\n"); +#if DLOG_ALL_ENCODES + fprintf(fp, "ALOGD(\"%%s: enter\", __FUNCTION__);\n"); +#endif + +#if INSTRUMENT_TIMING_GUEST + fprintf(fp, "\tstruct timespec ts0, ts1;\n"); + fprintf(fp, "\tclock_gettime(CLOCK_REALTIME, &ts0);\n"); +#endif + +// fprintf(fp, "\n\tDBG(\">>>> %s\\n\");\n", e->name().c_str()); + fprintf(fp, "\n\t%s *ctx = (%s *)self;\n", + classname.c_str(), + classname.c_str()); + fprintf(fp, "\tIOStream *stream = ctx->m_stream;\n" + "\tChecksumCalculator *checksumCalculator = ctx->m_checksumCalculator;\n" + "\tbool useChecksum = checksumCalculator->getVersion() > 0;\n\n"); + VarsArray & evars = e->vars(); + size_t maxvars = evars.size(); + size_t j; + + char buff[256]; + + // Define the __size_XXX variables that contain the size of data + // associated with pointers. + for (j = 0; j < maxvars; j++) { + Var& var = evars[j]; + + if (!var.isPointer()) + continue; + + const char* varname = var.name().c_str(); + fprintf(fp, "\tconst unsigned int __size_%s = ", varname); + + getVarEncodingSizeExpression(var, e, buff, sizeof(buff), + EncodingSizeFlags::None); + fprintf(fp, "%s;\n", buff); + } + + bool hasLargeFields = false; +#if WITH_LARGE_SUPPORT + // We need to take care of 'isLarge' variable in a special way + // Anything before an isLarge variable can be packed into a single + // buffer, which is then commited. Each isLarge variable is a pointer + // to data that can be written to directly through the pipe, which + // will be instant when using a QEMU pipe + + size_t nvars = 0; + size_t npointers = 0; + + // First, compute the total size, 8 bytes for the opcode + payload size (without checksum) + fprintf(fp, "\t unsigned char *ptr;\n"); + fprintf(fp, "\t unsigned char *buf;\n"); + fprintf(fp, "\t const size_t sizeWithoutChecksum = 8"); + + for (j = 0; j < maxvars; j++) { + fprintf(fp, " + "); + npointers += writeVarEncodingSize(evars[j], true, fp); + } + if (npointers > 0) { + fprintf(fp, " + %zu*4", npointers); + } + fprintf(fp, ";\n"); + + // Then, size of the checksum string + fprintf(fp, "\t const size_t checksumSize = checksumCalculator->checksumByteSize();\n"); + + // And, size of the whole thing + fprintf(fp, "\t const size_t totalSize = sizeWithoutChecksum + checksumSize;\n"); + + // We need to divide the packet into fragments. Each fragment contains + // either copied arguments to a temporary buffer, or direct writes for + // large variables. + // + // The first fragment must also contain the opcode+payload_size+checksum_size + // + nvars = 0; + while (nvars < maxvars || maxvars == 0) { + + // Skip over non-large fields + for (j = nvars; j < maxvars; j++) { + if (evars[j].isLarge()) + break; + } + + // Write a fragment if needed. + if (nvars == 0 || j > nvars) { + const char* plus = ""; + + if (nvars == 0 && j == maxvars) { + // Simple shortcut for the common case where we don't have large variables; + fprintf(fp, "\tbuf = stream->alloc(totalSize);\n"); + + } else { + hasLargeFields = true; + // allocate buffer from the stream until the first large variable + fprintf(fp, "\tbuf = stream->alloc("); + plus = ""; + + if (nvars == 0) { + fprintf(fp,"8"); plus = " + "; + } + if (j > nvars) { + npointers = 0; + for (j = nvars; j < maxvars && !evars[j].isLarge(); j++) { + fprintf(fp, "%s", plus); plus = " + "; + npointers += writeVarEncodingSize(evars[j], false, fp); + } + if (npointers > 0) { + fprintf(fp, "%s%zu*4", plus, npointers); plus = " + "; + } + } + fprintf(fp,");\n"); + } + fprintf(fp, "\tptr = buf;\n"); + + // encode packet header if needed. + if (nvars == 0) { + fprintf(fp, "\tint tmp = OP_%s;memcpy(ptr, &tmp, 4); ptr += 4;\n", e->name().c_str()); + fprintf(fp, "\tmemcpy(ptr, &totalSize, 4); ptr += 4;\n\n"); + } + + if (maxvars == 0) { + fprintf(fp, "\n\tif (useChecksum) checksumCalculator->addBuffer(buf, ptr-buf);\n"); + break; + } + + // encode non-large fields in this fragment + for (j = nvars; j < maxvars && !evars[j].isLarge(); j++) { + writeVarEncodingExpression(evars[j],fp); + } + + fprintf(fp, "\n\tif (useChecksum) checksumCalculator->addBuffer(buf, ptr-buf);\n"); + // Ensure the fragment is commited if it is followed by a large variable + if (j < maxvars) { + fprintf(fp, "\tstream->flush();\n"); + } + } + + // If we have one or more large variables, write them directly. + // As size + data + for ( ; j < maxvars && evars[j].isLarge(); j++) { + writeVarLargeEncodingExpression(evars[j], fp); + } + + nvars = j; + } + +#else /* !WITH_LARGE_SUPPORT */ + size_t nvars = evars.size(); + size_t npointers = 0; + fprintf(fp, "\tunsigned char *ptr;\n"); + fprintf(fp, "\tunsigned char *buf;\n"); + fprintf(fp, "\tconst size_t sizeWithoutChecksum = 8"); + for (size_t j = 0; j < nvars; j++) { + npointers += getVarEncodingSizeExpression( + evars[j], e, buff, sizeof(buff), + (evars[j].isDMA() ? EncodingSizeFlags::DmaPtrOnly + : EncodingSizeFlags::None) | + EncodingSizeFlags::UseExistingVar | + EncodingSizeFlags::ExcludeOut); + fprintf(fp, " + %s", buff); + } + fprintf(fp, " + %u * 4;\n", (unsigned int)npointers); + // Size of checksum + fprintf(fp, "\t const size_t checksumSize = checksumCalculator->checksumByteSize();\n"); + // Size of the whole thing + fprintf(fp, "\t const size_t totalSize = sizeWithoutChecksum + checksumSize;\n"); + + // allocate buffer from the stream; + fprintf(fp, "\tptr = buf = stream->alloc(totalSize);\n\n"); + + // encode into the stream; + fprintf(fp, "\tint tmp = OP_%s; memcpy(ptr, &tmp, 4); ptr += 4;\n", e->name().c_str()); + fprintf(fp, "\tmemcpy(ptr, &totalSize, 4); ptr += 4;\n\n"); + + // out variables + for (size_t j = 0; j < nvars; j++) { + writeVarEncodingExpression(evars[j], fp); + } + + fprintf(fp, "\tif (useChecksum) checksumCalculator->addBuffer(buf, ptr - buf);\n"); +#endif /* !WITH_LARGE_SUPPORT */ + + // checksum + if (hasLargeFields) { + fprintf(fp, "\tbuf = stream->alloc(checksumSize);\n"); + fprintf(fp, "\tif (useChecksum) checksumCalculator->writeChecksum(buf, checksumSize);\n\n"); + } else { + fprintf(fp, "\tif (useChecksum) checksumCalculator->writeChecksum(ptr, checksumSize); ptr += checksumSize;\n\n"); + } + + // in variables; + bool hasTimeBeforeReadback = false; + bool hasReadbackChecksum = false; + for (size_t j = 0; j < nvars; j++) { + if (evars[j].isPointer()) { + Var::PointerDir dir = evars[j].pointerDir(); + if (dir == Var::POINTER_INOUT || dir == Var::POINTER_OUT) { + const char* varname = evars[j].name().c_str(); + const char* indent = "\t"; + +#if INSTRUMENT_TIMING_GUEST + if (!hasTimeBeforeReadback) { + hasTimeBeforeReadback = true; + // Let's flush the stream before measuring the time. + fprintf(fp, "\tstream->flush();\n"); + fprintf(fp, "\tstruct timespec ts2;\n"); + fprintf(fp, "\tclock_gettime(CLOCK_REALTIME, &ts2);\n"); + } +#endif + if (evars[j].nullAllowed()) { + fprintf(fp, "\tif (%s != NULL) {\n",varname); + indent = "\t\t"; + } + + if (evars[j].guestUnpackExpression() != "") { + fprintf(fp, "%s%s;\n", indent, evars[j].guestUnpackExpression().c_str()); + } else { + fprintf(fp, "%sstream->readback(%s, __size_%s);\n", + indent, varname, varname); + } + fprintf(fp, "%sif (useChecksum) checksumCalculator->addBuffer(%s, __size_%s);\n", + indent, varname, varname); + if (evars[j].nullAllowed()) { + fprintf(fp, "\t}\n"); + } + hasReadbackChecksum = true; + } + } + } +//XXX fprintf(fp, "\n\tDBG(\"<<<< %s\\n\");\n", e->name().c_str()); + + // todo - return value for pointers + if (e->retval().isPointer()) { + fprintf(stderr, "WARNING: %s : return value of pointer is unsupported\n", + e->name().c_str()); + if (e->flushOnEncode()) { + fprintf(fp, "\tstream->flush();\n"); + } + addGuestTimePrinting(e, hasTimeBeforeReadback, fp); + fprintf(fp, "\t return NULL;\n"); + } else if (e->retval().type()->name() != "void") { +#if INSTRUMENT_TIMING_GUEST + if (!hasTimeBeforeReadback) { + hasTimeBeforeReadback = true; + fprintf(fp, "\tstream->flush();\n"); + fprintf(fp, "\tstruct timespec ts2;\n"); + fprintf(fp, "\tclock_gettime(CLOCK_REALTIME, &ts2);\n"); + } +#endif + + fprintf(fp, "\n\t%s retval;\n", e->retval().type()->name().c_str()); + fprintf(fp, "\tstream->readback(&retval, %u);\n",(unsigned) e->retval().type()->bytes()); + fprintf(fp, "\tif (useChecksum) checksumCalculator->addBuffer(&retval, %u);\n", + (unsigned) e->retval().type()->bytes()); + writeEncodingChecksumValidatorOnReturn(e->name().c_str(), fp); + addGuestTimePrinting(e, hasTimeBeforeReadback, fp); + fprintf(fp, "\treturn retval;\n"); + } else { + if (e->flushOnEncode()) fprintf(fp, "\tstream->flush();\n"); + if (hasReadbackChecksum) writeEncodingChecksumValidatorOnReturn(e->name().c_str(), fp); + addGuestTimePrinting(e, hasTimeBeforeReadback, fp); + } + fprintf(fp, "}\n\n"); + } + + fprintf(fp, "} // namespace\n\n"); + + // constructor + fprintf(fp, "%s::%s(IOStream *stream, ChecksumCalculator *checksumCalculator)\n{\n", classname.c_str(), classname.c_str()); + fprintf(fp, "\tm_stream = stream;\n"); + fprintf(fp, "\tm_checksumCalculator = checksumCalculator;\n\n"); + + for (size_t i = 0; i < n; i++) { + EntryPoint *e = &at(i); + if (e->unsupported()) { + fprintf(fp, + "\tthis->%s = (%s_%s_proc_t) &enc_unsupported;\n", + e->name().c_str(), + e->name().c_str(), + sideString(CLIENT_SIDE)); + } else { + fprintf(fp, + "\tthis->%s = &%s_enc;\n", + e->name().c_str(), + e->name().c_str()); + } + } + fprintf(fp, "}\n\n"); + + fclose(fp); + return 0; +} + + +int ApiGen::genDecoderHeader(const std::string &filename) +{ + FILE *fp = fopen(filename.c_str(), "wt"); + if (fp == NULL) { + perror(filename.c_str()); + return -1; + } + + printHeader(fp); + std::string classname = m_basename + "_decoder_context_t"; + + fprintf(fp, "\n#ifndef GUARD_%s\n", classname.c_str()); + fprintf(fp, "#define GUARD_%s\n\n", classname.c_str()); + + fprintf(fp, "#include \"OpenglRender/IOStream.h\"\n"); + fprintf(fp, "#include \"ChecksumCalculator.h\"\n"); + fprintf(fp, "#include \"%s_%s_context.h\"\n\n\n", m_basename.c_str(), sideString(SERVER_SIDE)); + fprintf(fp, "#include \"emugl/common/logging.h\"\n"); +#if INSTRUMENT_TIMING_HOST + fprintf(fp, "#include \"time.h\"\n"); +#endif + + for (size_t i = 0; i < m_decoderHeaders.size(); i++) { + fprintf(fp, "#include %s\n", m_decoderHeaders[i].c_str()); + } + fprintf(fp, "\n"); + + fprintf(fp, "struct %s : public %s_%s_context_t {\n\n", + classname.c_str(), m_basename.c_str(), sideString(SERVER_SIDE)); + fprintf(fp, "\tsize_t decode(void *buf, size_t bufsize, IOStream *stream, ChecksumCalculator* checksumCalc);\n"); + fprintf(fp, "\n};\n\n"); + fprintf(fp, "#endif // GUARD_%s\n", classname.c_str()); + + fclose(fp); + return 0; +} + +int ApiGen::genContextImpl(const std::string &filename, SideType side) +{ + FILE *fp = fopen(filename.c_str(), "wt"); + if (fp == NULL) { + perror(filename.c_str()); + return -1; + } + printHeader(fp); + + std::string classname = m_basename + "_" + sideString(side) + "_context_t"; + size_t n = size(); + fprintf(fp, "\n\n#include <string.h>\n"); + fprintf(fp, "#include \"%s_%s_context.h\"\n\n\n", m_basename.c_str(), sideString(side)); + fprintf(fp, "#include <stdio.h>\n\n"); + + fprintf(fp, "int %s::initDispatchByName(void *(*getProc)(const char *, void *userData), void *userData)\n{\n", classname.c_str()); + for (size_t i = 0; i < n; i++) { + EntryPoint *e = &at(i); + if (side == SERVER_SIDE && e->customDecoder() && !e->notApi()) { + fprintf(fp, "\t%s = (%s_dec_%s_proc_t) getProc(\"%s\", userData);\n", + e->name().c_str(), + e->name().c_str(), + sideString(side), + e->name().c_str()); + } else { + fprintf(fp, "\t%s = (%s_%s_proc_t) getProc(\"%s\", userData);\n", + e->name().c_str(), + e->name().c_str(), + sideString(side), + e->name().c_str()); + } + } + fprintf(fp, "\treturn 0;\n"); + fprintf(fp, "}\n\n"); + fclose(fp); + return 0; +} + +int ApiGen::genDecoderImpl(const std::string &filename) +{ + FILE *fp = fopen(filename.c_str(), "wt"); + if (fp == NULL) { + perror(filename.c_str()); + return -1; + } + + printHeader(fp); + + std::string classname = m_basename + "_decoder_context_t"; + + size_t n = size(); + + bool changesChecksum = false; + for (size_t i = 0; i < size(); ++i) { + const EntryPoint& ep = at(i); + if (ep.name().find("SelectChecksum") != std::string::npos) { + changesChecksum = true; + break; + } + } + + fprintf(fp, "\n\n#include <string.h>\n"); + fprintf(fp, "#include \"%s_opcodes.h\"\n\n", m_basename.c_str()); + fprintf(fp, "#include \"%s_dec.h\"\n\n\n", m_basename.c_str()); + fprintf(fp, "#include \"ProtocolUtils.h\"\n\n"); + fprintf(fp, "#include \"ChecksumCalculatorThreadInfo.h\"\n\n"); + fprintf(fp, "#include <stdio.h>\n\n"); + fprintf(fp, "typedef unsigned int tsize_t; // Target \"size_t\", which is 32-bit for now. It may or may not be the same as host's size_t when emugen is compiled.\n\n"); + + // helper macros + fprintf(fp, + "#ifdef OPENGL_DEBUG_PRINTOUT\n" + "# define DEBUG(...) do { if (emugl_cxt_logger) { emugl_cxt_logger(__VA_ARGS__); } } while(0)\n" + "#else\n" + "# define DEBUG(...) ((void)0)\n" + "#endif\n\n"); + + fprintf(fp, +#if DECODER_CHECK_GL_ERRORS + "#define CHECK_GL_ERRORS\n" +#endif + "#ifdef CHECK_GL_ERRORS\n" + "# define SET_LASTCALL(name) sprintf(lastCall, #name)\n" + "#else\n" + "# define SET_LASTCALL(name)\n" + "#endif\n"); + + // helper templates + fprintf(fp, "using namespace emugl;\n\n"); + + // decoder switch; + fprintf(fp, "size_t %s::decode(void *buf, size_t len, IOStream *stream, ChecksumCalculator* checksumCalc) {\n", classname.c_str()); + fprintf(fp, +"\tif (len < 8) return 0; \n\ +#ifdef CHECK_GL_ERRORS\n\ +\tchar lastCall[256] = {0};\n\ +#endif\n\ +\tunsigned char *ptr = (unsigned char *)buf;\n\ +\tconst unsigned char* const end = (const unsigned char*)buf + len;\n"); + if (!changesChecksum) { + fprintf(fp, +R"( const size_t checksumSize = checksumCalc->checksumByteSize(); + const bool useChecksum = checksumSize > 0; +)"); + } + fprintf(fp, +"\twhile (end - ptr >= 8) {\n\ +\t\tuint32_t opcode = *(uint32_t *)ptr; \n\ +\t\tint32_t packetLen = *(int32_t *)(ptr + 4);\n\ +\t\tif (end - ptr < packetLen) return ptr - (unsigned char*)buf;\n"); + if (changesChecksum) { + fprintf(fp, +R"( // Do this on every iteration, as some commands may change the checksum + // calculation parameters. + const size_t checksumSize = checksumCalc->checksumByteSize(); + const bool useChecksum = checksumSize > 0; +)"); + } + fprintf(fp, "\t\tswitch(opcode) {\n"); + + for (size_t f = 0; f < n; f++) { + enum Pass_t { + PASS_FIRST = 0, + PASS_VariableDeclarations = PASS_FIRST, + PASS_Protocol, + PASS_TmpBuffAlloc, + PASS_MemAlloc, + PASS_DebugPrint, + PASS_FunctionCall, + PASS_FlushOutput, + PASS_Epilog, + PASS_LAST }; + EntryPoint *e = &(*this)[f]; + + // construct a printout string; + std::string printString; + for (size_t i = 0; i < e->vars().size(); i++) { + Var *v = &e->vars()[i]; + if (!v->isVoid()) printString += (v->isPointer() ? "%p(%u)" : v->type()->printFormat()) + " "; + } + + // TODO - add for return value; + fprintf(fp, "\t\tcase OP_%s: {\n", e->name().c_str()); + +#if INSTRUMENT_TIMING_HOST + fprintf(fp, "\t\t\tstruct timespec ts0, ts1, ts2;\n"); + fprintf(fp, "\t\t\tclock_gettime(CLOCK_REALTIME, &ts0);\n"); +#endif + bool totalTmpBuffExist = false; + std::string totalTmpBuffOffset = "0"; + std::string *tmpBufOffset = new std::string[e->vars().size()]; + + // construct retval type string + std::string retvalType; + if (!e->retval().isVoid()) { + retvalType = e->retval().type()->name(); + } + + for (int pass = PASS_FIRST; pass < PASS_LAST; pass++) { +#if INSTRUMENT_TIMING_HOST + if (pass == PASS_FunctionCall) { + fprintf(fp, "\t\t\tclock_gettime(CLOCK_REALTIME, &ts2);\n"); + } +#endif + if (pass == PASS_FunctionCall && + !e->retval().isVoid() && + !e->retval().isPointer()) { + fprintf(fp, "\t\t\t*(%s *)(&tmpBuf[%s]) = ", retvalType.c_str(), + totalTmpBuffOffset.c_str()); + } + + if (pass == PASS_FunctionCall) { + if (e->customDecoder() && !e->notApi()) { + fprintf(fp, "\t\t\tthis->%s_dec(", e->name().c_str()); + } else { + fprintf(fp, "\t\t\tthis->%s(", e->name().c_str()); + } + if (e->customDecoder()) { + fprintf(fp, "this"); // add a context to the call + } + } else if (pass == PASS_DebugPrint) { + if (strstr(m_basename.c_str(), "gl")) { + fprintf(fp, "\t\t#ifdef CHECK_GL_ERRORS\n"); + fprintf(fp, "\t\tGLint err = this->glGetError();\n"); + fprintf(fp, "\t\tif (err) fprintf(stderr, \"%s Error (pre-call): 0x%%X before %s\\n\", err);\n", + m_basename.c_str(), e->name().c_str()); + fprintf(fp, "\t\t#endif\n"); + } + fprintf(fp, + "\t\t\tDEBUG(\"%s(%%p): %s(%s)\\n\", stream", + m_basename.c_str(), + e->name().c_str(), + printString.c_str()); + if (e->vars().size() > 0 && !e->vars()[0].isVoid()) { + fprintf(fp, ", "); + } + } + + std::string varoffset = "8"; // skip the header + VarsArray & evars = e->vars(); + // allocate memory for out pointers; + for (size_t j = 0; j < evars.size(); j++) { + Var *v = & evars[j]; + if (v->isVoid()) { + continue; + } + const char* var_name = v->name().c_str(); + const char* var_type_name = v->type()->name().c_str(); + const unsigned var_type_bytes = v->type()->bytes(); + + if ((pass == PASS_FunctionCall) && + (j != 0 || e->customDecoder())) { + fprintf(fp, ", "); + } + if (pass == PASS_DebugPrint && j != 0) { + fprintf(fp, ", "); + } + + if (v->isPointer() && v->isDMA()) { + if (pass == PASS_VariableDeclarations) { + fprintf(fp, + "\t\t\tuint64_t var_%s_guest_paddr = Unpack<uint64_t,uint64_t>(ptr + %s);\n" + "\t\t\t%s var_%s = stream->getDmaForReading(var_%s_guest_paddr);\n", + var_name, + varoffset.c_str(), + var_type_name, + var_name, + var_name); + } + if (pass == PASS_FunctionCall || + pass == PASS_DebugPrint) { + fprintf(fp, "var_%s", var_name); + } + varoffset += " + 8"; + } + + if (!v->isPointer()) { + if (pass == PASS_VariableDeclarations) { + fprintf(fp, + "\t\t\t%s var_%s = Unpack<%s,uint%u_t>(ptr + %s);\n", + var_type_name, + var_name, + var_type_name, + var_type_bytes * 8U, + varoffset.c_str()); + } + + if (pass == PASS_FunctionCall || + pass == PASS_DebugPrint) { + fprintf(fp, "var_%s", var_name); + } + varoffset += " + " + toString(var_type_bytes); + continue; + } + + if (pass == PASS_VariableDeclarations) { + fprintf(fp, + "\t\t\tuint32_t size_%s __attribute__((unused)) = Unpack<uint32_t,uint32_t>(ptr + %s);\n", + var_name, + varoffset.c_str()); + } + + if (!v->isDMA()) { + if (v->pointerDir() & Var::POINTER_IN) { + if (pass == PASS_VariableDeclarations) { + #if USE_ALIGNED_BUFFERS + fprintf(fp, + "\t\t\tInputBuffer inptr_%s(ptr + %s + 4, size_%s);\n", + var_name, + varoffset.c_str(), + var_name); + if (v->unpackExpression().size() > 0) { + fprintf(fp, + "\t\t\tvoid* inptr_%s_unpacked;\n" + "\t\t\t%s;\n", + var_name, + v->unpackExpression().c_str()); + } + + } + if (pass == PASS_FunctionCall && + v->pointerDir() == Var::POINTER_IN) { + if (v->nullAllowed()) { + fprintf(fp, + "size_%s == 0 ? nullptr : (%s)(inptr_%s.get())", + var_name, + var_type_name, + var_name); + } else { + if (v->unpackExpression().size() > 0) { + fprintf(fp, + "(%s)(inptr_%s_unpacked)", + var_type_name, + var_name); + } else { + fprintf(fp, + "(%s)(inptr_%s.get())", + var_type_name, + var_name); + } + } + } else if (pass == PASS_DebugPrint && + v->pointerDir() == Var::POINTER_IN) { + fprintf(fp, + "(%s)(inptr_%s.get()), size_%s", + var_type_name, + var_name, + var_name); + } + #else // !USE_ALIGNED_BUFFERS + fprintf(fp, + "unsigned char *inptr_%s = (ptr + %s + 4);\n", + var_name, + varoffset.c_str()); + } + if (pass == PASS_FunctionCall && + v->pointerDir() == Var::POINTER_IN) { + if (v->nullAllowed()) { + fprintf(fp, + "size_%s == 0 ? NULL : (%s)(inptr_%s)", + var_name, + var_type_name, + var_name); + } else { + fprintf(fp, + "(%s)(inptr_%s)", + var_type_name, + var_name); + } + } else if (pass == PASS_DebugPrint && + v->pointerDir() == Var::POINTER_IN) { + fprintf(fp, + "(%s)(inptr_%s), size_%s", + var_type_name, + var_name, + var_name); + } + #endif // !USE_ALIGNED_BUFFERS + varoffset += " + 4 + size_"; + varoffset += var_name; + } + if (v->pointerDir() & Var::POINTER_OUT) { // out pointer; + if (pass == PASS_TmpBuffAlloc) { + if (!totalTmpBuffExist) { + fprintf(fp, + "\t\t\tsize_t totalTmpSize = size_%s;\n", + var_name); + } else { + fprintf(fp, + "\t\t\ttotalTmpSize += size_%s;\n", + var_name); + } + tmpBufOffset[j] = totalTmpBuffOffset; + totalTmpBuffOffset += " + size_"; + totalTmpBuffOffset += var_name; + totalTmpBuffExist = true; + } else if (pass == PASS_MemAlloc) { + #if USE_ALIGNED_BUFFERS + fprintf(fp, + "\t\t\tOutputBuffer outptr_%s(&tmpBuf[%s], size_%s);\n", + var_name, + tmpBufOffset[j].c_str(), + var_name); + // If both input and output variable, initialize with the input. + if (v->pointerDir() == Var::POINTER_INOUT) { + fprintf(fp, + "\t\t\tmemcpy(outptr_%s.get(), inptr_%s.get(), size_%s);\n", + var_name, + var_name, + var_name); + } + + if (v->hostPackExpression() != "") { + fprintf(fp, "\t\t\tvoid* forPacking_%s = nullptr;\n", var_name); + } + if (v->hostPackTmpAllocExpression() != "") { + fprintf(fp, "\t\t\t%s;\n", v->hostPackTmpAllocExpression().c_str()); + } + } else if (pass == PASS_FunctionCall) { + if (v->hostPackExpression() != "") { + fprintf(fp, + "(%s)(forPacking_%s)", + var_type_name, + var_name); + } else { + if (v->nullAllowed()) { + fprintf(fp, + "size_%s == 0 ? nullptr : (%s)(outptr_%s.get())", + var_name, + var_type_name, + var_name); + } else { + fprintf(fp, + "(%s)(outptr_%s.get())", + var_type_name, + var_name); + } + } + } else if (pass == PASS_DebugPrint) { + fprintf(fp, + "(%s)(outptr_%s.get()), size_%s", + var_type_name, + var_name, + var_name); + } + if (pass == PASS_FlushOutput) { + if (v->hostPackExpression() != "") { + fprintf(fp, + "\t\t\tif (size_%s) {\n" + "\t\t\t%s; }\n", + var_name, + v->hostPackExpression().c_str()); + } + fprintf(fp, + "\t\t\toutptr_%s.flush();\n", + var_name); + } + #else // !USE_ALIGNED_BUFFERS + fprintf(fp, + "\t\t\tunsigned char *outptr_%s = &tmpBuf[%s];\n", + var_name, + tmpBufOffset[j].c_str()); + fprintf(fp, + "\t\t\tmemset(outptr_%s, 0, %s);\n", + var_name, + toString(v->type()->bytes()).c_str()); + } else if (pass == PASS_FunctionCall) { + if (v->nullAllowed()) { + fprintf(fp, + "size_%s == 0 ? NULL : (%s)(outptr_%s)", + var_name, + var_type_name, + var_name); + } else { + fprintf(fp, + "(%s)(outptr_%s)", + var_type_name, + var_name); + } + } else if (pass == PASS_DebugPrint) { + fprintf(fp, + "(%s)(outptr_%s), size_%s", + var_type_name, + var_name, + varoffset.c_str()); + } + #endif // !USE_ALIGNED_BUFFERS + if (v->pointerDir() == Var::POINTER_OUT) { + varoffset += " + 4"; + } + } + } + } + + if (pass == PASS_Protocol) { + fprintf(fp, + "\t\t\tif (useChecksum) {\n" + "\t\t\t\tChecksumCalculatorThreadInfo::validOrDie(checksumCalc, ptr, %s, " + "ptr + %s, checksumSize, " + "\n\t\t\t\t\t\"%s::decode," + " OP_%s: GL checksumCalculator failure\\n\");\n" + "\t\t\t}\n", + varoffset.c_str(), + varoffset.c_str(), + classname.c_str(), + e->name().c_str() + ); + + varoffset += " + 4"; + } + + if (pass == PASS_FunctionCall || + pass == PASS_DebugPrint) { + fprintf(fp, ");\n"); + + if (pass == PASS_FunctionCall) { + // unlock all dma buffers that have been passed + for (size_t j = 0; j < evars.size(); j++) { + Var *v = & evars[j]; + if (v->isVoid()) { + continue; + } + const char* var_name = v->name().c_str(); + if (v->isDMA()) { + fprintf(fp, + "\t\t\tstream->unlockDma(var_%s_guest_paddr);\n", + var_name); + } + } + } + } + + if (pass == PASS_TmpBuffAlloc) { + if (!e->retval().isVoid() && !e->retval().isPointer()) { + if (!totalTmpBuffExist) + fprintf(fp, + "\t\t\tsize_t totalTmpSize = sizeof(%s);\n", + retvalType.c_str()); + else + fprintf(fp, + "\t\t\ttotalTmpSize += sizeof(%s);\n", + retvalType.c_str()); + + totalTmpBuffExist = true; + } + if (totalTmpBuffExist) { + fprintf(fp, + "\t\t\ttotalTmpSize += checksumSize;\n" + "\t\t\tunsigned char *tmpBuf = stream->alloc(totalTmpSize);\n"); + } + } + + if (pass == PASS_Epilog) { + // send back out pointers data as well as retval + if (totalTmpBuffExist) { + fprintf(fp, + "\t\t\tif (useChecksum) {\n" + "\t\t\t\tChecksumCalculatorThreadInfo::writeChecksum(checksumCalc, " + "&tmpBuf[0], totalTmpSize - checksumSize, " + "&tmpBuf[totalTmpSize - checksumSize], checksumSize);\n" + "\t\t\t}\n" + "\t\t\tstream->flush();\n"); + } + } + } // pass; + +#if INSTRUMENT_TIMING_HOST + fprintf(fp, "\t\t\tclock_gettime(CLOCK_REALTIME, &ts1);\n"); + fprintf(fp, "\t\t\tlong timeDiff = ts1.tv_sec*1000000 + ts1.tv_nsec/1000 - (ts0.tv_sec*1000000 + ts0.tv_nsec/1000);\n"); + fprintf(fp, "\t\t\tlong timeDiff2 = ts1.tv_sec*1000000 + ts1.tv_nsec/1000 - (ts2.tv_sec*1000000 + ts2.tv_nsec/1000);\n"); + fprintf(fp, "\t\t\tprintf(\"(timing) %%4ld.%%06ld %s: %%ld (%%ld) us\\n\", " + "ts1.tv_sec, ts1.tv_nsec/1000, timeDiff, timeDiff2);\n", e->name().c_str()); +#endif + fprintf(fp, "\t\t\tSET_LASTCALL(\"%s\");\n", e->name().c_str()); + fprintf(fp, "\t\t\tbreak;\n"); + fprintf(fp, "\t\t}\n"); + + delete [] tmpBufOffset; + } + fprintf(fp, "\t\tdefault:\n"); + fprintf(fp, "\t\t\treturn ptr - (unsigned char*)buf;\n"); + fprintf(fp, "\t\t} //switch\n"); + if (strstr(m_basename.c_str(), "gl")) { + fprintf(fp, "\t\t#ifdef CHECK_GL_ERRORS\n"); + fprintf(fp, "\t\tGLint err = this->glGetError();\n"); + fprintf(fp, "\t\tif (err) fprintf(stderr, \"%s Error (post-call): 0x%%X in %%s\\n\", err, lastCall);\n", m_basename.c_str()); + fprintf(fp, "\t\t#endif\n"); + } + + fprintf(fp, "\t\tptr += packetLen;\n"); + fprintf(fp, "\t} // while\n"); + fprintf(fp, "\treturn ptr - (unsigned char*)buf;\n"); + fprintf(fp, "}\n"); + + fclose(fp); + return 0; +} + +int ApiGen::readSpec(const std::string & filename) +{ + FILE *specfp = fopen(filename.c_str(), "rt"); + if (specfp == NULL) { + return -1; + } + + char line[1000]; + unsigned int lc = 0; + while (fgets(line, sizeof(line), specfp) != NULL) { + lc++; + EntryPoint ref; + if (ref.parse(lc, std::string(line))) { + push_back(ref); + updateMaxEntryPointsParams(ref.vars().size()); + } + } + fclose(specfp); + return 0; +} + +int ApiGen::readAttributes(const std::string & attribFilename) +{ + enum { ST_NAME, ST_ATT } state; + + FILE *fp = fopen(attribFilename.c_str(), "rt"); + if (fp == NULL) { + perror(attribFilename.c_str()); + return -1; + } + char buf[1000]; + + state = ST_NAME; + EntryPoint *currentEntry = NULL; + size_t lc = 0; + bool globalAttributes = false; + while (fgets(buf, sizeof(buf), fp) != NULL) { + lc++; + std::string line(buf); + if (line.size() == 0) continue; // could that happen? + + if (line.at(0) == '#') continue; // comment + + size_t first = line.find_first_not_of(" \t\n"); + if (state == ST_ATT && (first == std::string::npos || first == 0)) state = ST_NAME; + + line = trim(line); + if (line.size() == 0 || line.at(0) == '#') continue; + + switch(state) { + case ST_NAME: + if (line == "GLOBAL") { + globalAttributes = true; + } else { + globalAttributes = false; + currentEntry = findEntryByName(line); + if (currentEntry == NULL) { + fprintf(stderr, "WARNING: %u: attribute of non existant entry point %s\n", (unsigned int)lc, line.c_str()); + } + } + state = ST_ATT; + break; + case ST_ATT: + if (globalAttributes) { + setGlobalAttribute(line, lc); + } else if (currentEntry != NULL) { + currentEntry->setAttribute(line, lc); + } + break; + } + } + return 0; +} + + +int ApiGen::setGlobalAttribute(const std::string & line, size_t lc) +{ + size_t pos = 0; + size_t last; + std::string token = getNextToken(line, pos, &last, WHITESPACE); + pos = last; + + if (token == "base_opcode") { + std::string str = getNextToken(line, pos, &last, WHITESPACE); + if (str.size() == 0) { + fprintf(stderr, "line %u: missing value for base_opcode\n", (unsigned) lc); + } else { + setBaseOpcode(atoi(str.c_str())); + } + } else if (token == "encoder_headers") { + std::string str = getNextToken(line, pos, &last, WHITESPACE); + pos = last; + while (str.size() != 0) { + encoderHeaders().push_back(str); + str = getNextToken(line, pos, &last, WHITESPACE); + pos = last; + } + } else if (token == "client_context_headers") { + std::string str = getNextToken(line, pos, &last, WHITESPACE); + pos = last; + while (str.size() != 0) { + clientContextHeaders().push_back(str); + str = getNextToken(line, pos, &last, WHITESPACE); + pos = last; + } + } else if (token == "server_context_headers") { + std::string str = getNextToken(line, pos, &last, WHITESPACE); + pos = last; + while (str.size() != 0) { + serverContextHeaders().push_back(str); + str = getNextToken(line, pos, &last, WHITESPACE); + pos = last; + } + } else if (token == "decoder_headers") { + std::string str = getNextToken(line, pos, &last, WHITESPACE); + pos = last; + while (str.size() != 0) { + decoderHeaders().push_back(str); + str = getNextToken(line, pos, &last, WHITESPACE); + pos = last; + } + } + else { + fprintf(stderr, "WARNING: %u : unknown global attribute %s\n", (unsigned int)lc, line.c_str()); + } + + return 0; +} |