2 * Copyright (C) 2011 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include "EntryPoint.h"
22 #include <sys/types.h>
24 /* Define this to 1 to enable support for the 'isLarge' variable flag
25 * that instructs the encoder to send large data buffers by a direct
26 * write through the pipe (i.e. without copying it into a temporary
27 * buffer. This has definite performance benefits when using a QEMU Pipe.
31 #define WITH_LARGE_SUPPORT 1
33 // Set to 1 to ensure buffers passed to/from EGL/GL are properly aligned.
34 // This prevents crashes with certain backends (e.g. OSMesa).
35 #define USE_ALIGNED_BUFFERS 1
37 EntryPoint * ApiGen::findEntryByName(const std::string & name)
39 EntryPoint * entry = NULL;
41 size_t n = this->size();
42 for (size_t i = 0; i < n; i++) {
43 if (at(i).name() == name) {
51 void ApiGen::printHeader(FILE *fp) const
53 fprintf(fp, "// Generated Code - DO NOT EDIT !!\n");
54 fprintf(fp, "// generated by 'emugen'\n");
57 int ApiGen::genProcTypes(const std::string &filename, SideType side)
59 FILE *fp = fopen(filename.c_str(), "wt");
61 perror(filename.c_str());
66 const char* basename = m_basename.c_str();
68 fprintf(fp, "#ifndef __%s_%s_proc_t_h\n", basename, sideString(side));
69 fprintf(fp, "#define __%s_%s_proc_t_h\n", basename, sideString(side));
71 fprintf(fp, "\n#include \"%s_types.h\"\n",basename);
72 fprintf(fp, "#ifndef %s_APIENTRY\n",basename);
73 fprintf(fp, "#define %s_APIENTRY \n",basename);
74 fprintf(fp, "#endif\n");
77 for (size_t i = 0; i < size(); i++) {
78 EntryPoint *e = &at(i);
80 fprintf(fp, "typedef ");
81 e->retval().printType(fp);
82 fprintf(fp, " (%s_APIENTRY *%s_%s_proc_t) (", basename, e->name().c_str(), sideString(side));
83 if (side == CLIENT_SIDE) { fprintf(fp, "void * ctx"); }
84 if (e->customDecoder() && side == SERVER_SIDE) { fprintf(fp, "void *ctx"); }
86 VarsArray & evars = e->vars();
87 size_t n = evars.size();
89 for (size_t j = 0; j < n; j++) {
90 if (!evars[j].isVoid()) {
91 if (j != 0 || side == CLIENT_SIDE || (side == SERVER_SIDE && e->customDecoder())) fprintf(fp, ", ");
92 evars[j].printType(fp);
97 fprintf(fp, "\n\n#endif\n");
101 int ApiGen::genFuncTable(const std::string &filename, SideType side)
103 FILE *fp = fopen(filename.c_str(), "wt");
105 perror(filename.c_str());
110 fprintf(fp, "#ifndef __%s_%s_ftable_t_h\n", m_basename.c_str(), sideString(side));
111 fprintf(fp, "#define __%s_%s_ftable_t_h\n", m_basename.c_str(), sideString(side));
113 fprintf(fp, "static const struct _%s_funcs_by_name {\n", m_basename.c_str());
115 "\tconst char *name;\n" \
117 "} %s_funcs_by_name[] = {\n", m_basename.c_str());
120 for (size_t i = 0; i < size(); i++) {
121 EntryPoint *e = &at(i);
122 if (e->notApi()) continue;
123 fprintf(fp, "\t{\"%s\", (void*)%s},\n", e->name().c_str(), e->name().c_str());
126 fprintf(fp, "static const int %s_num_funcs = sizeof(%s_funcs_by_name) / sizeof(struct _%s_funcs_by_name);\n",
127 m_basename.c_str(), m_basename.c_str(), m_basename.c_str());
128 fprintf(fp, "\n\n#endif\n");
132 int ApiGen::genContext(const std::string & filename, SideType side)
134 FILE *fp = fopen(filename.c_str(), "wt");
136 perror(filename.c_str());
141 fprintf(fp, "#ifndef __%s_%s_context_t_h\n", m_basename.c_str(), sideString(side));
142 fprintf(fp, "#define __%s_%s_context_t_h\n", m_basename.c_str(), sideString(side));
144 fprintf(fp, "\n#include \"%s_%s_proc.h\"\n",
146 side == CLIENT_SIDE ? "client" : "server");
147 fprintf(fp, "\n#include \"%s_types.h\"\n", m_basename.c_str());
149 StringVec & contextHeaders = side == CLIENT_SIDE ? m_clientContextHeaders : m_serverContextHeaders;
150 for (size_t i = 0; i < contextHeaders.size(); i++) {
151 fprintf(fp, "#include %s\n", contextHeaders[i].c_str());
155 fprintf(fp, "\nstruct %s_%s_context_t {\n\n",
156 m_basename.c_str(), sideString(side));
159 for (size_t i = 0; i < size(); i++) {
160 EntryPoint *e = &at(i);
161 fprintf(fp, "\t%s_%s_proc_t %s;\n", e->name().c_str(), sideString(side), e->name().c_str());
164 // virtual destructor
165 fprintf(fp, "\t virtual ~%s_%s_context_t() {}\n", m_basename.c_str(), sideString(side));
167 if (side == CLIENT_SIDE || side == WRAPPER_SIDE) {
168 fprintf(fp, "\n\ttypedef %s_%s_context_t *CONTEXT_ACCESSOR_TYPE(void);\n",
169 m_basename.c_str(), sideString(side));
170 fprintf(fp, "\tstatic void setContextAccessor(CONTEXT_ACCESSOR_TYPE *f);\n");
174 fprintf(fp, "\tint initDispatchByName( void *(*getProc)(const char *name, void *userData), void *userData);\n");
176 //client site set error virtual func
177 if (side == CLIENT_SIDE) {
178 fprintf(fp, "\tvirtual void setError(unsigned int error){ (void)error; };\n");
179 fprintf(fp, "\tvirtual unsigned int getError(){ return 0; };\n");
184 fprintf(fp, "\n#endif\n");
189 int ApiGen::genEntryPoints(const std::string & filename, SideType side)
192 if (side != CLIENT_SIDE && side != WRAPPER_SIDE) {
193 fprintf(stderr, "Entry points are only defined for Client and Wrapper components\n");
198 FILE *fp = fopen(filename.c_str(), "wt");
200 perror(filename.c_str());
205 fprintf(fp, "#include <stdio.h>\n");
206 fprintf(fp, "#include <stdlib.h>\n");
207 fprintf(fp, "#include \"%s_%s_context.h\"\n", m_basename.c_str(), sideString(side));
210 fprintf(fp, "#ifndef GL_TRUE\n");
211 fprintf(fp, "extern \"C\" {\n");
213 for (size_t i = 0; i < size(); i++) {
214 fprintf(fp, "\t"); at(i).print(fp, false); fprintf(fp, ";\n");
216 fprintf(fp, "};\n\n");
217 fprintf(fp, "#endif\n");
219 fprintf(fp, "#ifndef GET_CONTEXT\n");
220 fprintf(fp, "static %s_%s_context_t::CONTEXT_ACCESSOR_TYPE *getCurrentContext = NULL;\n",
221 m_basename.c_str(), sideString(side));
224 "void %s_%s_context_t::setContextAccessor(CONTEXT_ACCESSOR_TYPE *f) { getCurrentContext = f; }\n",
225 m_basename.c_str(), sideString(side));
226 fprintf(fp, "#define GET_CONTEXT %s_%s_context_t * ctx = getCurrentContext()\n",
227 m_basename.c_str(), sideString(side));
228 fprintf(fp, "#endif\n\n");
231 for (size_t i = 0; i < size(); i++) {
232 EntryPoint *e = &at(i);
235 fprintf(fp, "\tGET_CONTEXT;\n");
237 bool shouldReturn = !e->retval().isVoid();
238 bool shouldCallWithContext = (side == CLIENT_SIDE);
240 if (shouldCallWithContext) {
241 for (size_t j=0; j<e->vars().size(); j++) {
242 if (e->vars()[j].paramCheckExpression() != "")
243 fprintf(fp, "\t%s\n", e->vars()[j].paramCheckExpression().c_str());
246 fprintf(fp, "\t%sctx->%s(%s",
247 shouldReturn ? "return " : "",
249 shouldCallWithContext ? "ctx" : "");
250 size_t nvars = e->vars().size();
252 for (size_t j = 0; j < nvars; j++) {
253 if (!e->vars()[j].isVoid()) {
255 j != 0 || shouldCallWithContext ? "," : "",
256 e->vars()[j].name().c_str());
260 fprintf(fp, "}\n\n");
267 int ApiGen::genOpcodes(const std::string &filename)
269 FILE *fp = fopen(filename.c_str(), "wt");
271 perror(filename.c_str());
276 fprintf(fp, "#ifndef __GUARD_%s_opcodes_h_\n", m_basename.c_str());
277 fprintf(fp, "#define __GUARD_%s_opcodes_h_\n\n", m_basename.c_str());
278 for (size_t i = 0; i < size(); i++) {
279 fprintf(fp, "#define OP_%s \t\t\t\t\t%u\n", at(i).name().c_str(), (unsigned int)i + m_baseOpcode);
281 fprintf(fp, "#define OP_last \t\t\t\t\t%u\n", (unsigned int)size() + m_baseOpcode);
282 fprintf(fp,"\n\n#endif\n");
287 int ApiGen::genAttributesTemplate(const std::string &filename )
289 FILE *fp = fopen(filename.c_str(), "wt");
291 perror(filename.c_str());
295 for (size_t i = 0; i < size(); i++) {
296 if (at(i).hasPointers()) {
299 fprintf(fp, "%s\n\n", at(i).name().c_str());
306 int ApiGen::genEncoderHeader(const std::string &filename)
308 FILE *fp = fopen(filename.c_str(), "wt");
310 perror(filename.c_str());
315 std::string classname = m_basename + "_encoder_context_t";
317 fprintf(fp, "\n#ifndef GUARD_%s\n", classname.c_str());
318 fprintf(fp, "#define GUARD_%s\n\n", classname.c_str());
320 fprintf(fp, "#include \"IOStream.h\"\n");
321 fprintf(fp, "#include \"ChecksumCalculator.h\"\n");
322 fprintf(fp, "#include \"%s_%s_context.h\"\n\n\n", m_basename.c_str(), sideString(CLIENT_SIDE));
324 for (size_t i = 0; i < m_encoderHeaders.size(); i++) {
325 fprintf(fp, "#include %s\n", m_encoderHeaders[i].c_str());
329 fprintf(fp, "struct %s : public %s_%s_context_t {\n\n",
330 classname.c_str(), m_basename.c_str(), sideString(CLIENT_SIDE));
331 fprintf(fp, "\tIOStream *m_stream;\n");
332 fprintf(fp, "\tChecksumCalculator *m_checksumCalculator;\n\n");
334 fprintf(fp, "\t%s(IOStream *stream, ChecksumCalculator *checksumCalculator);\n", classname.c_str());
335 fprintf(fp, "};\n\n");
337 fprintf(fp, "#endif // GUARD_%s", classname.c_str());
343 // Format the byte length expression for a given variable into a user-provided buffer
344 // If the variable type is not a pointer, this is simply its size as a decimal constant
345 // If the variable is a pointer, this will be an expression provided by the .attrib file
346 // through the 'len' attribute.
348 // Returns 1 if the variable is a pointer, 0 otherwise
350 static int getVarEncodingSizeExpression(Var& var, EntryPoint* e, char* buff, size_t bufflen)
353 if (!var.isPointer()) {
354 snprintf(buff, bufflen, "%u", (unsigned int) var.type()->bytes());
357 const char* lenExpr = var.lenExpression().c_str();
358 const char* varname = var.name().c_str();
359 if (e != NULL && lenExpr[0] == '\0') {
360 fprintf(stderr, "%s: data len is undefined for '%s'\n",
361 e->name().c_str(), varname);
363 if (var.nullAllowed()) {
364 snprintf(buff, bufflen, "((%s != NULL) ? %s : 0)", varname, lenExpr);
366 snprintf(buff, bufflen, "%s", lenExpr);
372 static int writeVarEncodingSize(Var& var, FILE* fp)
375 if (!var.isPointer()) {
376 fprintf(fp, "%u", (unsigned int) var.type()->bytes());
379 fprintf(fp, "__size_%s", var.name().c_str());
386 static void writeVarEncodingExpression(Var& var, FILE* fp)
388 const char* varname = var.name().c_str();
390 if (var.isPointer()) {
391 // encode a pointer header
392 fprintf(fp, "\t*(unsigned int *)(ptr) = __size_%s; ptr += 4;\n", varname);
394 Var::PointerDir dir = var.pointerDir();
395 if (dir == Var::POINTER_INOUT || dir == Var::POINTER_IN) {
396 if (var.nullAllowed()) {
397 fprintf(fp, "\tif (%s != NULL) ", varname);
402 if (var.packExpression().size() != 0) {
403 fprintf(fp, "%s;", var.packExpression().c_str());
405 fprintf(fp, "memcpy(ptr, %s, __size_%s);",
409 fprintf(fp, "ptr += __size_%s;\n", varname);
412 // encode a non pointer variable
414 fprintf(fp, "\t\tmemcpy(ptr, &%s, %u); ptr += %u;\n",
416 (unsigned) var.type()->bytes(),
417 (unsigned) var.type()->bytes());
422 #if WITH_LARGE_SUPPORT
423 static void writeVarLargeEncodingExpression(Var& var, FILE* fp)
425 const char* varname = var.name().c_str();
427 fprintf(fp, "\tstream->writeFully(&__size_%s,4);\n", varname);
428 fprintf(fp, "\tif (useChecksum) checksumCalculator->addBuffer(&__size_%s,4);\n", varname);
429 if (var.nullAllowed()) {
430 fprintf(fp, "\tif (%s != NULL) {\n", varname);
432 if (var.writeExpression() != "") {
433 fprintf(fp, "%s", var.writeExpression().c_str());
435 fprintf(fp, "\t\tstream->writeFully(%s, __size_%s);\n", varname, varname);
436 fprintf(fp, "\t\tif (useChecksum) checksumCalculator->addBuffer(%s, __size_%s);\n", varname, varname);
438 if (var.nullAllowed()) fprintf(fp, "\t}\n");
440 #endif /* WITH_LARGE_SUPPORT */
442 static void writeEncodingChecksumValidatorOnReturn(const char* funcName, FILE* fp) {
443 fprintf(fp, "\tif (useChecksum) {\n"
444 "\t\tstd::unique_ptr<unsigned char[]> checksumBuf(new unsigned char[checksumSize]);\n"
445 "\t\tstream->readback(checksumBuf.get(), checksumSize);\n"
446 "\t\tif (!checksumCalculator->validate(checksumBuf.get(), checksumSize)) {\n"
447 "\t\t\tALOGE(\"%s: GL communication error, please report this issue to b.android.com.\\n\");\n"
455 int ApiGen::genEncoderImpl(const std::string &filename)
457 FILE *fp = fopen(filename.c_str(), "wt");
459 perror(filename.c_str());
465 fprintf(fp, "#include <memory>\n");
466 fprintf(fp, "#include <string.h>\n");
467 fprintf(fp, "#include \"%s_opcodes.h\"\n\n", m_basename.c_str());
468 fprintf(fp, "#include \"%s_enc.h\"\n\n\n", m_basename.c_str());
469 fprintf(fp, "#include <stdio.h>\n\n");
470 fprintf(fp, "namespace {\n\n");
472 // unsupport printout
474 "void enc_unsupported()\n"
476 "\tALOGE(\"Function is unsupported\\n\");\n"
480 std::string classname = m_basename + "_encoder_context_t";
483 for (size_t i = 0; i < n; i++) {
484 EntryPoint *e = &at(i);
486 if (e->unsupported()) continue;
489 e->print(fp, true, "_enc", /* classname + "::" */"", "void *self");
492 // fprintf(fp, "\n\tDBG(\">>>> %s\\n\");\n", e->name().c_str());
493 fprintf(fp, "\n\t%s *ctx = (%s *)self;\n",
496 fprintf(fp, "\tIOStream *stream = ctx->m_stream;\n"
497 "\tChecksumCalculator *checksumCalculator = ctx->m_checksumCalculator;\n"
498 "\tbool useChecksum = checksumCalculator->getVersion() > 0;\n\n");
499 VarsArray & evars = e->vars();
500 size_t maxvars = evars.size();
505 // Define the __size_XXX variables that contain the size of data
506 // associated with pointers.
507 for (j = 0; j < maxvars; j++) {
510 if (!var.isPointer())
513 const char* varname = var.name().c_str();
514 fprintf(fp, "\tconst unsigned int __size_%s = ", varname);
516 getVarEncodingSizeExpression(var, e, buff, sizeof(buff));
517 fprintf(fp, "%s;\n", buff);
520 bool hasLargeFields = false;
521 #if WITH_LARGE_SUPPORT
522 // We need to take care of 'isLarge' variable in a special way
523 // Anything before an isLarge variable can be packed into a single
524 // buffer, which is then commited. Each isLarge variable is a pointer
525 // to data that can be written to directly through the pipe, which
526 // will be instant when using a QEMU pipe
529 size_t npointers = 0;
531 // First, compute the total size, 8 bytes for the opcode + payload size (without checksum)
532 fprintf(fp, "\t unsigned char *ptr;\n");
533 fprintf(fp, "\t unsigned char *buf;\n");
534 fprintf(fp, "\t const size_t sizeWithoutChecksum = 8");
536 for (j = 0; j < maxvars; j++) {
538 npointers += writeVarEncodingSize(evars[j], fp);
541 fprintf(fp, " + %zu*4", npointers);
545 // Then, size of the checksum string
546 fprintf(fp, "\t const size_t checksumSize = checksumCalculator->checksumByteSize();\n");
548 // And, size of the whole thing
549 fprintf(fp, "\t const size_t totalSize = sizeWithoutChecksum + checksumSize;\n");
551 // We need to divide the packet into fragments. Each fragment contains
552 // either copied arguments to a temporary buffer, or direct writes for
555 // The first fragment must also contain the opcode+payload_size+checksum_size
558 while (nvars < maxvars || maxvars == 0) {
560 // Skip over non-large fields
561 for (j = nvars; j < maxvars; j++) {
562 if (evars[j].isLarge())
566 // Write a fragment if needed.
567 if (nvars == 0 || j > nvars) {
568 const char* plus = "";
570 if (nvars == 0 && j == maxvars) {
571 // Simple shortcut for the common case where we don't have large variables;
572 fprintf(fp, "\tbuf = stream->alloc(totalSize);\n");
575 hasLargeFields = true;
576 // allocate buffer from the stream until the first large variable
577 fprintf(fp, "\tbuf = stream->alloc(");
581 fprintf(fp,"8"); plus = " + ";
585 for (j = nvars; j < maxvars && !evars[j].isLarge(); j++) {
586 fprintf(fp, "%s", plus); plus = " + ";
587 npointers += writeVarEncodingSize(evars[j], fp);
590 fprintf(fp, "%s%zu*4", plus, npointers); plus = " + ";
595 fprintf(fp, "\tptr = buf;\n");
597 // encode packet header if needed.
599 fprintf(fp, "\tint tmp = OP_%s;memcpy(ptr, &tmp, 4); ptr += 4;\n", e->name().c_str());
600 fprintf(fp, "\tmemcpy(ptr, &totalSize, 4); ptr += 4;\n\n");
604 fprintf(fp, "\n\tif (useChecksum) checksumCalculator->addBuffer(buf, ptr-buf);\n");
608 // encode non-large fields in this fragment
609 for (j = nvars; j < maxvars && !evars[j].isLarge(); j++) {
610 writeVarEncodingExpression(evars[j],fp);
613 fprintf(fp, "\n\tif (useChecksum) checksumCalculator->addBuffer(buf, ptr-buf);\n");
614 // Ensure the fragment is commited if it is followed by a large variable
616 fprintf(fp, "\tstream->flush();\n");
620 // If we have one or more large variables, write them directly.
622 for ( ; j < maxvars && evars[j].isLarge(); j++) {
623 writeVarLargeEncodingExpression(evars[j], fp);
629 #else /* !WITH_LARGE_SUPPORT */
630 size_t nvars = evars.size();
631 size_t npointers = 0;
632 fprintf(fp, "\t const size_t sizeWithoutChecksum = 8");
633 for (size_t j = 0; j < nvars; j++) {
634 npointers += getVarEncodingSizeExpression(evars[j],e,buff,sizeof(buff));
635 fprintf(fp, " + %s", buff);
637 fprintf(fp, " + %u * 4;\n", (unsigned int) npointers);
639 fprintf(fp, "\t const size_t checksumSize = checksumCalculator->checksumByteSize();\n");
640 // Size of the whole thing
641 fprintf(fp, "\t const size_t totalSize = sizeWithoutChecksum + checksumSize;\n");
643 // allocate buffer from the stream;
644 fprintf(fp, "\t unsigned char *ptr = stream->alloc(sizeWithoutChecksum);\n\n");
646 // encode into the stream;
647 fprintf(fp, "\tint tmp = OP_%s; memcpy(ptr, &tmp, 4); ptr += 4;\n", e->name().c_str());
648 fprintf(fp, "\tmemcpy(ptr, &sizeWithoutChecksum, 4); ptr += 4;\n\n");
651 for (size_t j = 0; j < nvars; j++) {
652 writeVarEncodingExpression(evars[j], fp);
654 #endif /* !WITH_LARGE_SUPPORT */
657 if (hasLargeFields) {
658 fprintf(fp, "\tbuf = stream->alloc(checksumSize);\n");
659 fprintf(fp, "\tif (useChecksum) checksumCalculator->writeChecksum(buf, checksumSize);\n\n");
661 fprintf(fp, "\tif (useChecksum) checksumCalculator->writeChecksum(ptr, checksumSize); ptr += checksumSize;\n\n");
665 bool hasReadbackChecksum = false;
666 for (size_t j = 0; j < nvars; j++) {
667 if (evars[j].isPointer()) {
668 Var::PointerDir dir = evars[j].pointerDir();
669 if (dir == Var::POINTER_INOUT || dir == Var::POINTER_OUT) {
670 const char* varname = evars[j].name().c_str();
671 const char* indent = "\t";
672 if (evars[j].nullAllowed()) {
673 fprintf(fp, "\tif (%s != NULL) {\n",varname);
676 fprintf(fp, "%sstream->readback(%s, __size_%s);\n",
677 indent, varname, varname);
678 fprintf(fp, "%sif (useChecksum) checksumCalculator->addBuffer(%s, __size_%s);\n",
679 indent, varname, varname);
680 if (evars[j].nullAllowed()) {
681 fprintf(fp, "\t}\n");
683 hasReadbackChecksum = true;
687 //XXX fprintf(fp, "\n\tDBG(\"<<<< %s\\n\");\n", e->name().c_str());
689 // todo - return value for pointers
690 if (e->retval().isPointer()) {
691 fprintf(stderr, "WARNING: %s : return value of pointer is unsupported\n",
693 if (e->flushOnEncode()) {
694 fprintf(fp, "\tstream->flush();\n");
696 fprintf(fp, "\t return NULL;\n");
697 } else if (e->retval().type()->name() != "void") {
698 fprintf(fp, "\n\t%s retval;\n", e->retval().type()->name().c_str());
699 fprintf(fp, "\tstream->readback(&retval, %u);\n",(unsigned) e->retval().type()->bytes());
700 fprintf(fp, "\tif (useChecksum) checksumCalculator->addBuffer(&retval, %u);\n",
701 (unsigned) e->retval().type()->bytes());
702 writeEncodingChecksumValidatorOnReturn(e->name().c_str(), fp);
703 fprintf(fp, "\treturn retval;\n");
705 if (e->flushOnEncode()) fprintf(fp, "\tstream->flush();\n");
706 if (hasReadbackChecksum) writeEncodingChecksumValidatorOnReturn(e->name().c_str(), fp);
708 fprintf(fp, "}\n\n");
711 fprintf(fp, "} // namespace\n\n");
714 fprintf(fp, "%s::%s(IOStream *stream, ChecksumCalculator *checksumCalculator)\n{\n", classname.c_str(), classname.c_str());
715 fprintf(fp, "\tm_stream = stream;\n");
716 fprintf(fp, "\tm_checksumCalculator = checksumCalculator;\n\n");
718 for (size_t i = 0; i < n; i++) {
719 EntryPoint *e = &at(i);
720 if (e->unsupported()) {
722 "\tthis->%s = (%s_%s_proc_t) &enc_unsupported;\n",
725 sideString(CLIENT_SIDE));
728 "\tthis->%s = &%s_enc;\n",
733 fprintf(fp, "}\n\n");
740 int ApiGen::genDecoderHeader(const std::string &filename)
742 FILE *fp = fopen(filename.c_str(), "wt");
744 perror(filename.c_str());
749 std::string classname = m_basename + "_decoder_context_t";
751 fprintf(fp, "\n#ifndef GUARD_%s\n", classname.c_str());
752 fprintf(fp, "#define GUARD_%s\n\n", classname.c_str());
754 fprintf(fp, "#include \"IOStream.h\" \n");
755 fprintf(fp, "#include \"%s_%s_context.h\"\n\n\n", m_basename.c_str(), sideString(SERVER_SIDE));
756 if (strcmp(classname.c_str(), "gles2_decoder_context_t") == 0) {
757 fprintf(fp, "\n#include <map>\n");
758 fprintf(fp, "\n#include <mutex>\n");
760 fprintf(fp, "\n#include \"emugl/common/logging.h\"\n");
762 for (size_t i = 0; i < m_decoderHeaders.size(); i++) {
763 fprintf(fp, "#include %s\n", m_decoderHeaders[i].c_str());
767 fprintf(fp, "struct %s : public %s_%s_context_t {\n\n",
768 classname.c_str(), m_basename.c_str(), sideString(SERVER_SIDE));
769 fprintf(fp, "\tsize_t decode(void *buf, size_t bufsize, IOStream *stream);\n");
770 if (strcmp(classname.c_str(), "gles2_decoder_context_t") == 0){
772 "\tvoid freeShader(); \n\
773 \tvoid freeProgram(); \n\
774 \tstd::map<GLuint, GLuint> m_programs; \n\
775 \tstd::map<GLuint, GLuint> m_shaders; \n\
776 \tstd::mutex m_lock; \n\
781 fprintf(fp, "\n};\n\n");
782 fprintf(fp, "#endif // GUARD_%s\n", classname.c_str());
788 int ApiGen::genContextImpl(const std::string &filename, SideType side)
790 FILE *fp = fopen(filename.c_str(), "wt");
792 perror(filename.c_str());
797 std::string classname = m_basename + "_" + sideString(side) + "_context_t";
799 fprintf(fp, "\n\n#include <string.h>\n");
800 fprintf(fp, "#include \"%s_%s_context.h\"\n\n\n", m_basename.c_str(), sideString(side));
801 fprintf(fp, "#include <stdio.h>\n\n");
803 fprintf(fp, "int %s::initDispatchByName(void *(*getProc)(const char *, void *userData), void *userData)\n{\n", classname.c_str());
804 for (size_t i = 0; i < n; i++) {
805 EntryPoint *e = &at(i);
806 fprintf(fp, "\t%s = (%s_%s_proc_t) getProc(\"%s\", userData);\n",
812 fprintf(fp, "\treturn 0;\n");
813 fprintf(fp, "}\n\n");
818 int ApiGen::genDecoderImpl(const std::string &filename)
820 FILE *fp = fopen(filename.c_str(), "wt");
822 perror(filename.c_str());
828 std::string classname = m_basename + "_decoder_context_t";
832 fprintf(fp, "\n\n#include <string.h>\n");
833 fprintf(fp, "#include \"%s_opcodes.h\"\n\n", m_basename.c_str());
834 fprintf(fp, "#include \"%s_dec.h\"\n\n\n", m_basename.c_str());
835 fprintf(fp, "#include \"ProtocolUtils.h\"\n\n");
836 fprintf(fp, "#include \"ChecksumCalculatorThreadInfo.h\"\n\n");
837 fprintf(fp, "#include <stdio.h>\n\n");
838 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");
841 fprintf(fp, "# define DEBUG(...) do { if (emugl_cxt_logger) { emugl_cxt_logger(LogLevel::TRACE, __VA_ARGS__); } } while(0)\n\n");
844 "#ifdef CHECK_GLERROR\n"
845 "# define SET_LASTCALL(name) sprintf(lastCall, #name)\n"
847 "# define SET_LASTCALL(name) ((void)0)\n"
851 fprintf(fp, "using namespace emugl;\n\n");
853 // glsl shader/program free;
854 if (strcmp(classname.c_str(), "gles2_decoder_context_t") == 0) {
855 fprintf(fp, "void %s::freeShader(){\n", classname.c_str());
858 \tauto it = m_shaders.begin();\n\
860 \twhile(it != m_shaders.end()) \n\
862 \t\tthis->glDeleteShader(it->first);\n\
865 \tm_lock.unlock();\n\
868 fprintf(fp, "void %s::freeProgram(){\n", classname.c_str());
871 \tauto it = m_programs.begin(); \n\
873 \twhile(it != m_programs.end()) \n\
875 \t\tthis->glDeleteProgram(it->first);\n\
878 \tm_lock.unlock();\n\
883 fprintf(fp, "size_t %s::decode(void *buf, size_t len, IOStream *stream)\n{\n", classname.c_str());
887 \tif (len < 8) return pos; \n\
888 \tunsigned char *ptr = (unsigned char *)buf;\n\
889 \tbool unknownOpcode = false; \n\
890 #ifdef CHECK_GL_ERROR \n\
891 \tchar lastCall[256] = {0}; \n\
893 \twhile ((len - pos >= 8) && !unknownOpcode) { \n\
894 \t\tuint32_t opcode = *(uint32_t *)ptr; \n\
895 \t\tsize_t packetLen = *(uint32_t *)(ptr + 4);\n\
896 \t\tif (len - pos < packetLen) return pos; \n\
897 \t\tbool useChecksum = ChecksumCalculatorThreadInfo::getVersion() > 0;\n\
898 \t\tsize_t checksumSize = 0;\n\
899 \t\tif (useChecksum) {\n\
900 \t\t\tchecksumSize = ChecksumCalculatorThreadInfo::checksumByteSize();\n\
902 \t\tswitch(opcode) {\n");
904 for (size_t f = 0; f < n; f++) {
907 PASS_VariableDeclarations = PASS_FIRST,
916 EntryPoint *e = &at(f);
918 // construct a printout string;
919 std::string printString = "";
920 for (size_t i = 0; i < e->vars().size(); i++) {
921 Var *v = &e->vars()[i];
922 if (!v->isVoid()) printString += (v->isPointer() ? "%p(%u)" : v->type()->printFormat()) + " ";
925 // TODO - add for return value;
927 fprintf(fp, "\t\tcase OP_%s: {\n", e->name().c_str());
929 bool totalTmpBuffExist = false;
930 std::string totalTmpBuffOffset = "0";
931 std::string *tmpBufOffset = new std::string[e->vars().size()];
933 // construct retval type string
934 std::string retvalType;
935 if (!e->retval().isVoid()) {
936 retvalType = e->retval().type()->name();
939 for (int pass = PASS_FIRST; pass < PASS_LAST; pass++) {
940 if (pass == PASS_FunctionCall &&
941 !e->retval().isVoid() &&
942 !e->retval().isPointer()) {
943 fprintf(fp, "\t\t\t*(%s *)(&tmpBuf[%s]) = ", retvalType.c_str(),
944 totalTmpBuffOffset.c_str());
948 if (pass == PASS_FunctionCall) {
949 fprintf(fp, "\t\t\tthis->%s(", e->name().c_str());
950 if (e->customDecoder()) {
951 fprintf(fp, "this"); // add a context to the call
953 } else if (pass == PASS_DebugPrint) {
955 "\t\t\tDEBUG(\"%s(%%p): %s(%s)\", stream",
958 printString.c_str());
959 if (e->vars().size() > 0 && !e->vars()[0].isVoid()) {
964 std::string varoffset = "8"; // skip the header
965 VarsArray & evars = e->vars();
966 // allocate memory for out pointers;
967 for (size_t j = 0; j < evars.size(); j++) {
972 const char* var_name = v->name().c_str();
973 const char* var_type_name = v->type()->name().c_str();
974 const unsigned var_type_bytes = v->type()->bytes();
976 if ((pass == PASS_FunctionCall) &&
977 (j != 0 || e->customDecoder())) {
980 if (pass == PASS_DebugPrint && j != 0) {
984 if (!v->isPointer()) {
985 if (pass == PASS_VariableDeclarations) {
987 "\t\t\t%s var_%s = Unpack<%s,uint%u_t>(ptr + %s);\n",
995 if (pass == PASS_FunctionCall ||
996 pass == PASS_DebugPrint) {
997 fprintf(fp, "var_%s", var_name);
999 varoffset += " + " + toString(var_type_bytes);
1003 if (pass == PASS_VariableDeclarations) {
1005 "\t\t\tuint32_t size_%s __attribute__((unused)) = Unpack<uint32_t,uint32_t>(ptr + %s);\n",
1010 if (v->pointerDir() == Var::POINTER_IN ||
1011 v->pointerDir() == Var::POINTER_INOUT) {
1012 if (pass == PASS_VariableDeclarations) {
1013 #if USE_ALIGNED_BUFFERS
1015 "\t\t\tInputBuffer inptr_%s(ptr + %s + 4, size_%s);\n",
1020 if (pass == PASS_FunctionCall) {
1021 if (v->nullAllowed()) {
1023 "size_%s == 0 ? NULL : (%s)(inptr_%s.get())",
1029 "(%s)(inptr_%s.get())",
1033 } else if (pass == PASS_DebugPrint) {
1035 "(%s)(inptr_%s.get()), size_%s",
1040 #else // !USE_ALIGNED_BUFFERS
1042 "unsigned char *inptr_%s = (ptr + %s + 4);\n",
1046 if (pass == PASS_FunctionCall) {
1047 if (v->nullAllowed()) {
1049 "size_%s == 0 ? NULL : (%s)(inptr_%s)",
1059 } else if (pass == PASS_DebugPrint) {
1061 "(%s)(inptr_%s), size_%s",
1066 #endif // !USE_ALIGNED_BUFFERS
1067 varoffset += " + 4 + size_";
1068 varoffset += var_name;
1069 } else { // out pointer;
1070 if (pass == PASS_TmpBuffAlloc) {
1071 if (!totalTmpBuffExist) {
1073 "\t\t\tsize_t totalTmpSize = size_%s;\n",
1077 "\t\t\ttotalTmpSize += size_%s;\n",
1080 tmpBufOffset[j] = totalTmpBuffOffset;
1081 totalTmpBuffOffset += " + size_";
1082 totalTmpBuffOffset += var_name;
1083 totalTmpBuffExist = true;
1084 } else if (pass == PASS_MemAlloc) {
1085 #if USE_ALIGNED_BUFFERS
1087 "\t\t\tOutputBuffer outptr_%s(&tmpBuf[%s], size_%s);\n",
1089 tmpBufOffset[j].c_str(),
1091 } else if (pass == PASS_FunctionCall) {
1092 if (v->nullAllowed()) {
1094 "size_%s == 0 ? NULL : (%s)(outptr_%s.get())",
1100 "(%s)(outptr_%s.get())",
1104 } else if (pass == PASS_DebugPrint) {
1106 "(%s)(outptr_%s.get()), size_%s",
1111 if (pass == PASS_FlushOutput) {
1113 "\t\t\toutptr_%s.flush();\n",
1116 #else // !USE_ALIGNED_BUFFERS
1118 "\t\t\tunsigned char *outptr_%s = &tmpBuf[%s];\n",
1120 tmpBufOffset[j].c_str());
1122 "\t\t\tmemset(outptr_%s, 0, %s);\n",
1124 toString(v->type()->bytes()).c_str());
1125 } else if (pass == PASS_FunctionCall) {
1126 if (v->nullAllowed()) {
1128 "size_%s == 0 ? NULL : (%s)(outptr_%s)",
1138 } else if (pass == PASS_DebugPrint) {
1140 "(%s)(outptr_%s), size_%s",
1145 #endif // !USE_ALIGNED_BUFFERS
1146 varoffset += " + 4";
1150 if (pass == PASS_Protocol) {
1152 "\t\t\tif (useChecksum) {\n"
1153 "\t\t\t\tChecksumCalculatorThreadInfo::validOrDie(ptr, %s, "
1154 "ptr + %s, checksumSize, "
1155 "\n\t\t\t\t\t\"%s::decode,"
1156 " OP_%s: GL checksumCalculator failure\\n\");\n"
1164 varoffset += " + 4";
1167 if (pass == PASS_FunctionCall ||
1168 pass == PASS_DebugPrint) {
1169 fprintf(fp, ");\n");
1172 if (pass == PASS_TmpBuffAlloc) {
1173 if (!e->retval().isVoid() && !e->retval().isPointer()) {
1174 if (!totalTmpBuffExist)
1176 "\t\t\tsize_t totalTmpSize = sizeof(%s);\n",
1177 retvalType.c_str());
1180 "\t\t\ttotalTmpSize += sizeof(%s);\n",
1181 retvalType.c_str());
1183 totalTmpBuffExist = true;
1185 if (totalTmpBuffExist) {
1187 "\t\t\ttotalTmpSize += checksumSize;\n"
1188 "\t\t\tunsigned char *tmpBuf = stream->alloc(totalTmpSize);\n");
1192 if (pass == PASS_Epilog) {
1193 // send back out pointers data as well as retval
1194 if (totalTmpBuffExist) {
1196 "\t\t\tif (useChecksum) {\n"
1197 "\t\t\t\tChecksumCalculatorThreadInfo::writeChecksum("
1198 "&tmpBuf[0], totalTmpSize - checksumSize, "
1199 "&tmpBuf[totalTmpSize - checksumSize], checksumSize);\n"
1201 "\t\t\tstream->flush();\n");
1206 fprintf(fp, "\t\t\tSET_LASTCALL(\"%s\");\n", e->name().c_str());
1207 if (strcmp(m_basename.c_str(), "gles2") == 0) {
1208 if (strcmp(e->name().c_str(), "glAttachShader") == 0){
1210 \t\t\tm_lock.lock();\n\
1211 m_shaders.insert({var_shader, 1});\n\
1212 m_lock.unlock();\n");
1213 } else if(strcmp(e->name().c_str(), "glDeleteProgram") == 0){
1215 "\t\t\tm_lock.lock(); \n"
1216 "\t\t\tauto pro = m_programs.find(var_program); \n"
1217 "\t\t\tif (pro != m_programs.end()) \n"
1219 "\t\t\t\tm_programs.erase(pro); \n"
1221 "\t\t\tm_lock.unlock();\n");
1222 } else if(strcmp(e->name().c_str(), "glDeleteShader") == 0){
1224 "\t\t\tm_lock.lock(); \n\
1225 \t\t\tauto shader = m_shaders.find(var_shader); \n\
1226 \t\t\tif (shader != m_shaders.end()) \n\
1228 \t\t\t\tm_shaders.erase(shader); \n\
1230 \t\t\tm_lock.unlock(); \n");
1231 } else if(strcmp(e->name().c_str(), "glLinkProgram") == 0){
1233 \t\t\tm_lock.lock();\n\
1234 \t\t\tm_programs.insert({var_program, 1});\n\
1235 \t\t\tm_lock.unlock();\n");
1238 fprintf(fp, "\t\t\tbreak;\n");
1239 fprintf(fp, "\t\t}\n");
1241 delete [] tmpBufOffset;
1243 fprintf(fp, "\t\t\tdefault:\n");
1244 fprintf(fp, "\t\t\t\tunknownOpcode = true;\n");
1245 fprintf(fp, "\t\t} //switch\n");
1246 if (strstr(m_basename.c_str(), "gl")) {
1247 fprintf(fp, "#ifdef CHECK_GL_ERROR\n");
1248 fprintf(fp, "\tint err = lastCall[0] ? this->glGetError() : GL_NO_ERROR;\n");
1249 fprintf(fp, "\tif (err) fprintf(stderr, \"%s Error: 0x%%X in %%s\\n\", err, lastCall);\n", m_basename.c_str());
1250 fprintf(fp, "#endif\n");
1253 fprintf(fp, "\t\tif (!unknownOpcode) {\n");
1254 fprintf(fp, "\t\t\tpos += packetLen;\n");
1255 fprintf(fp, "\t\t\tptr += packetLen;\n");
1256 fprintf(fp, "\t\t}\n");
1257 fprintf(fp, "\t} // while\n");
1258 fprintf(fp, "\treturn pos;\n");
1265 int ApiGen::readSpec(const std::string & filename)
1267 FILE *specfp = fopen(filename.c_str(), "rt");
1268 if (specfp == NULL) {
1273 unsigned int lc = 0;
1274 while (fgets(line, sizeof(line), specfp) != NULL) {
1277 if (ref.parse(lc, std::string(line))) {
1279 updateMaxEntryPointsParams(ref.vars().size());
1286 int ApiGen::readAttributes(const std::string & attribFilename)
1288 enum { ST_NAME, ST_ATT } state;
1290 FILE *fp = fopen(attribFilename.c_str(), "rt");
1292 perror(attribFilename.c_str());
1298 EntryPoint *currentEntry = NULL;
1300 bool globalAttributes = false;
1301 while (fgets(buf, sizeof(buf), fp) != NULL) {
1303 std::string line(buf);
1304 if (line.size() == 0) continue; // could that happen?
1306 if (line.at(0) == '#') continue; // comment
1308 size_t first = line.find_first_not_of(" \t\n");
1309 if (state == ST_ATT && (first == std::string::npos || first == 0)) state = ST_NAME;
1312 if (line.size() == 0 || line.at(0) == '#') continue;
1316 if (line == "GLOBAL") {
1317 globalAttributes = true;
1319 globalAttributes = false;
1320 currentEntry = findEntryByName(line);
1321 if (currentEntry == NULL) {
1322 fprintf(stderr, "WARNING: %u: attribute of non existant entry point %s\n", (unsigned int)lc, line.c_str());
1328 if (globalAttributes) {
1329 setGlobalAttribute(line, lc);
1330 } else if (currentEntry != NULL) {
1331 currentEntry->setAttribute(line, lc);
1340 int ApiGen::setGlobalAttribute(const std::string & line, size_t lc)
1344 std::string token = getNextToken(line, pos, &last, WHITESPACE);
1347 if (token == "base_opcode") {
1348 std::string str = getNextToken(line, pos, &last, WHITESPACE);
1349 if (str.size() == 0) {
1350 fprintf(stderr, "line %u: missing value for base_opcode\n", (unsigned) lc);
1352 setBaseOpcode(atoi(str.c_str()));
1354 } else if (token == "encoder_headers") {
1355 std::string str = getNextToken(line, pos, &last, WHITESPACE);
1357 while (str.size() != 0) {
1358 encoderHeaders().push_back(str);
1359 str = getNextToken(line, pos, &last, WHITESPACE);
1362 } else if (token == "client_context_headers") {
1363 std::string str = getNextToken(line, pos, &last, WHITESPACE);
1365 while (str.size() != 0) {
1366 clientContextHeaders().push_back(str);
1367 str = getNextToken(line, pos, &last, WHITESPACE);
1370 } else if (token == "server_context_headers") {
1371 std::string str = getNextToken(line, pos, &last, WHITESPACE);
1373 while (str.size() != 0) {
1374 serverContextHeaders().push_back(str);
1375 str = getNextToken(line, pos, &last, WHITESPACE);
1378 } else if (token == "decoder_headers") {
1379 std::string str = getNextToken(line, pos, &last, WHITESPACE);
1381 while (str.size() != 0) {
1382 decoderHeaders().push_back(str);
1383 str = getNextToken(line, pos, &last, WHITESPACE);
1388 fprintf(stderr, "WARNING: %u : unknown global attribute %s\n", (unsigned int)lc, line.c_str());