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