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