void MemoryTracer::onCustomInstruction(S2EExecutionState* state, uint64_t opcode) { if (!OPCODE_CHECK(opcode, MEMORY_TRACER_OPCODE)) { return; } //XXX: remove this mess. Should have a function for extracting //info from opcodes. opcode >>= 16; uint8_t op = opcode & 0xFF; opcode >>= 8; MemoryTracerOpcodes opc = (MemoryTracerOpcodes)op; switch(opc) { case Enable: enableTracing(); break; case Disable: disableTracing(); break; default: s2e()->getWarningsStream() << "MemoryTracer: unsupported opcode " << hexval(opc) << '\n'; break; } }
void MemoryTracer::onTimer() { if (m_elapsedTics++ < m_timeTrigger) { return; } enableTracing(); m_timerConnection.disconnect(); }
void MemoryTracer::initialize() { m_tracer = static_cast<ExecutionTracer*>(s2e()->getPlugin("ExecutionTracer")); m_execDetector = static_cast<ModuleExecutionDetector*>(s2e()->getPlugin("ModuleExecutionDetector")); //Retrict monitoring to configured modules only m_monitorModules = s2e()->getConfig()->getBool(getConfigKey() + ".monitorModules"); if (m_monitorModules && !m_execDetector) { s2e()->getWarningsStream() << "MemoryTracer: The monitorModules option requires ModuleExecutionDetector\n"; exit(-1); } //Catch all accesses to the stack m_monitorStack = s2e()->getConfig()->getBool(getConfigKey() + ".monitorStack"); //Catch accesses that are above the specified address m_catchAbove = s2e()->getConfig()->getInt(getConfigKey() + ".catchAccessesAbove"); m_catchBelow = s2e()->getConfig()->getInt(getConfigKey() + ".catchAccessesBelow"); //Whether or not to include host addresses in the trace. //This is useful for debugging, bug yields larger traces m_traceHostAddresses = s2e()->getConfig()->getBool(getConfigKey() + ".traceHostAddresses"); //Check that the current state is actually allowed to write to //the object state. Can be useful to debug the engine. m_debugObjectStates = s2e()->getConfig()->getBool(getConfigKey() + ".debugObjectStates"); //Start monitoring after the specified number of seconds bool hasTimeTrigger = false; m_timeTrigger = s2e()->getConfig()->getInt(getConfigKey() + ".timeTrigger", 0, &hasTimeTrigger); m_elapsedTics = 0; bool manualMode = s2e()->getConfig()->getBool(getConfigKey() + ".manualTrigger"); m_monitorMemory = s2e()->getConfig()->getBool(getConfigKey() + ".monitorMemory"); m_monitorPageFaults = s2e()->getConfig()->getBool(getConfigKey() + ".monitorPageFaults"); m_monitorTlbMisses = s2e()->getConfig()->getBool(getConfigKey() + ".monitorTlbMisses"); s2e()->getDebugStream() << "MonitorMemory: " << m_monitorMemory << " PageFaults: " << m_monitorPageFaults << " TlbMisses: " << m_monitorTlbMisses << '\n'; if (hasTimeTrigger) { m_timerConnection = s2e()->getCorePlugin()->onTimer.connect( sigc::mem_fun(*this, &MemoryTracer::onTimer)); } else if (manualMode) { s2e()->getCorePlugin()->onCustomInstruction.connect( sigc::mem_fun(*this, &MemoryTracer::onCustomInstruction)); } else { enableTracing(); } }
void MemoryTracer::onCustomInstruction(S2EExecutionState* state, uint64_t opcode) { if (!OPCODE_CHECK(opcode, MEMORY_TRACER_OPCODE)) { return; } uint64_t subfunction = OPCODE_GETSUBFUNCTION(opcode); MemoryTracerOpcodes opc = (MemoryTracerOpcodes)subfunction; switch(opc) { case Enable: enableTracing(); break; case Disable: disableTracing(); break; default: s2e()->getWarningsStream() << "MemoryTracer: unsupported opcode " << hexval(opc) << '\n'; break; } }
int main(int argc, char *argv[]) { Core *core; int option; bool enableMemoryDump = false; uint32_t memDumpBase = 0; uint32_t memDumpLength = 0; char *memDumpFilename = NULL; size_t memDumpFilenameLen = 0; bool verbose = false; uint32_t fbWidth = 640; uint32_t fbHeight = 480; bool blockDeviceOpen = false; bool enableFbWindow = false; uint32_t totalThreads = 4; char *separator; uint32_t memorySize = 0x1000000; const char *sharedMemoryFile = NULL; struct stat st; enum { MODE_NORMAL, MODE_COSIMULATION, MODE_GDB_REMOTE_DEBUG } mode = MODE_NORMAL; while ((option = getopt(argc, argv, "f:d:vm:b:t:c:r:s:i:o:")) != -1) { switch (option) { case 'v': verbose = true; break; case 'r': gScreenRefreshRate = parseNumArg(optarg); break; case 'f': enableFbWindow = true; separator = strchr(optarg, 'x'); if (!separator) { fprintf(stderr, "Invalid framebuffer size %s\n", optarg); return 1; } fbWidth = parseNumArg(optarg); fbHeight = parseNumArg(separator + 1); break; case 'm': if (strcmp(optarg, "normal") == 0) mode = MODE_NORMAL; else if (strcmp(optarg, "cosim") == 0) mode = MODE_COSIMULATION; else if (strcmp(optarg, "gdb") == 0) mode = MODE_GDB_REMOTE_DEBUG; else { fprintf(stderr, "Unkown execution mode %s\n", optarg); return 1; } break; case 'd': // Memory dump, of the form: filename,start,length separator = strchr(optarg, ','); if (separator == NULL) { fprintf(stderr, "bad format for memory dump\n"); usage(); return 1; } memDumpFilenameLen = (size_t)(separator - optarg); memDumpFilename = (char*) malloc(memDumpFilenameLen + 1); strncpy(memDumpFilename, optarg, memDumpFilenameLen); memDumpFilename[memDumpFilenameLen] = '\0'; memDumpBase = parseNumArg(separator + 1); separator = strchr(separator + 1, ','); if (separator == NULL) { fprintf(stderr, "bad format for memory dump\n"); usage(); return 1; } memDumpLength = parseNumArg(separator + 1); enableMemoryDump = true; break; case 'b': if (openBlockDevice(optarg) < 0) return 1; blockDeviceOpen = true; break; case 'c': memorySize = parseNumArg(optarg); break; case 't': totalThreads = parseNumArg(optarg); if (totalThreads < 1 || totalThreads > 32) { fprintf(stderr, "Total threads must be between 1 and 32\n"); return 1; } break; case 's': sharedMemoryFile = optarg; break; case 'i': recvInterruptFd = open(optarg, O_RDWR); if (recvInterruptFd < 0) { perror("main: failed to open receive interrupt pipe"); return 1; } if (fstat(recvInterruptFd, &st) < 0) { perror("main: stat failed on receive interrupt pipe"); return 1; } if ((st.st_mode & S_IFMT) != S_IFIFO) { fprintf(stderr, "%s is not a pipe\n", optarg); return 1; } break; case 'o': sendInterruptFd = open(optarg, O_RDWR); if (sendInterruptFd < 0) { perror("main: failed to open send interrupt pipe"); return 1; } if (fstat(sendInterruptFd, &st) < 0) { perror("main: stat failed on send interrupt pipe"); return 1; } if ((st.st_mode & S_IFMT) != S_IFIFO) { fprintf(stderr, "%s is not a pipe\n", optarg); return 1; } break; case '?': usage(); return 1; } } if (optind == argc) { fprintf(stderr, "No image filename specified\n"); usage(); return 1; } // Don't randomize memory for cosimulation mode, because // memory is checked against the hardware model to ensure a match core = initCore(memorySize, totalThreads, mode != MODE_COSIMULATION, sharedMemoryFile); if (core == NULL) return 1; if (loadHexFile(core, argv[optind]) < 0) { fprintf(stderr, "Error reading image %s\n", argv[optind]); return 1; } if (enableFbWindow) { if (initFramebuffer(fbWidth, fbHeight) < 0) return 1; } switch (mode) { case MODE_NORMAL: if (verbose) enableTracing(core); setStopOnFault(core, false); if (enableFbWindow) { while (executeInstructions(core, ALL_THREADS, gScreenRefreshRate)) { updateFramebuffer(core); pollFbWindowEvent(); checkInterruptPipe(core); } } else { while (executeInstructions(core, ALL_THREADS, 1000000)) checkInterruptPipe(core); } break; case MODE_COSIMULATION: setStopOnFault(core, false); if (runCosimulation(core, verbose) < 0) return 1; // Failed break; case MODE_GDB_REMOTE_DEBUG: setStopOnFault(core, true); remoteGdbMainLoop(core, enableFbWindow); break; } if (enableMemoryDump) writeMemoryToFile(core, memDumpFilename, memDumpBase, memDumpLength); dumpInstructionStats(core); if (blockDeviceOpen) closeBlockDevice(); if (stoppedOnFault(core)) return 1; return 0; }
// Read events from standard in. Step each emulator thread in lockstep // and ensure the side effects match. int runCosimulation(Core *core, bool verbose) { char line[1024]; uint32_t threadId; uint32_t address; uint32_t pc; uint64_t writeMask; uint32_t vectorValues[NUM_VECTOR_LANES]; char valueStr[256]; uint32_t reg; uint32_t scalarValue; bool verilogModelHalted = false; unsigned long len; enableCosimulation(core); if (verbose) enableTracing(core); while (fgets(line, sizeof(line), stdin)) { if (verbose) printf("%s", line); len = strlen(line); if (len > 0) line[len - 1] = '\0'; // Strip off newline if (sscanf(line, "store %x %x %x %" PRIx64 " %s", &pc, &threadId, &address, &writeMask, valueStr) == 5) { // Memory Store if (parseHexVector(valueStr, vectorValues, true) < 0) return 0; cosimCheckEvent = EVENT_MEM_STORE; cosimCheckPc = pc; cosimCheckThread = threadId; cosimCheckAddress = address; cosimCheckMask = writeMask; memcpy(cosimCheckValues, vectorValues, sizeof(uint32_t) * NUM_VECTOR_LANES); if (!cosimStep(core, threadId)) return -1; } else if (sscanf(line, "vwriteback %x %x %x %" PRIx64 " %s", &pc, &threadId, ®, &writeMask, valueStr) == 5) { // Vector writeback if (parseHexVector(valueStr, vectorValues, false) < 0) { printf("test failed\n"); return 0; } cosimCheckEvent = EVENT_VECTOR_WRITEBACK; cosimCheckPc = pc; cosimCheckThread = threadId; cosimCheckRegister = reg; cosimCheckMask = writeMask; memcpy(cosimCheckValues, vectorValues, sizeof(uint32_t) * NUM_VECTOR_LANES); if (!cosimStep(core, threadId)) return -1; } else if (sscanf(line, "swriteback %x %x %x %x", &pc, &threadId, ®, &scalarValue) == 4) { // Scalar Writeback cosimCheckEvent = EVENT_SCALAR_WRITEBACK; cosimCheckPc = pc; cosimCheckThread = threadId; cosimCheckRegister = reg; cosimCheckValues[0] = scalarValue; if (!cosimStep(core, threadId)) return -1; } else if (strcmp(line, "***HALTED***") == 0) { verilogModelHalted = true; break; } else if (sscanf(line, "interrupt %d %x", &threadId, &pc) == 2) cosimInterrupt(core, threadId, pc); else if (!verbose) printf("%s\n", line); // Echo unrecognized lines to stdout (verbose already does this for all lines) } if (!verilogModelHalted) { printf("program did not finish normally\n"); printf("%s\n", line); // Print error (if any) return -1; } // Ensure emulator is also halted. If it executes any more instructions // cosimError will be flagged. cosimEventTriggered = false; cosimCheckEvent = EVENT_NONE; while (!coreHalted(core)) { executeInstructions(core, ALL_THREADS, 1); if (cosimError) return -1; } return 0; }
// Read events from standard in. Step each emulator thread in lockstep // and ensure the side effects match. // Returns 1 if successful, 0 if there was an error int runCosimulation(Core *core, int verbose) { char line[1024]; int threadId; uint32_t address; uint32_t pc; uint64_t writeMask; uint32_t vectorValues[16]; char valueStr[256]; int reg; uint32_t scalarValue; int verilogModelHalted = 0; int len; enableCosimulation(core, 1); if (verbose) enableTracing(core); while (fgets(line, sizeof(line), stdin)) { if (verbose) printf("%s", line); len = strlen(line); if (len > 0) line[len - 1] = '\0'; // Strip off newline if (sscanf(line, "store %x %x %x %llx %s", &pc, &threadId, &address, &writeMask, valueStr) == 5) { // Memory Store if (!parseHexVector(valueStr, vectorValues, 1)) return 0; cosimCheckEvent = kEventMemStore; cosimCheckPc = pc; cosimCheckThread = threadId; cosimCheckAddress = address; cosimCheckMask = writeMask; memcpy(cosimCheckValues, vectorValues, sizeof(uint32_t) * 16); if (!cosimStep(core, threadId)) return 0; } else if (sscanf(line, "vwriteback %x %x %x %llx %s", &pc, &threadId, ®, &writeMask, valueStr) == 5) { // Vector writeback if (!parseHexVector(valueStr, vectorValues, 0)) { printf("test failed\n"); return 0; } cosimCheckEvent = kEventVectorWriteback; cosimCheckPc = pc; cosimCheckThread = threadId; cosimCheckRegister = reg; cosimCheckMask = writeMask; memcpy(cosimCheckValues, vectorValues, sizeof(uint32_t) * 16); if (!cosimStep(core, threadId)) return 0; } else if (sscanf(line, "swriteback %x %x %x %x", &pc, &threadId, ®, &scalarValue) == 4) { // Scalar Writeback cosimCheckEvent = kEventScalarWriteback; cosimCheckPc = pc; cosimCheckThread = threadId; cosimCheckRegister = reg; cosimCheckValues[0] = scalarValue; if (!cosimStep(core, threadId)) return 0; } else if (strcmp(line, "***HALTED***") == 0) { // Note: we don't check that the reference model is actually verilogModelHalted verilogModelHalted = 1; break; } else if (sscanf(line, "interrupt %d %x", &threadId, &pc) == 2) cosimInterrupt(core, threadId, pc); else if (!verbose) printf("%s\n", line); // Echo unrecognized lines to stdout (verbose already does this for all lines) } if (!verilogModelHalted) { printf("program did not finish normally\n"); printf("%s\n", line); // Print error (if any) return 0; } // Ensure emulator is also halted. If it executes any more instructions // cosimError will be flagged. cosimEventTriggered = 0; cosimCheckEvent = kEventNone; while (!coreHalted(core)) { executeInstructions(core, -1, 1); if (cosimError) return 0; } return 1; }