Home | History | Annotate | Download | only in emugen
      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 #include "ApiGen.h"
     17 #include "android/base/EnumFlags.h"
     18 #include "EntryPoint.h"
     19 #include <stdio.h>
     20 #include <stdlib.h>
     21 #include "strUtils.h"
     22 #include <errno.h>
     23 #include <sys/types.h>
     24 
     25 /* Define this to 1 to enable support for the 'isLarge' variable flag
     26  * that instructs the encoder to send large data buffers by a direct
     27  * write through the pipe (i.e. without copying it into a temporary
     28  * buffer. This has definite performance benefits when using a QEMU Pipe.
     29  *
     30  * Set to 0 otherwise.
     31  */
     32 #define WITH_LARGE_SUPPORT  1
     33 
     34 // Set to 1 to ensure buffers passed to/from EGL/GL are properly aligned.
     35 // This prevents crashes with certain backends (e.g. OSMesa).
     36 #define USE_ALIGNED_BUFFERS 1
     37 
     38 // Set these to 1 if you want to instrument either guest's or host's code for
     39 // time-per-call printing.
     40 #define INSTRUMENT_TIMING_GUEST 0
     41 #define INSTRUMENT_TIMING_HOST 0
     42 
     43 // Set to 1 to print to logcat for every GL call encoded.
     44 #define DLOG_ALL_ENCODES 0
     45 
     46 // Set to 1 to check GL errors before and after every decoder call.
     47 #define DECODER_CHECK_GL_ERRORS 0
     48 
     49 EntryPoint * ApiGen::findEntryByName(const std::string & name)
     50 {
     51     EntryPoint * entry = NULL;
     52 
     53     size_t n = this->size();
     54     for (size_t i = 0; i < n; i++) {
     55         if (at(i).name() == name) {
     56             entry = &(at(i));
     57             break;
     58         }
     59     }
     60     return entry;
     61 }
     62 
     63 void ApiGen::printHeader(FILE *fp) const
     64 {
     65     fprintf(fp, "// Generated Code - DO NOT EDIT !!\n");
     66     fprintf(fp, "// generated by 'emugen'\n");
     67 }
     68 
     69 int ApiGen::genProcTypes(const std::string &filename, SideType side)
     70 {
     71     FILE *fp = fopen(filename.c_str(), "wt");
     72     if (fp == NULL) {
     73         perror(filename.c_str());
     74         return -1;
     75     }
     76     printHeader(fp);
     77 
     78     const char* basename = m_basename.c_str();
     79 
     80     fprintf(fp, "#ifndef __%s_%s_proc_t_h\n", basename, sideString(side));
     81     fprintf(fp, "#define __%s_%s_proc_t_h\n", basename, sideString(side));
     82     fprintf(fp, "\n\n");
     83     fprintf(fp, "\n#include \"%s_types.h\"\n",basename);
     84     fprintf(fp, "#ifndef %s_APIENTRY\n",basename);
     85     fprintf(fp, "#define %s_APIENTRY \n",basename);
     86     fprintf(fp, "#endif\n");
     87 
     88 
     89     for (size_t i = 0; i < size(); i++) {
     90         EntryPoint *e = &at(i);
     91 
     92         fprintf(fp, "typedef ");
     93         e->retval().printType(fp);
     94         fprintf(fp, " (%s_APIENTRY *%s_%s_proc_t) (", basename, e->name().c_str(), sideString(side));
     95         if (side == CLIENT_SIDE) { fprintf(fp, "void * ctx"); }
     96         if (e->customDecoder() && side == SERVER_SIDE) { fprintf(fp, "void *ctx"); }
     97 
     98         VarsArray & evars = e->vars();
     99         size_t n = evars.size();
    100 
    101         for (size_t j = 0; j < n; j++) {
    102             if (!evars[j].isVoid()) {
    103                 if (j != 0 || side == CLIENT_SIDE || (side == SERVER_SIDE && e->customDecoder())) fprintf(fp, ", ");
    104                 evars[j].printType(fp);
    105             }
    106         }
    107         fprintf(fp, ");\n");
    108 
    109         if (side == SERVER_SIDE && e->customDecoder() && !e->notApi()) {
    110             fprintf(fp, "typedef ");
    111             e->retval().printType(fp);
    112             fprintf(fp, " (%s_APIENTRY *%s_dec_%s_proc_t) (", basename, e->name().c_str(), sideString(side));
    113 
    114             VarsArray & evars = e->vars();
    115             size_t n = evars.size();
    116 
    117             for (size_t j = 0; j < n; j++) {
    118                 if (!evars[j].isVoid()) {
    119                     if (j != 0) fprintf(fp, ", ");
    120                     evars[j].printType(fp);
    121                 }
    122             }
    123             fprintf(fp, ");\n");
    124         }
    125     }
    126     fprintf(fp, "\n\n#endif\n");
    127     return 0;
    128 }
    129 
    130 int ApiGen::genFuncTable(const std::string &filename, SideType side)
    131 {
    132     FILE *fp = fopen(filename.c_str(), "wt");
    133     if (fp == NULL) {
    134         perror(filename.c_str());
    135         return -1;
    136     }
    137     printHeader(fp);
    138 
    139     fprintf(fp, "#ifndef __%s_%s_ftable_t_h\n", m_basename.c_str(), sideString(side));
    140     fprintf(fp, "#define __%s_%s_ftable_t_h\n", m_basename.c_str(), sideString(side));
    141     fprintf(fp, "\n\n");
    142     fprintf(fp, "static const struct _%s_funcs_by_name {\n", m_basename.c_str());
    143     fprintf(fp,
    144             "\tconst char *name;\n" \
    145             "\tvoid *proc;\n" \
    146             "} %s_funcs_by_name[] = {\n", m_basename.c_str());
    147 
    148 
    149     for (size_t i = 0; i < size(); i++) {
    150         EntryPoint *e = &at(i);
    151         if (e->notApi()) continue;
    152         fprintf(fp, "\t{\"%s\", (void*)%s},\n", e->name().c_str(), e->name().c_str());
    153     }
    154     fprintf(fp, "};\n");
    155     fprintf(fp, "static const int %s_num_funcs = sizeof(%s_funcs_by_name) / sizeof(struct _%s_funcs_by_name);\n",
    156             m_basename.c_str(), m_basename.c_str(), m_basename.c_str());
    157     fprintf(fp, "\n\n#endif\n");
    158     return 0;
    159 }
    160 
    161 int ApiGen::genContext(const std::string & filename, SideType side)
    162 {
    163     FILE *fp = fopen(filename.c_str(), "wt");
    164     if (fp == NULL) {
    165         perror(filename.c_str());
    166         return -1;
    167     }
    168     printHeader(fp);
    169 
    170     fprintf(fp, "#ifndef __%s_%s_context_t_h\n", m_basename.c_str(), sideString(side));
    171     fprintf(fp, "#define __%s_%s_context_t_h\n", m_basename.c_str(), sideString(side));
    172 
    173     fprintf(fp, "\n#include \"%s_%s_proc.h\"\n",
    174             m_basename.c_str(),
    175             side == CLIENT_SIDE ? "client" : "server");
    176     fprintf(fp, "\n#include \"%s_types.h\"\n", m_basename.c_str());
    177 
    178     StringVec & contextHeaders = side == CLIENT_SIDE ? m_clientContextHeaders : m_serverContextHeaders;
    179     for (size_t i = 0; i < contextHeaders.size(); i++) {
    180         fprintf(fp, "#include %s\n", contextHeaders[i].c_str());
    181     }
    182     fprintf(fp, "\n");
    183 
    184     fprintf(fp, "\nstruct %s_%s_context_t {\n\n",
    185             m_basename.c_str(), sideString(side));
    186 
    187     // API entry points
    188     for (size_t i = 0; i < size(); i++) {
    189         EntryPoint *e = &at(i);
    190         if (side == SERVER_SIDE && e->customDecoder() && !e->notApi()) {
    191             fprintf(fp, "\t%s_dec_%s_proc_t %s;\n", e->name().c_str(), sideString(side), e->name().c_str());
    192             fprintf(fp, "\t%s_%s_proc_t %s_dec;\n", e->name().c_str(), sideString(side), e->name().c_str());
    193         } else {
    194             fprintf(fp, "\t%s_%s_proc_t %s;\n", e->name().c_str(), sideString(side), e->name().c_str());
    195         }
    196     }
    197 
    198     // virtual destructor
    199     fprintf(fp, "\tvirtual ~%s_%s_context_t() {}\n", m_basename.c_str(), sideString(side));
    200     // accessor
    201     if (side == CLIENT_SIDE || side == WRAPPER_SIDE) {
    202         fprintf(fp, "\n\ttypedef %s_%s_context_t *CONTEXT_ACCESSOR_TYPE(void);\n",
    203                 m_basename.c_str(), sideString(side));
    204         fprintf(fp, "\tstatic void setContextAccessor(CONTEXT_ACCESSOR_TYPE *f);\n");
    205     }
    206 
    207     // init function
    208     fprintf(fp, "\tint initDispatchByName( void *(*getProc)(const char *name, void *userData), void *userData);\n");
    209 
    210     //client site set error virtual func
    211     if (side == CLIENT_SIDE) {
    212         fprintf(fp, "\tvirtual void setError(unsigned int  error){ (void)error; };\n");
    213         fprintf(fp, "\tvirtual unsigned int getError(){ return 0; };\n");
    214     }
    215 
    216     fprintf(fp, "};\n");
    217 
    218     fprintf(fp, "\n#endif\n");
    219     fclose(fp);
    220     return 0;
    221 }
    222 
    223 int ApiGen::genEntryPoints(const std::string & filename, SideType side)
    224 {
    225 
    226     if (side != CLIENT_SIDE && side != WRAPPER_SIDE) {
    227         fprintf(stderr, "Entry points are only defined for Client and Wrapper components\n");
    228         return -999;
    229     }
    230 
    231 
    232     FILE *fp = fopen(filename.c_str(), "wt");
    233     if (fp == NULL) {
    234         perror(filename.c_str());
    235         return errno;
    236     }
    237 
    238     printHeader(fp);
    239     fprintf(fp, "#include <stdio.h>\n");
    240     fprintf(fp, "#include <stdlib.h>\n");
    241     fprintf(fp, "#include \"%s_%s_context.h\"\n", m_basename.c_str(), sideString(side));
    242     fprintf(fp, "\n");
    243 
    244     fprintf(fp, "extern \"C\" {\n");
    245 
    246     for (size_t i = 0; i < size(); i++) {
    247         fprintf(fp, "\t"); at(i).print(fp, false); fprintf(fp, ";\n");
    248     }
    249     fprintf(fp, "};\n\n");
    250 
    251     fprintf(fp, "#ifndef GET_CONTEXT\n");
    252     fprintf(fp, "static %s_%s_context_t::CONTEXT_ACCESSOR_TYPE *getCurrentContext = NULL;\n",
    253             m_basename.c_str(), sideString(side));
    254 
    255     fprintf(fp,
    256             "void %s_%s_context_t::setContextAccessor(CONTEXT_ACCESSOR_TYPE *f) { getCurrentContext = f; }\n",
    257             m_basename.c_str(), sideString(side));
    258     fprintf(fp, "#define GET_CONTEXT %s_%s_context_t * ctx = getCurrentContext()\n",
    259                 m_basename.c_str(), sideString(side));
    260     fprintf(fp, "#endif\n\n");
    261 
    262 
    263     for (size_t i = 0; i < size(); i++) {
    264         EntryPoint *e = &at(i);
    265         e->print(fp);
    266         fprintf(fp, "{\n");
    267         fprintf(fp, "\tGET_CONTEXT;\n");
    268 
    269         bool shouldReturn = !e->retval().isVoid();
    270         bool shouldCallWithContext = (side == CLIENT_SIDE);
    271         //param check
    272         if (shouldCallWithContext) {
    273             for (size_t j=0; j<e->vars().size(); j++) {
    274                 if (e->vars()[j].paramCheckExpression() != "")
    275                     fprintf(fp, "\t%s\n", e->vars()[j].paramCheckExpression().c_str());
    276             }
    277         }
    278         fprintf(fp, "\t%sctx->%s(%s",
    279                 shouldReturn ? "return " : "",
    280                 e->name().c_str(),
    281                 shouldCallWithContext ? "ctx" : "");
    282         size_t nvars = e->vars().size();
    283 
    284         for (size_t j = 0; j < nvars; j++) {
    285             if (!e->vars()[j].isVoid()) {
    286                 fprintf(fp, "%s %s",
    287                         j != 0 || shouldCallWithContext ? "," : "",
    288                         e->vars()[j].name().c_str());
    289             }
    290         }
    291         fprintf(fp, ");\n");
    292         fprintf(fp, "}\n\n");
    293     }
    294     fclose(fp);
    295     return 0;
    296 }
    297 
    298 
    299 int ApiGen::genOpcodes(const std::string &filename)
    300 {
    301     FILE *fp = fopen(filename.c_str(), "wt");
    302     if (fp == NULL) {
    303         perror(filename.c_str());
    304         return errno;
    305     }
    306 
    307     printHeader(fp);
    308     fprintf(fp, "#ifndef __GUARD_%s_opcodes_h_\n", m_basename.c_str());
    309     fprintf(fp, "#define __GUARD_%s_opcodes_h_\n\n", m_basename.c_str());
    310     for (size_t i = 0; i < size(); i++) {
    311         fprintf(fp, "#define OP_%s \t\t\t\t\t%u\n", at(i).name().c_str(), (unsigned int)i + m_baseOpcode);
    312     }
    313     fprintf(fp, "#define OP_last \t\t\t\t\t%u\n", (unsigned int)size() + m_baseOpcode);
    314     fprintf(fp,"\n\n#endif\n");
    315     fclose(fp);
    316     return 0;
    317 
    318 }
    319 int ApiGen::genAttributesTemplate(const std::string &filename )
    320 {
    321     FILE *fp = fopen(filename.c_str(), "wt");
    322     if (fp == NULL) {
    323         perror(filename.c_str());
    324         return -1;
    325     }
    326 
    327     for (size_t i = 0; i < size(); i++) {
    328         if (at(i).hasPointers()) {
    329             fprintf(fp, "#");
    330             at(i).print(fp);
    331             fprintf(fp, "%s\n\n", at(i).name().c_str());
    332         }
    333     }
    334     fclose(fp);
    335     return 0;
    336 }
    337 
    338 int ApiGen::genEncoderHeader(const std::string &filename)
    339 {
    340     FILE *fp = fopen(filename.c_str(), "wt");
    341     if (fp == NULL) {
    342         perror(filename.c_str());
    343         return -1;
    344     }
    345 
    346     printHeader(fp);
    347     std::string classname = m_basename + "_encoder_context_t";
    348 
    349     fprintf(fp, "\n#ifndef GUARD_%s\n", classname.c_str());
    350     fprintf(fp, "#define GUARD_%s\n\n", classname.c_str());
    351 
    352     fprintf(fp, "#include \"IOStream.h\"\n");
    353     fprintf(fp, "#include \"ChecksumCalculator.h\"\n");
    354     fprintf(fp, "#include \"%s_%s_context.h\"\n\n\n", m_basename.c_str(), sideString(CLIENT_SIDE));
    355 
    356     for (size_t i = 0; i < m_encoderHeaders.size(); i++) {
    357         fprintf(fp, "#include %s\n", m_encoderHeaders[i].c_str());
    358     }
    359     fprintf(fp, "\n");
    360 
    361     fprintf(fp, "struct %s : public %s_%s_context_t {\n\n",
    362             classname.c_str(), m_basename.c_str(), sideString(CLIENT_SIDE));
    363     fprintf(fp, "\tIOStream *m_stream;\n");
    364     fprintf(fp, "\tChecksumCalculator *m_checksumCalculator;\n\n");
    365 
    366     fprintf(fp, "\t%s(IOStream *stream, ChecksumCalculator *checksumCalculator);\n", classname.c_str());
    367     fprintf(fp, "\tvirtual uint64_t lockAndWriteDma(void* data, uint32_t sz) { return 0; }\n");
    368     fprintf(fp, "};\n\n");
    369 
    370     fprintf(fp, "#endif  // GUARD_%s\n", classname.c_str());
    371 
    372     fclose(fp);
    373     return 0;
    374 }
    375 
    376 // Format the byte length expression for a given variable into a user-provided buffer
    377 // If the variable type is not a pointer, this is simply its size as a decimal constant
    378 // If the variable is a pointer, this will be an expression provided by the .attrib file
    379 // through the 'len' attribute.
    380 //
    381 // Returns 1 if the variable is a pointer, 0 otherwise
    382 //
    383 enum class EncodingSizeFlags {
    384     None = 0,
    385     DmaPtrOnly = 1,
    386     ExcludeOut = 2,
    387     UseExistingVar = 4,
    388 };
    389 
    390 static int getVarEncodingSizeExpression(
    391         Var& var, EntryPoint* e, char* buff, size_t bufflen,
    392         EncodingSizeFlags flags)
    393 {
    394     if (!var.isPointer()) {
    395         snprintf(buff, bufflen, "%u", (unsigned int) var.type()->bytes());
    396         return 0;
    397     }
    398 
    399     if ((flags & EncodingSizeFlags::DmaPtrOnly) != 0) {
    400         snprintf(buff, bufflen, "8");
    401     } else if ((flags & EncodingSizeFlags::ExcludeOut) != 0 &&
    402             !(var.pointerDir() & Var::POINTER_IN)) {
    403         snprintf(buff, bufflen, "0");
    404     } else if ((flags & EncodingSizeFlags::UseExistingVar) != 0) {
    405         snprintf(buff, bufflen, "__size_%s", var.name().c_str());
    406     } else {
    407         const char* lenExpr = var.lenExpression().c_str();
    408         const char* varname = var.name().c_str();
    409         if (e != NULL && lenExpr[0] == '\0') {
    410             fprintf(stderr, "%s: data len is undefined for '%s'\n",
    411                     e->name().c_str(), varname);
    412         }
    413         if (var.nullAllowed()) {
    414             snprintf(buff, bufflen, "((%s != NULL) ? %s : 0)", varname, lenExpr);
    415         } else {
    416             snprintf(buff, bufflen, "%s", lenExpr);
    417         }
    418     }
    419     return 1;
    420 }
    421 
    422 static int writeVarEncodingSize(Var& var, bool excludeOutVars, FILE* fp)
    423 {
    424     int ret = 0;
    425     if (!var.isPointer()) {
    426         fprintf(fp, "%u", (unsigned int) var.type()->bytes());
    427     } else {
    428         ret = 1;
    429         if (var.isDMA()) {
    430             fprintf(fp, "8");
    431             return ret;
    432         }
    433 
    434         if (excludeOutVars && !(var.pointerDir() & Var::POINTER_IN)) {
    435             fprintf(fp, "0");
    436         } else {
    437             fprintf(fp, "__size_%s", var.name().c_str());
    438         }
    439     }
    440     return ret;
    441 }
    442 
    443 static void writeVarEncodingExpression(Var& var, FILE* fp)
    444 {
    445     const char* varname = var.name().c_str();
    446 
    447     if (var.isPointer()) {
    448         // encode a pointer header
    449         if (var.isDMA()) {
    450             fprintf(fp, "\t*(uint64_t *)(ptr) = ctx->lockAndWriteDma(%s, __size_%s); ptr += 8;\n", varname, varname);
    451         } else {
    452             fprintf(fp, "\t*(unsigned int *)(ptr) = __size_%s; ptr += 4;\n", varname);
    453 
    454             Var::PointerDir dir = var.pointerDir();
    455             if (dir == Var::POINTER_INOUT || dir == Var::POINTER_IN) {
    456                 if (var.nullAllowed()) {
    457                     fprintf(fp, "\tif (%s != NULL) ", varname);
    458                 } else {
    459                     fprintf(fp, "\t");
    460                 }
    461 
    462                 if (var.packExpression().size() != 0) {
    463                     fprintf(fp, "%s;", var.packExpression().c_str());
    464                 } else {
    465                     fprintf(fp, "memcpy(ptr, %s, __size_%s);",
    466                             varname, varname);
    467                 }
    468 
    469                 fprintf(fp, "ptr += __size_%s;\n", varname);
    470             }
    471         }
    472     } else {
    473         // encode a non pointer variable
    474         if (!var.isVoid()) {
    475             fprintf(fp, "\t\tmemcpy(ptr, &%s, %u); ptr += %u;\n",
    476                     varname,
    477                     (unsigned) var.type()->bytes(),
    478                     (unsigned) var.type()->bytes());
    479         }
    480     }
    481 }
    482 
    483 #if WITH_LARGE_SUPPORT
    484 static void writeVarLargeEncodingExpression(Var& var, FILE* fp)
    485 {
    486     const char* varname = var.name().c_str();
    487 
    488     fprintf(fp, "\tstream->writeFully(&__size_%s,4);\n", varname);
    489     fprintf(fp, "\tif (useChecksum) checksumCalculator->addBuffer(&__size_%s,4);\n", varname);
    490     if (var.nullAllowed()) {
    491         fprintf(fp, "\tif (%s != NULL) {\n", varname);
    492     }
    493     if (var.writeExpression() != "") {
    494         fprintf(fp, "%s", var.writeExpression().c_str());
    495     } else {
    496         fprintf(fp, "\t\tstream->writeFully(%s, __size_%s);\n", varname, varname);
    497         fprintf(fp, "\t\tif (useChecksum) checksumCalculator->addBuffer(%s, __size_%s);\n", varname, varname);
    498     }
    499     if (var.nullAllowed()) fprintf(fp, "\t}\n");
    500 }
    501 #endif /* WITH_LARGE_SUPPORT */
    502 
    503 static void writeEncodingChecksumValidatorOnReturn(const char* funcName, FILE* fp) {
    504     fprintf(fp, "\tif (useChecksum) {\n"
    505                 "\t\tunsigned char *checksumBufPtr = NULL;\n"
    506                 "\t\tunsigned char checksumBuf[ChecksumCalculator::kMaxChecksumSize];\n"
    507                 "\t\tif (checksumSize > 0) checksumBufPtr = &checksumBuf[0];\n"
    508                 "\t\tstream->readback(checksumBufPtr, checksumSize);\n"
    509                 "\t\tif (!checksumCalculator->validate(checksumBufPtr, checksumSize)) {\n"
    510                 "\t\t\tALOGE(\"%s: GL communication error, please report this issue to b.android.com.\\n\");\n"
    511                 "\t\t\tabort();\n"
    512                 "\t\t}\n"
    513                 "\t}\n",
    514             funcName
    515     );
    516 }
    517 
    518 static void addGuestTimePrinting(const EntryPoint* e, bool hasTimeBeforeReadback,
    519                                  FILE* fp) {
    520 #if INSTRUMENT_TIMING_GUEST
    521     fprintf(fp, "\tclock_gettime(CLOCK_REALTIME, &ts1);\n");
    522     fprintf(fp, "\tlong timeDiff = ts1.tv_sec*1000000 + ts1.tv_nsec/1000 - (ts0.tv_sec*1000000 + ts0.tv_nsec/1000);\n");
    523     if (hasTimeBeforeReadback) {
    524         fprintf(fp, "\tlong timeDiff2 = ts1.tv_sec*1000000 + ts1.tv_nsec/1000 - (ts2.tv_sec*1000000 + ts2.tv_nsec/1000);\n");
    525         fprintf(fp, "\tALOGW(\"%s: %%ld (%%ld) us\\n\", timeDiff, timeDiff2);\n", e->name().c_str());
    526     } else {
    527         fprintf(fp, "\tALOGW(\"%s: %%ld us\\n\", timeDiff);\n", e->name().c_str());
    528     }
    529 #endif
    530 }
    531 
    532 int ApiGen::genEncoderImpl(const std::string &filename)
    533 {
    534     FILE *fp = fopen(filename.c_str(), "wt");
    535     if (fp == NULL) {
    536         perror(filename.c_str());
    537         return -1;
    538     }
    539 
    540     printHeader(fp);
    541     fprintf(fp, "\n\n");
    542     fprintf(fp, "#include <string.h>\n");
    543     fprintf(fp, "#include \"%s_opcodes.h\"\n\n", m_basename.c_str());
    544     fprintf(fp, "#include \"%s_enc.h\"\n\n\n", m_basename.c_str());
    545     fprintf(fp, "#include <vector>\n\n");
    546     fprintf(fp, "#include <stdio.h>\n\n");
    547     fprintf(fp, "namespace {\n\n");
    548 
    549     // unsupport printout
    550     fprintf(fp,
    551             "void enc_unsupported()\n"
    552             "{\n"
    553             "\tALOGE(\"Function is unsupported\\n\");\n"
    554             "}\n\n");
    555 
    556     // entry points;
    557     std::string classname = m_basename + "_encoder_context_t";
    558 
    559     size_t n = size();
    560     for (size_t i = 0; i < n; i++) {
    561         EntryPoint *e = &at(i);
    562 
    563         if (e->unsupported()) continue;
    564 
    565         e->print(fp, true, "_enc", /* classname + "::" */"", "void *self");
    566         fprintf(fp, "{\n");
    567 #if DLOG_ALL_ENCODES
    568         fprintf(fp, "ALOGD(\"%%s: enter\", __FUNCTION__);\n");
    569 #endif
    570 
    571 #if INSTRUMENT_TIMING_GUEST
    572         fprintf(fp, "\tstruct timespec ts0, ts1;\n");
    573         fprintf(fp, "\tclock_gettime(CLOCK_REALTIME, &ts0);\n");
    574 #endif
    575 
    576 //      fprintf(fp, "\n\tDBG(\">>>> %s\\n\");\n", e->name().c_str());
    577         fprintf(fp, "\n\t%s *ctx = (%s *)self;\n",
    578                 classname.c_str(),
    579                 classname.c_str());
    580         fprintf(fp, "\tIOStream *stream = ctx->m_stream;\n"
    581                     "\tChecksumCalculator *checksumCalculator = ctx->m_checksumCalculator;\n"
    582                     "\tbool useChecksum = checksumCalculator->getVersion() > 0;\n\n");
    583         VarsArray & evars = e->vars();
    584         size_t  maxvars = evars.size();
    585         size_t  j;
    586 
    587         char    buff[256];
    588 
    589         // Define the __size_XXX variables that contain the size of data
    590         // associated with pointers.
    591         for (j = 0; j < maxvars; j++) {
    592             Var& var = evars[j];
    593 
    594             if (!var.isPointer())
    595                 continue;
    596 
    597             const char* varname = var.name().c_str();
    598             fprintf(fp, "\tconst unsigned int __size_%s = ", varname);
    599 
    600             getVarEncodingSizeExpression(var, e, buff, sizeof(buff),
    601                                          EncodingSizeFlags::None);
    602             fprintf(fp, "%s;\n", buff);
    603         }
    604 
    605         bool hasLargeFields = false;
    606 #if WITH_LARGE_SUPPORT
    607         // We need to take care of 'isLarge' variable in a special way
    608         // Anything before an isLarge variable can be packed into a single
    609         // buffer, which is then commited. Each isLarge variable is a pointer
    610         // to data that can be written to directly through the pipe, which
    611         // will be instant when using a QEMU pipe
    612 
    613         size_t  nvars   = 0;
    614         size_t  npointers = 0;
    615 
    616         // First, compute the total size, 8 bytes for the opcode + payload size (without checksum)
    617         fprintf(fp, "\t unsigned char *ptr;\n");
    618         fprintf(fp, "\t unsigned char *buf;\n");
    619         fprintf(fp, "\t const size_t sizeWithoutChecksum = 8");
    620 
    621         for (j = 0; j < maxvars; j++) {
    622             fprintf(fp, " + ");
    623             npointers += writeVarEncodingSize(evars[j], true, fp);
    624         }
    625         if (npointers > 0) {
    626             fprintf(fp, " + %zu*4", npointers);
    627         }
    628         fprintf(fp, ";\n");
    629 
    630         // Then, size of the checksum string
    631         fprintf(fp, "\t const size_t checksumSize = checksumCalculator->checksumByteSize();\n");
    632 
    633         // And, size of the whole thing
    634         fprintf(fp, "\t const size_t totalSize = sizeWithoutChecksum + checksumSize;\n");
    635 
    636         // We need to divide the packet into fragments. Each fragment contains
    637         // either copied arguments to a temporary buffer, or direct writes for
    638         // large variables.
    639         //
    640         // The first fragment must also contain the opcode+payload_size+checksum_size
    641         //
    642         nvars = 0;
    643         while (nvars < maxvars || maxvars == 0) {
    644 
    645             // Skip over non-large fields
    646             for (j = nvars; j < maxvars; j++) {
    647                 if (evars[j].isLarge())
    648                     break;
    649             }
    650 
    651             // Write a fragment if needed.
    652             if (nvars == 0 || j > nvars) {
    653                 const char* plus = "";
    654 
    655                 if (nvars == 0 && j == maxvars) {
    656                     // Simple shortcut for the common case where we don't have large variables;
    657                     fprintf(fp, "\tbuf = stream->alloc(totalSize);\n");
    658 
    659                 } else {
    660                     hasLargeFields = true;
    661                     // allocate buffer from the stream until the first large variable
    662                     fprintf(fp, "\tbuf = stream->alloc(");
    663                     plus = "";
    664 
    665                     if (nvars == 0) {
    666                         fprintf(fp,"8"); plus = " + ";
    667                     }
    668                     if (j > nvars) {
    669                         npointers = 0;
    670                         for (j = nvars; j < maxvars && !evars[j].isLarge(); j++) {
    671                             fprintf(fp, "%s", plus); plus = " + ";
    672                             npointers += writeVarEncodingSize(evars[j], false, fp);
    673                         }
    674                         if (npointers > 0) {
    675                             fprintf(fp, "%s%zu*4", plus, npointers); plus = " + ";
    676                         }
    677                     }
    678                     fprintf(fp,");\n");
    679                 }
    680                 fprintf(fp, "\tptr = buf;\n");
    681 
    682                 // encode packet header if needed.
    683                 if (nvars == 0) {
    684                     fprintf(fp, "\tint tmp = OP_%s;memcpy(ptr, &tmp, 4); ptr += 4;\n",  e->name().c_str());
    685                     fprintf(fp, "\tmemcpy(ptr, &totalSize, 4);  ptr += 4;\n\n");
    686                 }
    687 
    688                 if (maxvars == 0) {
    689                     fprintf(fp, "\n\tif (useChecksum) checksumCalculator->addBuffer(buf, ptr-buf);\n");
    690                     break;
    691                 }
    692 
    693                 // encode non-large fields in this fragment
    694                 for (j = nvars; j < maxvars && !evars[j].isLarge(); j++) {
    695                     writeVarEncodingExpression(evars[j],fp);
    696                 }
    697 
    698                 fprintf(fp, "\n\tif (useChecksum) checksumCalculator->addBuffer(buf, ptr-buf);\n");
    699                 // Ensure the fragment is commited if it is followed by a large variable
    700                 if (j < maxvars) {
    701                     fprintf(fp, "\tstream->flush();\n");
    702                 }
    703             }
    704 
    705             // If we have one or more large variables, write them directly.
    706             // As size + data
    707             for ( ; j < maxvars && evars[j].isLarge(); j++) {
    708                 writeVarLargeEncodingExpression(evars[j], fp);
    709             }
    710 
    711             nvars = j;
    712         }
    713 
    714 #else /* !WITH_LARGE_SUPPORT */
    715         size_t nvars = evars.size();
    716         size_t npointers = 0;
    717         fprintf(fp, "\tunsigned char *ptr;\n");
    718         fprintf(fp, "\tunsigned char *buf;\n");
    719         fprintf(fp, "\tconst size_t sizeWithoutChecksum = 8");
    720         for (size_t j = 0; j < nvars; j++) {
    721             npointers += getVarEncodingSizeExpression(
    722                     evars[j], e, buff, sizeof(buff),
    723                     (evars[j].isDMA() ? EncodingSizeFlags::DmaPtrOnly
    724                                       : EncodingSizeFlags::None) |
    725                             EncodingSizeFlags::UseExistingVar |
    726                             EncodingSizeFlags::ExcludeOut);
    727             fprintf(fp, " + %s", buff);
    728         }
    729         fprintf(fp, " + %u * 4;\n", (unsigned int)npointers);
    730         // Size of checksum
    731         fprintf(fp, "\t const size_t checksumSize = checksumCalculator->checksumByteSize();\n");
    732         // Size of the whole thing
    733         fprintf(fp, "\t const size_t totalSize = sizeWithoutChecksum + checksumSize;\n");
    734 
    735         // allocate buffer from the stream;
    736         fprintf(fp, "\tptr = buf = stream->alloc(totalSize);\n\n");
    737 
    738         // encode into the stream;
    739         fprintf(fp, "\tint tmp = OP_%s; memcpy(ptr, &tmp, 4); ptr += 4;\n", e->name().c_str());
    740         fprintf(fp, "\tmemcpy(ptr, &totalSize, 4);  ptr += 4;\n\n");
    741 
    742         // out variables
    743         for (size_t j = 0; j < nvars; j++) {
    744             writeVarEncodingExpression(evars[j], fp);
    745         }
    746 
    747         fprintf(fp, "\tif (useChecksum) checksumCalculator->addBuffer(buf, ptr - buf);\n");
    748 #endif /* !WITH_LARGE_SUPPORT */
    749 
    750         // checksum
    751         if (hasLargeFields) {
    752             fprintf(fp, "\tbuf = stream->alloc(checksumSize);\n");
    753             fprintf(fp, "\tif (useChecksum) checksumCalculator->writeChecksum(buf, checksumSize);\n\n");
    754         } else {
    755             fprintf(fp, "\tif (useChecksum) checksumCalculator->writeChecksum(ptr, checksumSize); ptr += checksumSize;\n\n");
    756         }
    757 
    758         // in variables;
    759         bool hasTimeBeforeReadback = false;
    760         bool hasReadbackChecksum = false;
    761         for (size_t j = 0; j < nvars; j++) {
    762             if (evars[j].isPointer()) {
    763                 Var::PointerDir dir = evars[j].pointerDir();
    764                 if (dir == Var::POINTER_INOUT || dir == Var::POINTER_OUT) {
    765                     const char* varname = evars[j].name().c_str();
    766                     const char* indent = "\t";
    767 
    768 #if INSTRUMENT_TIMING_GUEST
    769                     if (!hasTimeBeforeReadback) {
    770                         hasTimeBeforeReadback = true;
    771                         // Let's flush the stream before measuring the time.
    772                         fprintf(fp, "\tstream->flush();\n");
    773                         fprintf(fp, "\tstruct timespec ts2;\n");
    774                         fprintf(fp, "\tclock_gettime(CLOCK_REALTIME, &ts2);\n");
    775                     }
    776 #endif
    777                     if (evars[j].nullAllowed()) {
    778                         fprintf(fp, "\tif (%s != NULL) {\n",varname);
    779                         indent = "\t\t";
    780                     }
    781 
    782                     if (evars[j].guestUnpackExpression() != "") {
    783                         fprintf(fp, "%s%s;\n", indent, evars[j].guestUnpackExpression().c_str());
    784                     } else {
    785                         fprintf(fp, "%sstream->readback(%s, __size_%s);\n",
    786                                 indent, varname, varname);
    787                     }
    788                     fprintf(fp, "%sif (useChecksum) checksumCalculator->addBuffer(%s, __size_%s);\n",
    789                             indent, varname, varname);
    790                     if (evars[j].nullAllowed()) {
    791                         fprintf(fp, "\t}\n");
    792                     }
    793                     hasReadbackChecksum = true;
    794                 }
    795             }
    796         }
    797 //XXX       fprintf(fp, "\n\tDBG(\"<<<< %s\\n\");\n", e->name().c_str());
    798 
    799         // todo - return value for pointers
    800         if (e->retval().isPointer()) {
    801             fprintf(stderr, "WARNING: %s : return value of pointer is unsupported\n",
    802                     e->name().c_str());
    803             if (e->flushOnEncode()) {
    804                 fprintf(fp, "\tstream->flush();\n");
    805             }
    806             addGuestTimePrinting(e, hasTimeBeforeReadback, fp);
    807             fprintf(fp, "\t return NULL;\n");
    808         } else if (e->retval().type()->name() != "void") {
    809 #if INSTRUMENT_TIMING_GUEST
    810             if (!hasTimeBeforeReadback) {
    811                 hasTimeBeforeReadback = true;
    812                 fprintf(fp, "\tstream->flush();\n");
    813                 fprintf(fp, "\tstruct timespec ts2;\n");
    814                 fprintf(fp, "\tclock_gettime(CLOCK_REALTIME, &ts2);\n");
    815             }
    816 #endif
    817 
    818             fprintf(fp, "\n\t%s retval;\n", e->retval().type()->name().c_str());
    819             fprintf(fp, "\tstream->readback(&retval, %u);\n",(unsigned) e->retval().type()->bytes());
    820             fprintf(fp, "\tif (useChecksum) checksumCalculator->addBuffer(&retval, %u);\n",
    821                     (unsigned) e->retval().type()->bytes());
    822             writeEncodingChecksumValidatorOnReturn(e->name().c_str(), fp);
    823             addGuestTimePrinting(e, hasTimeBeforeReadback, fp);
    824             fprintf(fp, "\treturn retval;\n");
    825         } else {
    826             if (e->flushOnEncode()) fprintf(fp, "\tstream->flush();\n");
    827             if (hasReadbackChecksum) writeEncodingChecksumValidatorOnReturn(e->name().c_str(), fp);
    828             addGuestTimePrinting(e, hasTimeBeforeReadback, fp);
    829         }
    830         fprintf(fp, "}\n\n");
    831     }
    832 
    833     fprintf(fp, "}  // namespace\n\n");
    834 
    835     // constructor
    836     fprintf(fp, "%s::%s(IOStream *stream, ChecksumCalculator *checksumCalculator)\n{\n", classname.c_str(), classname.c_str());
    837     fprintf(fp, "\tm_stream = stream;\n");
    838     fprintf(fp, "\tm_checksumCalculator = checksumCalculator;\n\n");
    839 
    840     for (size_t i = 0; i < n; i++) {
    841         EntryPoint *e = &at(i);
    842         if (e->unsupported()) {
    843             fprintf(fp,
    844                     "\tthis->%s = (%s_%s_proc_t) &enc_unsupported;\n",
    845                     e->name().c_str(),
    846                     e->name().c_str(),
    847                     sideString(CLIENT_SIDE));
    848         } else {
    849             fprintf(fp,
    850                     "\tthis->%s = &%s_enc;\n",
    851                     e->name().c_str(),
    852                     e->name().c_str());
    853         }
    854     }
    855     fprintf(fp, "}\n\n");
    856 
    857     fclose(fp);
    858     return 0;
    859 }
    860 
    861 
    862 int ApiGen::genDecoderHeader(const std::string &filename)
    863 {
    864     FILE *fp = fopen(filename.c_str(), "wt");
    865     if (fp == NULL) {
    866         perror(filename.c_str());
    867         return -1;
    868     }
    869 
    870     printHeader(fp);
    871     std::string classname = m_basename + "_decoder_context_t";
    872 
    873     fprintf(fp, "\n#ifndef GUARD_%s\n", classname.c_str());
    874     fprintf(fp, "#define GUARD_%s\n\n", classname.c_str());
    875 
    876     fprintf(fp, "#include \"OpenglRender/IOStream.h\"\n");
    877     fprintf(fp, "#include \"ChecksumCalculator.h\"\n");
    878     fprintf(fp, "#include \"%s_%s_context.h\"\n\n\n", m_basename.c_str(), sideString(SERVER_SIDE));
    879     fprintf(fp, "#include \"emugl/common/logging.h\"\n");
    880 #if INSTRUMENT_TIMING_HOST
    881     fprintf(fp, "#include \"time.h\"\n");
    882 #endif
    883 
    884     for (size_t i = 0; i < m_decoderHeaders.size(); i++) {
    885         fprintf(fp, "#include %s\n", m_decoderHeaders[i].c_str());
    886     }
    887     fprintf(fp, "\n");
    888 
    889     fprintf(fp, "struct %s : public %s_%s_context_t {\n\n",
    890             classname.c_str(), m_basename.c_str(), sideString(SERVER_SIDE));
    891     fprintf(fp, "\tsize_t decode(void *buf, size_t bufsize, IOStream *stream, ChecksumCalculator* checksumCalc);\n");
    892     fprintf(fp, "\n};\n\n");
    893     fprintf(fp, "#endif  // GUARD_%s\n", classname.c_str());
    894 
    895     fclose(fp);
    896     return 0;
    897 }
    898 
    899 int ApiGen::genContextImpl(const std::string &filename, SideType side)
    900 {
    901     FILE *fp = fopen(filename.c_str(), "wt");
    902     if (fp == NULL) {
    903         perror(filename.c_str());
    904         return -1;
    905     }
    906     printHeader(fp);
    907 
    908     std::string classname = m_basename + "_" + sideString(side) + "_context_t";
    909     size_t n = size();
    910     fprintf(fp, "\n\n#include <string.h>\n");
    911     fprintf(fp, "#include \"%s_%s_context.h\"\n\n\n", m_basename.c_str(), sideString(side));
    912     fprintf(fp, "#include <stdio.h>\n\n");
    913 
    914     fprintf(fp, "int %s::initDispatchByName(void *(*getProc)(const char *, void *userData), void *userData)\n{\n", classname.c_str());
    915     for (size_t i = 0; i < n; i++) {
    916         EntryPoint *e = &at(i);
    917         if (side == SERVER_SIDE && e->customDecoder() && !e->notApi()) {
    918             fprintf(fp, "\t%s = (%s_dec_%s_proc_t) getProc(\"%s\", userData);\n",
    919                     e->name().c_str(),
    920                     e->name().c_str(),
    921                     sideString(side),
    922                     e->name().c_str());
    923         } else {
    924             fprintf(fp, "\t%s = (%s_%s_proc_t) getProc(\"%s\", userData);\n",
    925                     e->name().c_str(),
    926                     e->name().c_str(),
    927                     sideString(side),
    928                     e->name().c_str());
    929         }
    930     }
    931     fprintf(fp, "\treturn 0;\n");
    932     fprintf(fp, "}\n\n");
    933     fclose(fp);
    934     return 0;
    935 }
    936 
    937 int ApiGen::genDecoderImpl(const std::string &filename)
    938 {
    939     FILE *fp = fopen(filename.c_str(), "wt");
    940     if (fp == NULL) {
    941         perror(filename.c_str());
    942         return -1;
    943     }
    944 
    945     printHeader(fp);
    946 
    947     std::string classname = m_basename + "_decoder_context_t";
    948 
    949     size_t n = size();
    950 
    951     bool changesChecksum = false;
    952     for (size_t i = 0; i < size(); ++i) {
    953         const EntryPoint& ep = at(i);
    954         if (ep.name().find("SelectChecksum") != std::string::npos) {
    955             changesChecksum = true;
    956             break;
    957         }
    958     }
    959 
    960     fprintf(fp, "\n\n#include <string.h>\n");
    961     fprintf(fp, "#include \"%s_opcodes.h\"\n\n", m_basename.c_str());
    962     fprintf(fp, "#include \"%s_dec.h\"\n\n\n", m_basename.c_str());
    963     fprintf(fp, "#include \"ProtocolUtils.h\"\n\n");
    964     fprintf(fp, "#include \"ChecksumCalculatorThreadInfo.h\"\n\n");
    965     fprintf(fp, "#include <stdio.h>\n\n");
    966     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");
    967 
    968     // helper macros
    969     fprintf(fp,
    970             "#ifdef OPENGL_DEBUG_PRINTOUT\n"
    971             "#  define DEBUG(...) do { if (emugl_cxt_logger) { emugl_cxt_logger(__VA_ARGS__); } } while(0)\n"
    972             "#else\n"
    973             "#  define DEBUG(...)  ((void)0)\n"
    974             "#endif\n\n");
    975 
    976     fprintf(fp,
    977 #if DECODER_CHECK_GL_ERRORS
    978             "#define CHECK_GL_ERRORS\n"
    979 #endif
    980             "#ifdef CHECK_GL_ERRORS\n"
    981             "#  define SET_LASTCALL(name)  sprintf(lastCall, #name)\n"
    982             "#else\n"
    983             "#  define SET_LASTCALL(name)\n"
    984             "#endif\n");
    985 
    986     // helper templates
    987     fprintf(fp, "using namespace emugl;\n\n");
    988 
    989     // decoder switch;
    990     fprintf(fp, "size_t %s::decode(void *buf, size_t len, IOStream *stream, ChecksumCalculator* checksumCalc) {\n", classname.c_str());
    991     fprintf(fp,
    992 "\tif (len < 8) return 0; \n\
    993 #ifdef CHECK_GL_ERRORS\n\
    994 \tchar lastCall[256] = {0};\n\
    995 #endif\n\
    996 \tunsigned char *ptr = (unsigned char *)buf;\n\
    997 \tconst unsigned char* const end = (const unsigned char*)buf + len;\n");
    998     if (!changesChecksum) {
    999         fprintf(fp,
   1000 R"(    const size_t checksumSize = checksumCalc->checksumByteSize();
   1001     const bool useChecksum = checksumSize > 0;
   1002 )");
   1003     }
   1004     fprintf(fp,
   1005 "\twhile (end - ptr >= 8) {\n\
   1006 \t\tuint32_t opcode = *(uint32_t *)ptr;   \n\
   1007 \t\tint32_t packetLen = *(int32_t *)(ptr + 4);\n\
   1008 \t\tif (end - ptr < packetLen) return ptr - (unsigned char*)buf;\n");
   1009     if (changesChecksum) {
   1010         fprintf(fp,
   1011 R"(        // Do this on every iteration, as some commands may change the checksum
   1012         // calculation parameters.
   1013         const size_t checksumSize = checksumCalc->checksumByteSize();
   1014         const bool useChecksum = checksumSize > 0;
   1015 )");
   1016     }
   1017     fprintf(fp, "\t\tswitch(opcode) {\n");
   1018 
   1019     for (size_t f = 0; f < n; f++) {
   1020         enum Pass_t {
   1021             PASS_FIRST = 0,
   1022             PASS_VariableDeclarations = PASS_FIRST,
   1023             PASS_Protocol,
   1024             PASS_TmpBuffAlloc,
   1025             PASS_MemAlloc,
   1026             PASS_DebugPrint,
   1027             PASS_FunctionCall,
   1028             PASS_FlushOutput,
   1029             PASS_Epilog,
   1030             PASS_LAST };
   1031         EntryPoint *e = &(*this)[f];
   1032 
   1033         // construct a printout string;
   1034         std::string printString;
   1035         for (size_t i = 0; i < e->vars().size(); i++) {
   1036             Var *v = &e->vars()[i];
   1037             if (!v->isVoid())  printString += (v->isPointer() ? "%p(%u)" : v->type()->printFormat()) + " ";
   1038         }
   1039 
   1040         // TODO - add for return value;
   1041         fprintf(fp, "\t\tcase OP_%s: {\n", e->name().c_str());
   1042 
   1043 #if INSTRUMENT_TIMING_HOST
   1044         fprintf(fp, "\t\t\tstruct timespec ts0, ts1, ts2;\n");
   1045         fprintf(fp, "\t\t\tclock_gettime(CLOCK_REALTIME, &ts0);\n");
   1046 #endif
   1047         bool totalTmpBuffExist = false;
   1048         std::string totalTmpBuffOffset = "0";
   1049         std::string *tmpBufOffset = new std::string[e->vars().size()];
   1050 
   1051         // construct retval type string
   1052         std::string retvalType;
   1053         if (!e->retval().isVoid()) {
   1054             retvalType = e->retval().type()->name();
   1055         }
   1056 
   1057         for (int pass = PASS_FIRST; pass < PASS_LAST; pass++) {
   1058 #if INSTRUMENT_TIMING_HOST
   1059             if (pass == PASS_FunctionCall) {
   1060                 fprintf(fp, "\t\t\tclock_gettime(CLOCK_REALTIME, &ts2);\n");
   1061             }
   1062 #endif
   1063             if (pass == PASS_FunctionCall &&
   1064                 !e->retval().isVoid() &&
   1065                 !e->retval().isPointer()) {
   1066                 fprintf(fp, "\t\t\t*(%s *)(&tmpBuf[%s]) = ", retvalType.c_str(),
   1067                         totalTmpBuffOffset.c_str());
   1068             }
   1069 
   1070             if (pass == PASS_FunctionCall) {
   1071                 if (e->customDecoder() && !e->notApi()) {
   1072                     fprintf(fp, "\t\t\tthis->%s_dec(", e->name().c_str());
   1073                 } else {
   1074                     fprintf(fp, "\t\t\tthis->%s(", e->name().c_str());
   1075                 }
   1076                 if (e->customDecoder()) {
   1077                     fprintf(fp, "this"); // add a context to the call
   1078                 }
   1079             } else if (pass == PASS_DebugPrint) {
   1080                 if (strstr(m_basename.c_str(), "gl")) {
   1081                     fprintf(fp, "\t\t#ifdef CHECK_GL_ERRORS\n");
   1082                     fprintf(fp, "\t\tGLint err = this->glGetError();\n");
   1083                     fprintf(fp, "\t\tif (err) fprintf(stderr, \"%s Error (pre-call): 0x%%X before %s\\n\", err);\n",
   1084                             m_basename.c_str(), e->name().c_str());
   1085                     fprintf(fp, "\t\t#endif\n");
   1086                 }
   1087                 fprintf(fp,
   1088                         "\t\t\tDEBUG(\"%s(%%p): %s(%s)\\n\", stream",
   1089                         m_basename.c_str(),
   1090                         e->name().c_str(),
   1091                         printString.c_str());
   1092                 if (e->vars().size() > 0 && !e->vars()[0].isVoid()) {
   1093                     fprintf(fp, ", ");
   1094                 }
   1095             }
   1096 
   1097             std::string varoffset = "8"; // skip the header
   1098             VarsArray & evars = e->vars();
   1099             // allocate memory for out pointers;
   1100             for (size_t j = 0; j < evars.size(); j++) {
   1101                 Var *v = & evars[j];
   1102                 if (v->isVoid()) {
   1103                     continue;
   1104                 }
   1105                 const char* var_name = v->name().c_str();
   1106                 const char* var_type_name = v->type()->name().c_str();
   1107                 const unsigned var_type_bytes = v->type()->bytes();
   1108 
   1109                 if ((pass == PASS_FunctionCall) &&
   1110                     (j != 0 || e->customDecoder())) {
   1111                     fprintf(fp, ", ");
   1112                 }
   1113                 if (pass == PASS_DebugPrint && j != 0) {
   1114                     fprintf(fp, ", ");
   1115                 }
   1116 
   1117                 if (v->isPointer() && v->isDMA()) {
   1118                     if (pass == PASS_VariableDeclarations) {
   1119                         fprintf(fp,
   1120                                 "\t\t\tuint64_t var_%s_guest_paddr = Unpack<uint64_t,uint64_t>(ptr + %s);\n"
   1121                                 "\t\t\t%s var_%s = stream->getDmaForReading(var_%s_guest_paddr);\n",
   1122                                 var_name,
   1123                                 varoffset.c_str(),
   1124                                 var_type_name,
   1125                                 var_name,
   1126                                 var_name);
   1127                     }
   1128                     if (pass == PASS_FunctionCall ||
   1129                         pass == PASS_DebugPrint) {
   1130                         fprintf(fp, "var_%s", var_name);
   1131                     }
   1132                     varoffset += " + 8";
   1133                 }
   1134 
   1135                 if (!v->isPointer()) {
   1136                     if (pass == PASS_VariableDeclarations) {
   1137                         fprintf(fp,
   1138                                 "\t\t\t%s var_%s = Unpack<%s,uint%u_t>(ptr + %s);\n",
   1139                                 var_type_name,
   1140                                 var_name,
   1141                                 var_type_name,
   1142                                 var_type_bytes * 8U,
   1143                                 varoffset.c_str());
   1144                     }
   1145 
   1146                     if (pass == PASS_FunctionCall ||
   1147                         pass == PASS_DebugPrint) {
   1148                         fprintf(fp, "var_%s", var_name);
   1149                     }
   1150                     varoffset += " + " + toString(var_type_bytes);
   1151                     continue;
   1152                 }
   1153 
   1154                 if (pass == PASS_VariableDeclarations) {
   1155                     fprintf(fp,
   1156                             "\t\t\tuint32_t size_%s __attribute__((unused)) = Unpack<uint32_t,uint32_t>(ptr + %s);\n",
   1157                             var_name,
   1158                             varoffset.c_str());
   1159                 }
   1160 
   1161                 if (!v->isDMA()) {
   1162                     if (v->pointerDir() & Var::POINTER_IN) {
   1163                         if (pass == PASS_VariableDeclarations) {
   1164     #if USE_ALIGNED_BUFFERS
   1165                             fprintf(fp,
   1166                                     "\t\t\tInputBuffer inptr_%s(ptr + %s + 4, size_%s);\n",
   1167                                     var_name,
   1168                                     varoffset.c_str(),
   1169                                     var_name);
   1170                             if (v->unpackExpression().size() > 0) {
   1171                                 fprintf(fp,
   1172                                     "\t\t\tvoid* inptr_%s_unpacked;\n"
   1173                                     "\t\t\t%s;\n",
   1174                                     var_name,
   1175                                     v->unpackExpression().c_str());
   1176                             }
   1177 
   1178                         }
   1179                         if (pass == PASS_FunctionCall &&
   1180                             v->pointerDir() == Var::POINTER_IN) {
   1181                             if (v->nullAllowed()) {
   1182                                 fprintf(fp,
   1183                                         "size_%s == 0 ? nullptr : (%s)(inptr_%s.get())",
   1184                                         var_name,
   1185                                         var_type_name,
   1186                                         var_name);
   1187                             } else {
   1188                                 if (v->unpackExpression().size() > 0) {
   1189                                     fprintf(fp,
   1190                                             "(%s)(inptr_%s_unpacked)",
   1191                                             var_type_name,
   1192                                             var_name);
   1193                                 } else {
   1194                                     fprintf(fp,
   1195                                             "(%s)(inptr_%s.get())",
   1196                                             var_type_name,
   1197                                             var_name);
   1198                                 }
   1199                             }
   1200                         } else if (pass == PASS_DebugPrint &&
   1201                                    v->pointerDir() == Var::POINTER_IN) {
   1202                             fprintf(fp,
   1203                                     "(%s)(inptr_%s.get()), size_%s",
   1204                                     var_type_name,
   1205                                     var_name,
   1206                                     var_name);
   1207                         }
   1208     #else  // !USE_ALIGNED_BUFFERS
   1209                             fprintf(fp,
   1210                                     "unsigned char *inptr_%s = (ptr + %s + 4);\n",
   1211                                     var_name,
   1212                                     varoffset.c_str());
   1213                         }
   1214                         if (pass == PASS_FunctionCall &&
   1215                             v->pointerDir() == Var::POINTER_IN) {
   1216                             if (v->nullAllowed()) {
   1217                                 fprintf(fp,
   1218                                         "size_%s == 0 ? NULL : (%s)(inptr_%s)",
   1219                                         var_name,
   1220                                         var_type_name,
   1221                                         var_name);
   1222                             } else {
   1223                                 fprintf(fp,
   1224                                         "(%s)(inptr_%s)",
   1225                                         var_type_name,
   1226                                         var_name);
   1227                             }
   1228                         } else if (pass == PASS_DebugPrint &&
   1229                                    v->pointerDir() == Var::POINTER_IN) {
   1230                             fprintf(fp,
   1231                                     "(%s)(inptr_%s), size_%s",
   1232                                     var_type_name,
   1233                                     var_name,
   1234                                     var_name);
   1235                         }
   1236     #endif  // !USE_ALIGNED_BUFFERS
   1237                         varoffset += " + 4 + size_";
   1238                         varoffset += var_name;
   1239                     }
   1240                     if (v->pointerDir() & Var::POINTER_OUT)  { // out pointer;
   1241                         if (pass == PASS_TmpBuffAlloc) {
   1242                             if (!totalTmpBuffExist) {
   1243                                 fprintf(fp,
   1244                                         "\t\t\tsize_t totalTmpSize = size_%s;\n",
   1245                                         var_name);
   1246                             } else {
   1247                                 fprintf(fp,
   1248                                         "\t\t\ttotalTmpSize += size_%s;\n",
   1249                                         var_name);
   1250                             }
   1251                             tmpBufOffset[j] = totalTmpBuffOffset;
   1252                             totalTmpBuffOffset += " + size_";
   1253                             totalTmpBuffOffset += var_name;
   1254                             totalTmpBuffExist = true;
   1255                         } else if (pass == PASS_MemAlloc) {
   1256     #if USE_ALIGNED_BUFFERS
   1257                             fprintf(fp,
   1258                                     "\t\t\tOutputBuffer outptr_%s(&tmpBuf[%s], size_%s);\n",
   1259                                     var_name,
   1260                                     tmpBufOffset[j].c_str(),
   1261                                     var_name);
   1262                             // If both input and output variable, initialize with the input.
   1263                             if (v->pointerDir() == Var::POINTER_INOUT) {
   1264                                 fprintf(fp,
   1265                                         "\t\t\tmemcpy(outptr_%s.get(), inptr_%s.get(), size_%s);\n",
   1266                                         var_name,
   1267                                         var_name,
   1268                                         var_name);
   1269                             }
   1270 
   1271                             if (v->hostPackExpression() != "") {
   1272                                 fprintf(fp, "\t\t\tvoid* forPacking_%s = nullptr;\n", var_name);
   1273                             }
   1274                             if (v->hostPackTmpAllocExpression() != "") {
   1275                                 fprintf(fp, "\t\t\t%s;\n", v->hostPackTmpAllocExpression().c_str());
   1276                             }
   1277                         } else if (pass == PASS_FunctionCall) {
   1278                             if (v->hostPackExpression() != "") {
   1279                                 fprintf(fp,
   1280                                         "(%s)(forPacking_%s)",
   1281                                         var_type_name,
   1282                                         var_name);
   1283                             } else {
   1284                                 if (v->nullAllowed()) {
   1285                                     fprintf(fp,
   1286                                             "size_%s == 0 ? nullptr : (%s)(outptr_%s.get())",
   1287                                             var_name,
   1288                                             var_type_name,
   1289                                             var_name);
   1290                                 } else {
   1291                                     fprintf(fp,
   1292                                             "(%s)(outptr_%s.get())",
   1293                                             var_type_name,
   1294                                             var_name);
   1295                                 }
   1296                             }
   1297                         } else if (pass == PASS_DebugPrint) {
   1298                             fprintf(fp,
   1299                                     "(%s)(outptr_%s.get()), size_%s",
   1300                                     var_type_name,
   1301                                     var_name,
   1302                                     var_name);
   1303                         }
   1304                         if (pass == PASS_FlushOutput) {
   1305                             if (v->hostPackExpression() != "") {
   1306                                 fprintf(fp,
   1307                                         "\t\t\tif (size_%s) {\n"
   1308                                         "\t\t\t%s; }\n",
   1309                                         var_name,
   1310                                         v->hostPackExpression().c_str());
   1311                             }
   1312                             fprintf(fp,
   1313                                     "\t\t\toutptr_%s.flush();\n",
   1314                                     var_name);
   1315                         }
   1316     #else  // !USE_ALIGNED_BUFFERS
   1317                             fprintf(fp,
   1318                                     "\t\t\tunsigned char *outptr_%s = &tmpBuf[%s];\n",
   1319                                     var_name,
   1320                                     tmpBufOffset[j].c_str());
   1321                             fprintf(fp,
   1322                                     "\t\t\tmemset(outptr_%s, 0, %s);\n",
   1323                                     var_name,
   1324                                     toString(v->type()->bytes()).c_str());
   1325                         } else if (pass == PASS_FunctionCall) {
   1326                             if (v->nullAllowed()) {
   1327                                 fprintf(fp,
   1328                                         "size_%s == 0 ? NULL : (%s)(outptr_%s)",
   1329                                         var_name,
   1330                                         var_type_name,
   1331                                         var_name);
   1332                             } else {
   1333                                 fprintf(fp,
   1334                                         "(%s)(outptr_%s)",
   1335                                         var_type_name,
   1336                                         var_name);
   1337                             }
   1338                         } else if (pass == PASS_DebugPrint) {
   1339                             fprintf(fp,
   1340                                     "(%s)(outptr_%s), size_%s",
   1341                                     var_type_name,
   1342                                     var_name,
   1343                                     varoffset.c_str());
   1344                         }
   1345     #endif  // !USE_ALIGNED_BUFFERS
   1346                     if (v->pointerDir() == Var::POINTER_OUT) {
   1347                         varoffset += " + 4";
   1348                     }
   1349                     }
   1350                 }
   1351             }
   1352 
   1353             if (pass == PASS_Protocol) {
   1354                 fprintf(fp,
   1355                         "\t\t\tif (useChecksum) {\n"
   1356                         "\t\t\t\tChecksumCalculatorThreadInfo::validOrDie(checksumCalc, ptr, %s, "
   1357                         "ptr + %s, checksumSize, "
   1358                         "\n\t\t\t\t\t\"%s::decode,"
   1359                         " OP_%s: GL checksumCalculator failure\\n\");\n"
   1360                         "\t\t\t}\n",
   1361                         varoffset.c_str(),
   1362                         varoffset.c_str(),
   1363                         classname.c_str(),
   1364                         e->name().c_str()
   1365                         );
   1366 
   1367                 varoffset += " + 4";
   1368             }
   1369 
   1370             if (pass == PASS_FunctionCall ||
   1371                 pass == PASS_DebugPrint) {
   1372                 fprintf(fp, ");\n");
   1373 
   1374                 if (pass == PASS_FunctionCall) {
   1375                     // unlock all dma buffers that have been passed
   1376                     for (size_t j = 0; j < evars.size(); j++) {
   1377                         Var *v = & evars[j];
   1378                         if (v->isVoid()) {
   1379                             continue;
   1380                         }
   1381                         const char* var_name = v->name().c_str();
   1382                         if (v->isDMA()) {
   1383                             fprintf(fp,
   1384                                     "\t\t\tstream->unlockDma(var_%s_guest_paddr);\n",
   1385                                     var_name);
   1386                         }
   1387                     }
   1388                 }
   1389             }
   1390 
   1391             if (pass == PASS_TmpBuffAlloc) {
   1392                 if (!e->retval().isVoid() && !e->retval().isPointer()) {
   1393                     if (!totalTmpBuffExist)
   1394                         fprintf(fp,
   1395                                 "\t\t\tsize_t totalTmpSize = sizeof(%s);\n",
   1396                                 retvalType.c_str());
   1397                     else
   1398                         fprintf(fp,
   1399                                 "\t\t\ttotalTmpSize += sizeof(%s);\n",
   1400                                 retvalType.c_str());
   1401 
   1402                     totalTmpBuffExist = true;
   1403                 }
   1404                 if (totalTmpBuffExist) {
   1405                     fprintf(fp,
   1406                             "\t\t\ttotalTmpSize += checksumSize;\n"
   1407                             "\t\t\tunsigned char *tmpBuf = stream->alloc(totalTmpSize);\n");
   1408                 }
   1409             }
   1410 
   1411             if (pass == PASS_Epilog) {
   1412                 // send back out pointers data as well as retval
   1413                 if (totalTmpBuffExist) {
   1414                     fprintf(fp,
   1415                             "\t\t\tif (useChecksum) {\n"
   1416                             "\t\t\t\tChecksumCalculatorThreadInfo::writeChecksum(checksumCalc, "
   1417                             "&tmpBuf[0], totalTmpSize - checksumSize, "
   1418                             "&tmpBuf[totalTmpSize - checksumSize], checksumSize);\n"
   1419                             "\t\t\t}\n"
   1420                             "\t\t\tstream->flush();\n");
   1421                 }
   1422             }
   1423         } // pass;
   1424 
   1425 #if INSTRUMENT_TIMING_HOST
   1426         fprintf(fp, "\t\t\tclock_gettime(CLOCK_REALTIME, &ts1);\n");
   1427         fprintf(fp, "\t\t\tlong timeDiff = ts1.tv_sec*1000000 + ts1.tv_nsec/1000 - (ts0.tv_sec*1000000 + ts0.tv_nsec/1000);\n");
   1428         fprintf(fp, "\t\t\tlong timeDiff2 = ts1.tv_sec*1000000 + ts1.tv_nsec/1000 - (ts2.tv_sec*1000000 + ts2.tv_nsec/1000);\n");
   1429         fprintf(fp, "\t\t\tprintf(\"(timing) %%4ld.%%06ld %s: %%ld (%%ld) us\\n\", "
   1430                     "ts1.tv_sec, ts1.tv_nsec/1000, timeDiff, timeDiff2);\n", e->name().c_str());
   1431 #endif
   1432         fprintf(fp, "\t\t\tSET_LASTCALL(\"%s\");\n", e->name().c_str());
   1433         fprintf(fp, "\t\t\tbreak;\n");
   1434         fprintf(fp, "\t\t}\n");
   1435 
   1436         delete [] tmpBufOffset;
   1437     }
   1438     fprintf(fp, "\t\tdefault:\n");
   1439     fprintf(fp, "\t\t\treturn ptr - (unsigned char*)buf;\n");
   1440     fprintf(fp, "\t\t} //switch\n");
   1441     if (strstr(m_basename.c_str(), "gl")) {
   1442         fprintf(fp, "\t\t#ifdef CHECK_GL_ERRORS\n");
   1443         fprintf(fp, "\t\tGLint err = this->glGetError();\n");
   1444         fprintf(fp, "\t\tif (err) fprintf(stderr, \"%s Error (post-call): 0x%%X in %%s\\n\", err, lastCall);\n", m_basename.c_str());
   1445         fprintf(fp, "\t\t#endif\n");
   1446     }
   1447 
   1448     fprintf(fp, "\t\tptr += packetLen;\n");
   1449     fprintf(fp, "\t} // while\n");
   1450     fprintf(fp, "\treturn ptr - (unsigned char*)buf;\n");
   1451     fprintf(fp, "}\n");
   1452 
   1453     fclose(fp);
   1454     return 0;
   1455 }
   1456 
   1457 int ApiGen::readSpec(const std::string & filename)
   1458 {
   1459     FILE *specfp = fopen(filename.c_str(), "rt");
   1460     if (specfp == NULL) {
   1461         return -1;
   1462     }
   1463 
   1464     char line[1000];
   1465     unsigned int lc = 0;
   1466     while (fgets(line, sizeof(line), specfp) != NULL) {
   1467         lc++;
   1468         EntryPoint ref;
   1469         if (ref.parse(lc, std::string(line))) {
   1470             push_back(ref);
   1471             updateMaxEntryPointsParams(ref.vars().size());
   1472         }
   1473     }
   1474     fclose(specfp);
   1475     return 0;
   1476 }
   1477 
   1478 int ApiGen::readAttributes(const std::string & attribFilename)
   1479 {
   1480     enum { ST_NAME, ST_ATT } state;
   1481 
   1482     FILE *fp = fopen(attribFilename.c_str(), "rt");
   1483     if (fp == NULL) {
   1484         perror(attribFilename.c_str());
   1485         return -1;
   1486     }
   1487     char buf[1000];
   1488 
   1489     state = ST_NAME;
   1490     EntryPoint *currentEntry = NULL;
   1491     size_t lc = 0;
   1492     bool globalAttributes = false;
   1493     while (fgets(buf, sizeof(buf), fp) != NULL) {
   1494         lc++;
   1495         std::string line(buf);
   1496         if (line.size() == 0) continue; // could that happen?
   1497 
   1498         if (line.at(0) == '#') continue; // comment
   1499 
   1500         size_t first = line.find_first_not_of(" \t\n");
   1501         if (state == ST_ATT && (first == std::string::npos || first == 0)) state = ST_NAME;
   1502 
   1503         line = trim(line);
   1504         if (line.size() == 0 || line.at(0) == '#') continue;
   1505 
   1506         switch(state) {
   1507         case ST_NAME:
   1508             if (line == "GLOBAL") {
   1509                 globalAttributes = true;
   1510             } else {
   1511                 globalAttributes = false;
   1512                 currentEntry = findEntryByName(line);
   1513                 if (currentEntry == NULL) {
   1514                     fprintf(stderr, "WARNING: %u: attribute of non existant entry point %s\n", (unsigned int)lc, line.c_str());
   1515                 }
   1516             }
   1517             state = ST_ATT;
   1518             break;
   1519         case ST_ATT:
   1520             if (globalAttributes) {
   1521                 setGlobalAttribute(line, lc);
   1522             } else  if (currentEntry != NULL) {
   1523                 currentEntry->setAttribute(line, lc);
   1524             }
   1525             break;
   1526         }
   1527     }
   1528     return 0;
   1529 }
   1530 
   1531 
   1532 int ApiGen::setGlobalAttribute(const std::string & line, size_t lc)
   1533 {
   1534     size_t pos = 0;
   1535     size_t last;
   1536     std::string token = getNextToken(line, pos, &last, WHITESPACE);
   1537     pos = last;
   1538 
   1539     if (token == "base_opcode") {
   1540         std::string str = getNextToken(line, pos, &last, WHITESPACE);
   1541         if (str.size() == 0) {
   1542             fprintf(stderr, "line %u: missing value for base_opcode\n", (unsigned) lc);
   1543         } else {
   1544             setBaseOpcode(atoi(str.c_str()));
   1545         }
   1546     } else  if (token == "encoder_headers") {
   1547         std::string str = getNextToken(line, pos, &last, WHITESPACE);
   1548         pos = last;
   1549         while (str.size() != 0) {
   1550             encoderHeaders().push_back(str);
   1551             str = getNextToken(line, pos, &last, WHITESPACE);
   1552             pos = last;
   1553         }
   1554     } else if (token == "client_context_headers") {
   1555         std::string str = getNextToken(line, pos, &last, WHITESPACE);
   1556         pos = last;
   1557         while (str.size() != 0) {
   1558             clientContextHeaders().push_back(str);
   1559             str = getNextToken(line, pos, &last, WHITESPACE);
   1560             pos = last;
   1561         }
   1562     } else if (token == "server_context_headers") {
   1563         std::string str = getNextToken(line, pos, &last, WHITESPACE);
   1564         pos = last;
   1565         while (str.size() != 0) {
   1566             serverContextHeaders().push_back(str);
   1567             str = getNextToken(line, pos, &last, WHITESPACE);
   1568             pos = last;
   1569         }
   1570     } else if (token == "decoder_headers") {
   1571         std::string str = getNextToken(line, pos, &last, WHITESPACE);
   1572         pos = last;
   1573         while (str.size() != 0) {
   1574             decoderHeaders().push_back(str);
   1575             str = getNextToken(line, pos, &last, WHITESPACE);
   1576             pos = last;
   1577         }
   1578     }
   1579     else {
   1580         fprintf(stderr, "WARNING: %u : unknown global attribute %s\n", (unsigned int)lc, line.c_str());
   1581     }
   1582 
   1583     return 0;
   1584 }
   1585