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 
findEntryByName(const std::string & name)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 
printHeader(FILE * fp) const63 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 
genProcTypes(const std::string & filename,SideType side)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 
genFuncTable(const std::string & filename,SideType side)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 
genContext(const std::string & filename,SideType side)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 
genEntryPoints(const std::string & filename,SideType side)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 
genOpcodes(const std::string & filename)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 }
genAttributesTemplate(const std::string & filename)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 
genEncoderHeader(const std::string & filename)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 
getVarEncodingSizeExpression(Var & var,EntryPoint * e,char * buff,size_t bufflen,EncodingSizeFlags flags)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 
writeVarEncodingSize(Var & var,bool excludeOutVars,FILE * fp)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 
writeVarEncodingExpression(Var & var,FILE * fp)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
writeVarLargeEncodingExpression(Var & var,FILE * fp)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 
writeEncodingChecksumValidatorOnReturn(const char * funcName,FILE * fp)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 
addGuestTimePrinting(const EntryPoint * e,bool hasTimeBeforeReadback,FILE * fp)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 
genEncoderImpl(const std::string & filename)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 
genDecoderHeader(const std::string & filename)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 
genContextImpl(const std::string & filename,SideType side)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 
genDecoderImpl(const std::string & filename)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 
readSpec(const std::string & filename)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 
readAttributes(const std::string & attribFilename)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 
setGlobalAttribute(const std::string & line,size_t lc)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