summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt10
-rw-r--r--apps/uscxml-debug.cpp317
-rw-r--r--src/uscxml/Interpreter.cpp13
-rw-r--r--src/uscxml/Message.cpp18
-rw-r--r--src/uscxml/server/HTTPServer.cpp4
5 files changed, 341 insertions, 21 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 968e4d9..a0377bf 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -923,10 +923,18 @@ if (NOT CMAKE_CROSSCOMPILING)
set_target_properties(uscxml-browser PROPERTIES COTIRE_ADD_UNITY_BUILD FALSE)
# cotire(uscxml-browser)
endif()
-
set_target_properties(uscxml-browser PROPERTIES FOLDER "Apps")
install_executable(TARGETS uscxml-browser COMPONENT tools)
+ add_executable(uscxml-debug apps/uscxml-debug.cpp)
+ target_link_libraries(uscxml-debug uscxml)
+ if (NOT CMAKE_CROSSCOMPILING)
+ set_target_properties(uscxml-debug PROPERTIES COTIRE_ADD_UNITY_BUILD FALSE)
+# cotire(uscxml-browser)
+ endif()
+ set_target_properties(uscxml-debug PROPERTIES FOLDER "Apps")
+ install_executable(TARGETS uscxml-debug COMPONENT tools)
+
if (PROTOBUF_FOUND AND OFF)
file(GLOB W3C-MMI-COMMON ${PROJECT_SOURCE_DIR}/apps/w3c-mmi/*.cpp ${PROJECT_SOURCE_DIR}/apps/w3c-mmi/*.h)
diff --git a/apps/uscxml-debug.cpp b/apps/uscxml-debug.cpp
new file mode 100644
index 0000000..5b9c989
--- /dev/null
+++ b/apps/uscxml-debug.cpp
@@ -0,0 +1,317 @@
+#include "uscxml/config.h"
+#include "uscxml/Interpreter.h"
+#include "uscxml/DOMUtils.h"
+#include "uscxml/UUID.h"
+#include "uscxml/debug/SCXMLDotWriter.h"
+#include <glog/logging.h>
+#include <time.h> // mktime
+
+#include <boost/algorithm/string.hpp>
+
+#ifdef HAS_SIGNAL_H
+#include <signal.h>
+#endif
+
+#ifdef HAS_EXECINFO_H
+#include <execinfo.h>
+#endif
+
+#ifdef HAS_DLFCN_H
+#include <dlfcn.h>
+#endif
+
+using namespace uscxml;
+
+class Debugger : public HTTPServlet, public InterpreterMonitor, public google::LogSink {
+public:
+ class BreakPoint {
+ public:
+
+ enum When {
+ UNDEF_WHEN, AFTER, BEFORE, ON
+ };
+
+ enum Subject {
+ UNDEF_SUBJECT, STATE, TRANSITION, CONFIGURATION, EVENT
+ };
+
+ enum Action {
+ UNDEF_ACTION, ENTER, EXIT
+ };
+
+ BreakPoint(const Data& data) {
+ subject = UNDEF_SUBJECT;
+ when = UNDEF_WHEN;
+ action = UNDEF_ACTION;
+
+ if (data.hasKey("action")) {
+ if (false) {
+ } else if (iequals(data["action"], "enter")) {
+ action = ENTER;
+ } else if (iequals(data["action"], "exit")) {
+ action = EXIT;
+ }
+ }
+ if (data.hasKey("time")) {
+ if (false) {
+ } else if (iequals(data["time"], "before")) {
+ when = BEFORE;
+ } else if (iequals(data["time"], "after")) {
+ when = AFTER;
+ } else if (iequals(data["time"], "on")) {
+ when = ON;
+ }
+ }
+ if (data.hasKey("subject")) {
+ if (false) {
+ } else if (iequals(data["subject"], "state")) {
+ subject = STATE;
+ if (data.hasKey("stateId"))
+ state = data["stateId"].atom;
+ } else if (iequals(data["subject"], "transition")) {
+ subject = TRANSITION;
+ if (data.hasKey("fromStateId"))
+ fromState = data["fromStateId"].atom;
+ if (data.hasKey("toStateId"))
+ fromState = data["toStateId"].atom;
+ } else if (iequals(data["subject"], "event")) {
+ subject = EVENT;
+ if (data.hasKey("eventName"))
+ eventName = data["eventName"].atom;
+ } else if (iequals(data["subject"], "configuration")) {
+ subject = CONFIGURATION;
+ } else if (iequals(data["subject"], "microstep")) {
+ subject = CONFIGURATION;
+ }
+ }
+
+ if (data.hasKey("condition")) {
+ condition = data["condition"].atom;
+ }
+ }
+
+ bool isValid() {
+ return true;
+ }
+
+ protected:
+ When when;
+ Subject subject;
+ Action action;
+
+ std::string state;
+ std::string toState;
+ std::string fromState;
+ std::string eventName;
+
+ std::string condition;
+
+ };
+
+ class LogMessage {
+ public:
+ google::LogSeverity severity;
+ std::string full_filename;
+ std::string base_filename;
+ int line;
+ const struct ::tm* tm_time;
+ std::string message;
+ std::string formatted;
+
+ Data toData() {
+ Data data;
+ data.compound["severity"] = severity;
+ data.compound["fullFilename"] = Data(full_filename, Data::VERBATIM);
+ data.compound["baseFilename"] = Data(base_filename, Data::VERBATIM);
+ data.compound["line"] = line;
+ data.compound["message"] = Data(message, Data::VERBATIM);
+ data.compound["time"] = mktime((struct ::tm*)tm_time);
+ data.compound["formatted"] = Data(formatted, Data::VERBATIM);
+ return data;
+ }
+ };
+
+ std::string _url;
+ Interpreter _interpreter;
+ HTTPServer::Request _debugReq;
+ tthread::recursive_mutex _mutex;
+ std::list<LogMessage> _logMessages;
+ std::map<std::string, BreakPoint> _breakPoints;
+
+ virtual ~Debugger() {
+ }
+
+ // callbacks from http requests
+
+ void debug(const HTTPServer::Request& request) {
+ tthread::lock_guard<tthread::recursive_mutex> lock(_mutex);
+
+ // save request and run until we reach a breakpoint
+ _debugReq = request;
+ _interpreter.interpret();
+ }
+
+ void connect(const HTTPServer::Request& request) {
+ Data replyData;
+ replyData.compound["status"] = Data("success", Data::VERBATIM);
+ returnData(request, replyData);
+ }
+
+ void disconnect(const HTTPServer::Request& request) {
+ Data replyData;
+ replyData.compound["status"] = Data("success", Data::VERBATIM);
+ returnData(request, replyData);
+ }
+
+ void prepare(const HTTPServer::Request& request) {
+ tthread::lock_guard<tthread::recursive_mutex> lock(_mutex);
+
+ _interpreter = Interpreter::fromXML(request.data["content"].atom);
+
+ Data replyData;
+ if (_interpreter) {
+ // register ourself as a monitor
+ _interpreter.addMonitor(this);
+ replyData.compound["status"] = Data("success", Data::VERBATIM);
+ } else {
+ replyData.compound["status"] = Data("failure", Data::VERBATIM);
+ }
+ returnData(request, replyData);
+ }
+
+ void addBreakPoint(const HTTPServer::Request& request) {
+ BreakPoint breakPoint(request.data["content"]);
+ Data replyData;
+ if (breakPoint.isValid()) {
+ replyData.compound["status"] = Data("success", Data::VERBATIM);
+ } else {
+ replyData.compound["status"] = Data("failure", Data::VERBATIM);
+ }
+ returnData(request, replyData);
+ }
+
+ // helpers
+
+ void returnData(const HTTPServer::Request& request, Data replyData) {
+ //always include latest log
+ while(_logMessages.size() > 0) {
+ replyData.compound["log"].array.push_back(_logMessages.front().toData());
+ _logMessages.pop_front();
+ }
+
+ HTTPServer::Reply reply(request);
+ reply.headers["Content-type"] = "application/json";
+ reply.headers["Access-Control-Allow-Origin"] = "*";
+ reply.content = Data::toJSON(replyData);
+ HTTPServer::reply(reply);
+ }
+
+ bool isCORS(const HTTPServer::Request& request) {
+ return (request.data["type"].atom == "options" &&
+ request.data["header"].hasKey("Origin") &&
+ request.data["header"].hasKey("Access-Control-Request-Method"));
+ }
+
+ void handleCORS(const HTTPServer::Request& request) {
+ HTTPServer::Reply corsReply(request);
+ if (request.data["header"].hasKey("Origin")) {
+ corsReply.headers["Access-Control-Allow-Origin"] = request.data["header"]["Origin"].atom;
+ } else {
+ corsReply.headers["Access-Control-Allow-Origin"] = "*";
+ }
+ if (request.data["header"].hasKey("Access-Control-Request-Method"))
+ corsReply.headers["Access-Control-Allow-Methods"] = request.data["header"]["Access-Control-Request-Method"].atom;
+ if (request.data["header"].hasKey("Access-Control-Request-Headers"))
+ corsReply.headers["Access-Control-Allow-Headers"] = request.data["header"]["Access-Control-Request-Headers"].atom;
+
+// std::cout << "CORS!" << std::endl << request << std::endl;
+ HTTPServer::reply(corsReply);
+ }
+
+ // HTTPServlet
+
+ bool httpRecvRequest(const HTTPServer::Request& request) {
+ if (isCORS(request)) {
+ handleCORS(request);
+ return true;
+ }
+
+ std::cout << Data::toJSON(request.data) << std::endl;
+
+ if (false) {
+ } else if (boost::istarts_with(request.data["path"].atom, "/connect")) {
+ connect(request);
+ } else if (boost::istarts_with(request.data["path"].atom, "/disconnect")) {
+ disconnect(request);
+ } else if (boost::istarts_with(request.data["path"].atom, "/prepare")) {
+ prepare(request);
+ } else if (boost::istarts_with(request.data["path"].atom, "/debug")) {
+ debug(request);
+ } else if (boost::istarts_with(request.data["path"].atom, "/breakpoint/add")) {
+ addBreakPoint(request);
+ }
+ return true;
+ }
+ void setURL(const std::string& url) {
+ _url = url;
+ }
+
+ // InterpreterMonitor
+ void onStableConfiguration(Interpreter interpreter) {
+ }
+
+ void beforeCompletion(Interpreter interpreter) {}
+ void afterCompletion(Interpreter interpreter) {}
+ void beforeMicroStep(Interpreter interpreter) {}
+ void beforeTakingTransitions(Interpreter interpreter, const Arabica::XPath::NodeSet<std::string>& transitions) {}
+ void beforeEnteringStates(Interpreter interpreter, const Arabica::XPath::NodeSet<std::string>& statesToEnter) {}
+ void afterEnteringStates(Interpreter interpreter) {}
+ void beforeExitingStates(Interpreter interpreter, const Arabica::XPath::NodeSet<std::string>& statesToExit) {}
+ void afterExitingStates(Interpreter interpreter) {}
+
+ // google::LogSink
+
+ virtual void send(google::LogSeverity severity, const char* full_filename,
+ const char* base_filename, int line,
+ const struct ::tm* tm_time,
+ const char* message, size_t message_len) {
+
+ tthread::lock_guard<tthread::recursive_mutex> lock(_mutex);
+
+ LogMessage msg;
+ msg.severity = severity;
+ msg.full_filename = full_filename;
+ msg.base_filename = base_filename;
+ msg.line = line;
+ msg.tm_time = tm_time;
+ msg.message = std::string(message, message_len);
+ msg.formatted = ToString(severity, base_filename, line, tm_time, message, message_len);
+
+ _logMessages.push_back(msg);
+ }
+
+};
+
+
+int main(int argc, char** argv) {
+ using namespace uscxml;
+
+ InterpreterOptions options = InterpreterOptions::fromCmdLine(argc, argv);
+ Debugger debugger;
+
+ // setup logging
+ google::InitGoogleLogging(argv[0]);
+ google::AddLogSink(&debugger);
+
+ // setup HTTP server
+ HTTPServer::getInstance(18088, 18089, NULL);
+
+
+ HTTPServer::getInstance()->registerServlet("/", &debugger);
+
+ while(true)
+ tthread::this_thread::sleep_for(tthread::chrono::seconds(1));
+
+
+ return EXIT_SUCCESS;
+} \ No newline at end of file
diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp
index fa3e227..6334065 100644
--- a/src/uscxml/Interpreter.cpp
+++ b/src/uscxml/Interpreter.cpp
@@ -707,24 +707,11 @@ void InterpreterImpl::processDOMorText(const Arabica::DOM::Node<std::string>& el
Arabica::SAX::InputSource<std::string> inputSource;
inputSource.setByteStream(ssPtr);
-// parser.setFeature(Arabica::SAX::FeatureNames<std::string>().external_general, true);
-
if (parser.parse(inputSource) && parser.getDocument()) {
Document<std::string> doc = parser.getDocument();
dom = doc.getDocumentElement();
-#if 0
- Node<std::string> content = doc.getDocumentElement();
- assert(content.getNodeType() == Node_base::ELEMENT_NODE);
- Node<std::string> container = doc.createElement("container");
- dom.replaceChild(container, content);
- container.appendChild(content);
-// std::cout << dom << std::endl;
-#endif
return;
} else {
- if (parser.errorsReported()) {
- LOG(ERROR) << parser.errors();
- }
text = srcContent.str();
return;
}
diff --git a/src/uscxml/Message.cpp b/src/uscxml/Message.cpp
index b664fd1..bbd7f28 100644
--- a/src/uscxml/Message.cpp
+++ b/src/uscxml/Message.cpp
@@ -318,8 +318,7 @@ Data Data::fromXML(const std::string& xmlString) {
Data Data::fromJSON(const std::string& jsonString) {
Data data;
- std::string trimmed = jsonString;
- boost::trim(trimmed);
+ std::string trimmed = boost::trim_copy(jsonString);
if (trimmed.length() == 0)
return data;
@@ -387,23 +386,29 @@ Data Data::fromJSON(const std::string& jsonString) {
size_t currTok = 0;
do {
+ // used for debugging
// jsmntok_t t2 = t[currTok];
// std::string value = trimmed.substr(t[currTok].start, t[currTok].end - t[currTok].start);
switch (t[currTok].type) {
case JSMN_STRING:
dataStack.back()->type = Data::VERBATIM;
- case JSMN_PRIMITIVE:
- dataStack.back()->atom = trimmed.substr(t[currTok].start, t[currTok].end - t[currTok].start);
+ case JSMN_PRIMITIVE: {
+ std::string value = trimmed.substr(t[currTok].start, t[currTok].end - t[currTok].start);
+ if (dataStack.back()->type == Data::VERBATIM) {
+ boost::replace_all(value, "\\\"", "\"");
+ }
+ dataStack.back()->atom = value;
dataStack.pop_back();
currTok++;
break;
+ }
case JSMN_OBJECT:
case JSMN_ARRAY:
tokenStack.push_back(t[currTok]);
currTok++;
break;
}
-
+ // used for debugging
// t2 = t[currTok];
// value = trimmed.substr(t[currTok].start, t[currTok].end - t[currTok].start);
@@ -419,7 +424,8 @@ Data Data::fromJSON(const std::string& jsonString) {
if (tokenStack.back().type == JSMN_OBJECT && (t[currTok].type == JSMN_PRIMITIVE || t[currTok].type == JSMN_STRING)) {
// grab key and push new data
- dataStack.push_back(&(dataStack.back()->compound[trimmed.substr(t[currTok].start, t[currTok].end - t[currTok].start)]));
+ std::string value = trimmed.substr(t[currTok].start, t[currTok].end - t[currTok].start);
+ dataStack.push_back(&(dataStack.back()->compound[value]));
currTok++;
}
if (tokenStack.back().type == JSMN_ARRAY) {
diff --git a/src/uscxml/server/HTTPServer.cpp b/src/uscxml/server/HTTPServer.cpp
index d9fbc77..6c26811 100644
--- a/src/uscxml/server/HTTPServer.cpp
+++ b/src/uscxml/server/HTTPServer.cpp
@@ -440,7 +440,9 @@ void HTTPServer::processByMatchingServlet(const Request& request) {
while(servletIter != _httpServlets.end()) {
// is the servlet path a prefix of the actual path?
std::string servletPath = "/" + servletIter->first;
- if (iequals(actualPath.substr(0, servletPath.length()), servletPath) && // servlet path is a prefix
+ if (servletIter->first.length() == 0) {
+ matches.insert(std::make_pair(servletPath, servletIter->second)); // single servlet at root
+ } else if (iequals(actualPath.substr(0, servletPath.length()), servletPath) && // servlet path is a prefix
iequals(actualPath.substr(servletPath.length(), 1), "/")) { // and next character is a '/'
matches.insert(std::make_pair(servletPath, servletIter->second));
}