// I feel dirty, but we need to access the datamodel timer // #define protected public #include "uscxml/config.h" #include "uscxml/Common.h" #include "uscxml/Convenience.h" #include "uscxml/Interpreter.h" #include "uscxml/DOMUtils.h" #include "uscxml/Factory.h" #include "uscxml/server/HTTPServer.h" #include "uscxml/transform/ChartToFlatSCXML.h" #include #include #ifdef HAS_SIGNAL_H #include #endif #ifdef BUILD_PROFILING # include "uscxml/plugins/DataModel.h" # endif #ifdef _WIN32 #include "XGetopt.h" #include "XGetopt.cpp" #endif static bool withFlattening = false; static double delayFactor = 1; static size_t benchmarkRuns = 0; static std::string documentURI; int retCode = EXIT_FAILURE; uscxml::Interpreter interpreter; void printUsageAndExit(const char* progName) { // remove path from program name std::string progStr(progName); if (progStr.find_last_of(PATH_SEPERATOR) != std::string::npos) { progStr = progStr.substr(progStr.find_last_of(PATH_SEPERATOR) + 1, progStr.length() - (progStr.find_last_of(PATH_SEPERATOR) + 1)); } printf("%s version " USCXML_VERSION " (" CMAKE_BUILD_TYPE " build - " CMAKE_COMPILER_STRING ")\n", progStr.c_str()); printf("Usage\n"); printf("\t%s", progStr.c_str()); printf(" [-f] [-dN] [-bN]"); #ifdef BUILD_AS_PLUGINS printf(" [-p pluginPath]"); #endif printf(" URL"); printf("\n"); printf("Options\n"); printf("\t-f : flatten to SCXML state-machine\n"); printf("\t-d FACTOR : delay factor\n"); printf("\t-b ITERATIONS : benchmark with number of runs\n"); printf("\n"); exit(1); } class W3CStatusMonitor : public uscxml::InterpreterMonitor { void beforeCompletion(uscxml::Interpreter tmp) { if (interpreter.getConfiguration().size() == 1 && interpreter.isInState("pass")) { std::cout << "TEST SUCCEEDED" << std::endl; retCode = EXIT_SUCCESS; return; } std::cout << "TEST FAILED" << std::endl; } }; int main(int argc, char** argv) { using namespace uscxml; try { #if defined(HAS_SIGNAL_H) && !defined(WIN32) signal(SIGPIPE, SIG_IGN); #endif if (argc < 2) { exit(EXIT_FAILURE); } google::InitGoogleLogging(argv[0]); google::LogToStderr(); HTTPServer::getInstance(32954, 32955, NULL); // bind to some random tcp sockets for ioprocessor tests char* dfEnv = getenv("USCXML_DELAY_FACTOR"); if (dfEnv) { delayFactor = strTo(dfEnv); } int option; while ((option = getopt(argc, argv, "fd:b:")) != -1) { switch(option) { case 'f': withFlattening = true; break; case 'd': delayFactor = strTo(optarg); break; case 'b': benchmarkRuns = strTo(optarg); break; default: break; } } const char* envBenchmarkRuns = getenv("USCXML_BENCHMARK_ITERATIONS"); if (envBenchmarkRuns != NULL) { benchmarkRuns = strTo(envBenchmarkRuns); } documentURI = argv[optind]; LOG(INFO) << "Processing " << documentURI << (withFlattening ? " FSM converted" : "") << (delayFactor ? "" : " with delays *= " + toStr(delayFactor)) << (benchmarkRuns > 0 ? " for " + toStr(benchmarkRuns) + " benchmarks" : ""); if (withFlattening) { interpreter = Interpreter::fromURL(documentURI); Transformer flattener = ChartToFlatSCXML::transform(interpreter); interpreter = flattener; // std::cout << interpreter.getDocument() << std::endl; } else { interpreter = Interpreter::fromURL(documentURI); } if (delayFactor != 1) { Arabica::DOM::Document document = interpreter.getDocument(); Arabica::DOM::Element root = document.getDocumentElement(); Arabica::XPath::NodeSet sends = InterpreterImpl::filterChildElements(interpreter.getNameSpaceInfo().xmlNSPrefix + "send", root, true); for (int i = 0; i < sends.size(); i++) { Arabica::DOM::Element send = Arabica::DOM::Element(sends[i]); if (HAS_ATTR(send, "delay")) { NumAttr delay(ATTR(send, "delay")); int value = strTo(delay.value); if (delay.unit == "s") value *= 1000; value *= delayFactor; send.setAttribute("delay", toStr(value) + "ms"); std::cout << ATTR(send, "delay") << std::endl; } else if (HAS_ATTR(send, "delayexpr")) { std::string delayExpr = ATTR(send, "delayexpr"); send.setAttribute("delayexpr", "(" + delayExpr + ".indexOf('ms', " + delayExpr + ".length - 2) !== -1 ? " "(" + delayExpr + ".slice(0,-2) * " + toStr(delayFactor) + ") + \"ms\" : " "(" + delayExpr + ".slice(0,-1) * 1000 * " + toStr(delayFactor) + ") + \"ms\")"); std::cout << ATTR(send, "delayexpr") << std::endl; } } std::list issues = interpreter.validate(); for (std::list::iterator issueIter = issues.begin(); issueIter != issues.end(); issueIter++) { std::cout << *issueIter << std::endl; } } if (interpreter) { W3CStatusMonitor* vm = new W3CStatusMonitor(); interpreter.addMonitor(vm); if (benchmarkRuns > 0) { LOG(INFO) << "Benchmarking " << documentURI << (withFlattening ? " FSM converted" : "") << (delayFactor ? "" : " with delays *= " + toStr(delayFactor)); InterpreterState state = interpreter.getState(); double avg = 0; #ifdef BUILD_PROFILING double avgDm = 0; double avgStep = 0; #endif size_t remainingRuns = benchmarkRuns; uint64_t start = tthread::chrono::system_clock::now(); while(remainingRuns-- > 0) { Timer t; t.start(); for(;;) { state = interpreter.step(true); if (state == USCXML_FINISHED) { #ifdef BUILD_PROFILING avgDm += interpreter.getDataModel().timer.elapsed; interpreter.getDataModel().timer.reset(); avgStep += interpreter.timer.elapsed; #endif } if (state < 0) break; } t.stop(); avg += t.elapsed; interpreter.reset(); } uint64_t totalDuration = tthread::chrono::system_clock::now() - start; std::cout << benchmarkRuns << " iterations in " << totalDuration << " ms" << std::endl; std::cout << (avg * 1000.0) / (double)benchmarkRuns << " ms on average" << std::endl; #ifdef BUILD_PROFILING std::cout << (avgDm * 1000.0) / (double)benchmarkRuns << " ms in datamodel" << std::endl; std::cout << (avgStep * 1000.0) / (double)benchmarkRuns << " ms in microsteps" << std::endl; #endif } else { interpreter.start(); while(interpreter.runOnMainThread(25)); } } } catch(Event e) { std::cout << e << std::endl; } catch(std::exception e) { std::cout << e.what() << std::endl; } return retCode; }