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