From 0e0be07906a720ae54e4572d6ac0cb657424550d Mon Sep 17 00:00:00 2001 From: Stefan Radomski Date: Sat, 18 Jun 2016 13:55:39 +0200 Subject: Started to port Debugger and issue 87 --- apps/uscxml-browser.cpp | 2 +- apps/uscxml-debugger.html | 2836 ++++++++++++++++++++ .../swig/wrapped/WrappedInterpreterMonitor.cpp | 24 +- .../swig/wrapped/WrappedInterpreterMonitor.h | 35 +- src/uscxml/Interpreter.cpp | 22 +- src/uscxml/Interpreter.h | 7 +- src/uscxml/debug/Breakpoint.cpp | 267 ++ src/uscxml/debug/Breakpoint.h | 100 + src/uscxml/debug/DebugSession.cpp | 373 +++ src/uscxml/debug/DebugSession.h | 107 + src/uscxml/debug/Debugger.cpp | 258 ++ src/uscxml/debug/Debugger.h | 116 + src/uscxml/debug/DebuggerServlet.cpp | 261 ++ src/uscxml/debug/DebuggerServlet.h | 108 + src/uscxml/interpreter/BasicContentExecutor.cpp | 14 +- src/uscxml/interpreter/BasicEventQueue.cpp | 20 +- src/uscxml/interpreter/ContentExecutorImpl.h | 2 +- src/uscxml/interpreter/FastMicroStep.cpp | 87 +- src/uscxml/interpreter/FastMicroStep.h | 2 +- src/uscxml/interpreter/InterpreterImpl.cpp | 2 +- src/uscxml/interpreter/InterpreterImpl.h | 18 +- src/uscxml/interpreter/InterpreterMonitor.h | 64 +- src/uscxml/interpreter/MicroStepImpl.h | 5 +- src/uscxml/util/BlockingQueue.h | 77 + src/uscxml/util/DOM.h | 40 +- test/issues/test-issue87.scxml.todo | 189 ++ test/src/test-lifecycle.cpp | 46 +- test/src/test-state-pass.cpp | 4 +- test/src/test-stress.cpp | 4 +- test/src/test-url.cpp | 2 +- test/src/test-validating.cpp | 4 +- 31 files changed, 4917 insertions(+), 179 deletions(-) create mode 100644 apps/uscxml-debugger.html create mode 100644 src/uscxml/debug/Breakpoint.cpp create mode 100644 src/uscxml/debug/Breakpoint.h create mode 100644 src/uscxml/debug/DebugSession.cpp create mode 100644 src/uscxml/debug/DebugSession.h create mode 100644 src/uscxml/debug/Debugger.cpp create mode 100644 src/uscxml/debug/Debugger.h create mode 100644 src/uscxml/debug/DebuggerServlet.cpp create mode 100644 src/uscxml/debug/DebuggerServlet.h create mode 100644 src/uscxml/util/BlockingQueue.h create mode 100644 test/issues/test-issue87.scxml.todo diff --git a/apps/uscxml-browser.cpp b/apps/uscxml-browser.cpp index 722b0af..a57b79b 100644 --- a/apps/uscxml-browser.cpp +++ b/apps/uscxml-browser.cpp @@ -74,7 +74,7 @@ int main(int argc, char** argv) { if (options.verbose) { StateTransitionMonitor* vm = new StateTransitionMonitor(); vm->copyToInvokers(true); - interpreter.setMonitor(vm); + interpreter.addMonitor(vm); } interpreters.push_back(interpreter); diff --git a/apps/uscxml-debugger.html b/apps/uscxml-debugger.html new file mode 100644 index 0000000..3aece02 --- /dev/null +++ b/apps/uscxml-debugger.html @@ -0,0 +1,2836 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+ + diff --git a/src/bindings/swig/wrapped/WrappedInterpreterMonitor.cpp b/src/bindings/swig/wrapped/WrappedInterpreterMonitor.cpp index f066a72..a89ff54 100644 --- a/src/bindings/swig/wrapped/WrappedInterpreterMonitor.cpp +++ b/src/bindings/swig/wrapped/WrappedInterpreterMonitor.cpp @@ -30,31 +30,31 @@ using namespace XERCESC_NS; WrappedInterpreterMonitor::WrappedInterpreterMonitor() {} WrappedInterpreterMonitor::~WrappedInterpreterMonitor() {} -void WrappedInterpreterMonitor::beforeExitingState(const XERCESC_NS::DOMElement* state) { +void WrappedInterpreterMonitor::beforeExitingState(InterpreterImpl* impl, const XERCESC_NS::DOMElement* state) { std::stringstream ss; ss << *state; beforeExitingState(ATTR(state, "id"), DOMUtils::xPathForNode(state), ss.str()); } -void WrappedInterpreterMonitor::afterExitingState(const XERCESC_NS::DOMElement* state) { +void WrappedInterpreterMonitor::afterExitingState(InterpreterImpl* impl, const XERCESC_NS::DOMElement* state) { std::stringstream ss; ss << *state; afterExitingState(ATTR(state, "id"), DOMUtils::xPathForNode(state), ss.str()); } -void WrappedInterpreterMonitor::beforeExecutingContent(const XERCESC_NS::DOMElement* content) { +void WrappedInterpreterMonitor::beforeExecutingContent(InterpreterImpl* impl, const XERCESC_NS::DOMElement* content) { std::stringstream ss; ss << *content; beforeExecutingContent(TAGNAME(content), DOMUtils::xPathForNode(content), ss.str()); } -void WrappedInterpreterMonitor::afterExecutingContent(const XERCESC_NS::DOMElement* content) { +void WrappedInterpreterMonitor::afterExecutingContent(InterpreterImpl* impl, const XERCESC_NS::DOMElement* content) { std::stringstream ss; ss << *content; afterExecutingContent(TAGNAME(content), DOMUtils::xPathForNode(content), ss.str()); } -void WrappedInterpreterMonitor::beforeUninvoking(const XERCESC_NS::DOMElement* invoker, const std::string& invokeid) { +void WrappedInterpreterMonitor::beforeUninvoking(InterpreterImpl* impl, const XERCESC_NS::DOMElement* invoker, const std::string& invokeid) { std::stringstream ss; ss << *invoker; std::string invokeId; @@ -65,7 +65,7 @@ void WrappedInterpreterMonitor::beforeUninvoking(const XERCESC_NS::DOMElement* i beforeUninvoking(DOMUtils::xPathForNode(invoker), invokeId, ss.str()); } -void WrappedInterpreterMonitor::afterUninvoking(const XERCESC_NS::DOMElement* invoker, const std::string& invokeid) { +void WrappedInterpreterMonitor::afterUninvoking(InterpreterImpl* impl, const XERCESC_NS::DOMElement* invoker, const std::string& invokeid) { std::stringstream ss; ss << *invoker; std::string invokeId; @@ -76,7 +76,7 @@ void WrappedInterpreterMonitor::afterUninvoking(const XERCESC_NS::DOMElement* in afterUninvoking(DOMUtils::xPathForNode(invoker), invokeId, ss.str()); } -void WrappedInterpreterMonitor::beforeTakingTransition(const XERCESC_NS::DOMElement* transition) { +void WrappedInterpreterMonitor::beforeTakingTransition(InterpreterImpl* impl, const XERCESC_NS::DOMElement* transition) { XERCESC_NS::DOMElement* sourceState = getSourceState(transition); const XERCESC_NS::DOMElement* root = DOMUtils::getNearestAncestor(transition, "scxml"); @@ -93,7 +93,7 @@ void WrappedInterpreterMonitor::beforeTakingTransition(const XERCESC_NS::DOMElem beforeTakingTransition(DOMUtils::xPathForNode(transition), ATTR_CAST(sourceState, "id"), targets, ss.str()); } -void WrappedInterpreterMonitor::afterTakingTransition(const XERCESC_NS::DOMElement* transition) { +void WrappedInterpreterMonitor::afterTakingTransition(InterpreterImpl* impl, const XERCESC_NS::DOMElement* transition) { XERCESC_NS::DOMElement* sourceState = getSourceState(transition); const XERCESC_NS::DOMElement* root = DOMUtils::getNearestAncestor(transition, "scxml"); @@ -110,19 +110,19 @@ void WrappedInterpreterMonitor::afterTakingTransition(const XERCESC_NS::DOMEleme afterTakingTransition(DOMUtils::xPathForNode(transition), ATTR_CAST(sourceState, "id"), targets, ss.str()); } -void WrappedInterpreterMonitor::beforeEnteringState(const XERCESC_NS::DOMElement* state) { +void WrappedInterpreterMonitor::beforeEnteringState(InterpreterImpl* impl, const XERCESC_NS::DOMElement* state) { std::stringstream ss; ss << *state; beforeEnteringState(ATTR(state, "id"), DOMUtils::xPathForNode(state), ss.str()); } -void WrappedInterpreterMonitor::afterEnteringState(const XERCESC_NS::DOMElement* state) { +void WrappedInterpreterMonitor::afterEnteringState(InterpreterImpl* impl, const XERCESC_NS::DOMElement* state) { std::stringstream ss; ss << *state; afterEnteringState(ATTR(state, "id"), DOMUtils::xPathForNode(state), ss.str()); } -void WrappedInterpreterMonitor::beforeInvoking(const XERCESC_NS::DOMElement* invoker, const std::string& invokeid) { +void WrappedInterpreterMonitor::beforeInvoking(InterpreterImpl* impl, const XERCESC_NS::DOMElement* invoker, const std::string& invokeid) { std::stringstream ss; ss << *invoker; std::string invokeId; @@ -133,7 +133,7 @@ void WrappedInterpreterMonitor::beforeInvoking(const XERCESC_NS::DOMElement* inv beforeInvoking(DOMUtils::xPathForNode(invoker), invokeId, ss.str()); } -void WrappedInterpreterMonitor::afterInvoking(const XERCESC_NS::DOMElement* invoker, const std::string& invokeid) { +void WrappedInterpreterMonitor::afterInvoking(InterpreterImpl* impl, const XERCESC_NS::DOMElement* invoker, const std::string& invokeid) { std::stringstream ss; ss << *invoker; std::string invokeId; diff --git a/src/bindings/swig/wrapped/WrappedInterpreterMonitor.h b/src/bindings/swig/wrapped/WrappedInterpreterMonitor.h index e83c896..c5655cf 100644 --- a/src/bindings/swig/wrapped/WrappedInterpreterMonitor.h +++ b/src/bindings/swig/wrapped/WrappedInterpreterMonitor.h @@ -44,82 +44,91 @@ public: WrappedInterpreterMonitor(); virtual ~WrappedInterpreterMonitor(); - void beforeExitingState(const XERCESC_NS::DOMElement* state); + void beforeExitingState(InterpreterImpl* impl, const XERCESC_NS::DOMElement* state); virtual void beforeExitingState(const std::string& stateId, const std::string& xpath, const std::string& stateXML) {} - void afterExitingState(const XERCESC_NS::DOMElement* state); + void afterExitingState(InterpreterImpl* impl, const XERCESC_NS::DOMElement* state); virtual void afterExitingState(const std::string& stateId, const std::string& xpath, const std::string& stateXML) {} - void beforeExecutingContent(const XERCESC_NS::DOMElement* content); + void beforeExecutingContent(InterpreterImpl* impl, const XERCESC_NS::DOMElement* content); virtual void beforeExecutingContent(const std::string& tagName, const std::string& xpath, const std::string& contentXML) {} - void afterExecutingContent(const XERCESC_NS::DOMElement* content); + void afterExecutingContent(InterpreterImpl* impl, const XERCESC_NS::DOMElement* content); virtual void afterExecutingContent(const std::string& tagName, const std::string& xpath, const std::string& contentXML) {} - void beforeUninvoking(const XERCESC_NS::DOMElement* invoker, + void beforeUninvoking(InterpreterImpl* impl, + const XERCESC_NS::DOMElement* invoker, const std::string& invokeid); virtual void beforeUninvoking(const std::string& xpath, const std::string& invokeid, const std::string& invokerXML) {} - void afterUninvoking(const XERCESC_NS::DOMElement* invoker, + void afterUninvoking(InterpreterImpl* impl, + const XERCESC_NS::DOMElement* invoker, const std::string& invokeid); virtual void afterUninvoking(const std::string& xpath, const std::string& invokeid, const std::string& invokerXML) {} - void beforeTakingTransition(const XERCESC_NS::DOMElement* transition); + void beforeTakingTransition(InterpreterImpl* impl, + const XERCESC_NS::DOMElement* transition); virtual void beforeTakingTransition(const std::string& xpath, const std::string& source, const std::list& targets, const std::string& transitionXML) {} - void afterTakingTransition(const XERCESC_NS::DOMElement* transition); + void afterTakingTransition(InterpreterImpl* impl, + const XERCESC_NS::DOMElement* transition); virtual void afterTakingTransition(const std::string& xpath, const std::string& source, const std::list& targets, const std::string& transitionXML) {} - void beforeEnteringState(const XERCESC_NS::DOMElement* state); + void beforeEnteringState(InterpreterImpl* impl, + const XERCESC_NS::DOMElement* state); virtual void beforeEnteringState(const std::string& stateId, const std::string& xpath, const std::string& stateXML) {} - void afterEnteringState(const XERCESC_NS::DOMElement* state); + void afterEnteringState(InterpreterImpl* impl, + const XERCESC_NS::DOMElement* state); virtual void afterEnteringState(const std::string& stateId, const std::string& xpath, const std::string& stateXML) {} - void beforeInvoking(const XERCESC_NS::DOMElement* invoker, + void beforeInvoking(InterpreterImpl* impl, + const XERCESC_NS::DOMElement* invoker, const std::string& invokeid); virtual void beforeInvoking(const std::string& xpath, const std::string& invokeid, const std::string& invokerXML) {} - void afterInvoking(const XERCESC_NS::DOMElement* invoker, + void afterInvoking(InterpreterImpl* impl, + const XERCESC_NS::DOMElement* invoker, const std::string& invokeid); virtual void afterInvoking(const std::string& xpath, const std::string& invokeid, const std::string& invokerXML) {} - virtual void reportIssue(const InterpreterIssue& issue) {} + virtual void reportIssue(InterpreterImpl* impl, + const InterpreterIssue& issue) {} }; } diff --git a/src/uscxml/Interpreter.cpp b/src/uscxml/Interpreter.cpp index a050401..d21ec6a 100644 --- a/src/uscxml/Interpreter.cpp +++ b/src/uscxml/Interpreter.cpp @@ -220,8 +220,12 @@ void Interpreter::setActionLanguage(ActionLanguage actionLanguage) { return _impl->setActionLanguage(actionLanguage); } -void Interpreter::setMonitor(InterpreterMonitor* monitor) { - return _impl->setMonitor(monitor); +void Interpreter::addMonitor(InterpreterMonitor* monitor) { + return _impl->addMonitor(monitor); +} + +void Interpreter::removeMonitor(InterpreterMonitor* monitor) { + return _impl->removeMonitor(monitor); } std::list Interpreter::validate() { @@ -240,19 +244,19 @@ static void printNodeSet(const std::list nodes) { } #endif -void StateTransitionMonitor::beforeTakingTransition(const XERCESC_NS::DOMElement* transition) { +void StateTransitionMonitor::beforeTakingTransition(InterpreterImpl* impl, const XERCESC_NS::DOMElement* transition) { std::lock_guard lock(_mutex); std::cerr << "Transition: " << uscxml::DOMUtils::xPathForNode(transition) << std::endl; } -void StateTransitionMonitor::onStableConfiguration() { +void StateTransitionMonitor::onStableConfiguration(InterpreterImpl* impl) { std::lock_guard lock(_mutex); std::cerr << "Stable Config: { "; // printNodeSet(_interpreter.getConfiguration()); std::cerr << " }" << std::endl; } -void StateTransitionMonitor::beforeProcessingEvent(const uscxml::Event& event) { +void StateTransitionMonitor::beforeProcessingEvent(InterpreterImpl* impl, const uscxml::Event& event) { std::lock_guard lock(_mutex); switch (event.eventType) { case uscxml::Event::INTERNAL: @@ -267,23 +271,23 @@ void StateTransitionMonitor::beforeProcessingEvent(const uscxml::Event& event) } } -void StateTransitionMonitor::beforeExecutingContent(const XERCESC_NS::DOMElement* element) { +void StateTransitionMonitor::beforeExecutingContent(InterpreterImpl* impl, const XERCESC_NS::DOMElement* element) { std::lock_guard lock(_mutex); std::cerr << "Executable Content: " << DOMUtils::xPathForNode(element) << std::endl; } -void StateTransitionMonitor::beforeExitingState(const XERCESC_NS::DOMElement* state) { +void StateTransitionMonitor::beforeExitingState(InterpreterImpl* impl, const XERCESC_NS::DOMElement* state) { std::lock_guard lock(_mutex); std::cerr << "Exiting: " << (HAS_ATTR(state, "id") ? ATTR(state, "id") : DOMUtils::xPathForNode(state)) << std::endl; } -void StateTransitionMonitor::beforeEnteringState(const XERCESC_NS::DOMElement* state) { +void StateTransitionMonitor::beforeEnteringState(InterpreterImpl* impl, const XERCESC_NS::DOMElement* state) { std::lock_guard lock(_mutex); std::cerr << "Entering: " << (HAS_ATTR(state, "id") ? ATTR(state, "id") : DOMUtils::xPathForNode(state)) << std::endl; } -void StateTransitionMonitor::beforeMicroStep() { +void StateTransitionMonitor::beforeMicroStep(InterpreterImpl* impl) { std::lock_guard lock(_mutex); std::cerr << "Config: {"; // printNodeSet(_interpreter.getConfiguration()); diff --git a/src/uscxml/Interpreter.h b/src/uscxml/Interpreter.h index 4dbefd8..0d7573c 100644 --- a/src/uscxml/Interpreter.h +++ b/src/uscxml/Interpreter.h @@ -221,7 +221,12 @@ public: /** * Attach a monitor to make more details of the interpreter observable. */ - void setMonitor(InterpreterMonitor* monitor); + void addMonitor(InterpreterMonitor* monitor); + + /** + * Remove a monitor that was attached previously. + */ + void removeMonitor(InterpreterMonitor* monitor); /** * Return the actual implementation of the Interperter. diff --git a/src/uscxml/debug/Breakpoint.cpp b/src/uscxml/debug/Breakpoint.cpp new file mode 100644 index 0000000..1e40ee3 --- /dev/null +++ b/src/uscxml/debug/Breakpoint.cpp @@ -0,0 +1,267 @@ +/** + * @file + * @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see . + * @endcond + */ + +#include "uscxml/debug/Breakpoint.h" +#include "uscxml/Interpreter.h" +#include "uscxml/interpreter/InterpreterImpl.h" +#include "uscxml/util/DOM.h" +#include "uscxml/util/String.h" + +namespace uscxml { + +Breakpoint::Breakpoint(const Data& data) { + enabled = true; + subject = UNDEF_SUBJECT; + when = UNDEF_WHEN; + action = UNDEF_ACTION; + + if (data.hasKey("when")) { + if (false) { + } else if (data.at("when").atom == "before") { + when = BEFORE; + } else if (data.at("when").atom == "after") { + when = AFTER; + } else if (data.at("when").atom == "on") { + when = ON; + } + } + + if (data.hasKey("action")) { + if (false) { + } else if (data.at("action").atom == "enter") { + action = ENTER; + } else if (data.at("action").atom == "exit") { + action = EXIT; + } else if (data.at("action").atom == "invoke") { + action = INVOKE; + } else if (data.at("action").atom == "cancel") { + action = UNINVOKE; + } + } + + if (data.hasKey("subject")) { + if (false) { + } else if (data.at("subject").atom == "state") { + subject = STATE; + } else if (data.at("subject").atom == "transition") { + subject = TRANSITION; + } else if (data.at("subject").atom == "stable") { + subject = STABLE; + } else if (data.at("subject").atom == "microstep") { + subject = MICROSTEP; + } else if (data.at("subject").atom == "event") { + subject = EVENT; + } else if (data.at("subject").atom == "invoker") { + subject = INVOKER; + } else if (data.at("subject").atom == "executable") { + subject = EXECUTABLE; + } + } + + if (data.hasKey("condition")) + condition = data.at("condition").atom; + + if (data.hasKey("invokeId")) + invokeId = data.at("invokeId").atom; + + if (data.hasKey("invokeType")) + invokeType = data.at("invokeType").atom; + + if (data.hasKey("eventName")) + eventName = data.at("eventName").atom; + + if (data.hasKey("executableName")) + executableName = data.at("executableName").atom; + + if (data.hasKey("executableXPath")) + executableXPath = data.at("executableXPath").atom; + + if (data.hasKey("stateId")) + stateId = data.at("stateId").atom; + + if (data.hasKey("transSourceId")) + transSourceId = data.at("transSourceId").atom; + + if (data.hasKey("transTargetId")) + transTargetId = data.at("transTargetId").atom; + +} + +Data Breakpoint::toData() const { + Data data; + + switch (subject) { + case STATE: + data.compound["subject"] = Data("state", Data::VERBATIM); + break; + case TRANSITION: + data.compound["subject"] = Data("transition", Data::VERBATIM); + break; + case STABLE: + data.compound["subject"] = Data("stable", Data::VERBATIM); + break; + case MICROSTEP: + data.compound["subject"] = Data("microstep", Data::VERBATIM); + break; + case EVENT: + data.compound["subject"] = Data("event", Data::VERBATIM); + break; + case INVOKER: + data.compound["subject"] = Data("invoker", Data::VERBATIM); + break; + case EXECUTABLE: + data.compound["subject"] = Data("executable", Data::VERBATIM); + break; + default: + break; + } + + switch (when) { + case AFTER: + data.compound["when"] = Data("after", Data::VERBATIM); + break; + case BEFORE: + data.compound["when"] = Data("before", Data::VERBATIM); + break; + case ON: + data.compound["when"] = Data("on", Data::VERBATIM); + break; + default: + break; + } + + switch (action) { + case ENTER: + data.compound["action"] = Data("enter", Data::VERBATIM); + break; + case EXIT: + data.compound["action"] = Data("exit", Data::VERBATIM); + break; + case INVOKE: + data.compound["action"] = Data("invoke", Data::VERBATIM); + break; + case UNINVOKE: + data.compound["action"] = Data("cancel", Data::VERBATIM); + break; + default: + break; + } + + if (invokeId.length() > 0) + data.compound["invokeId"] = Data(invokeId, Data::VERBATIM); + + if (invokeType.length() > 0) + data.compound["invokeType"] = Data(invokeType, Data::VERBATIM); + + if (eventName.length() > 0) + data.compound["eventName"] = Data(eventName, Data::VERBATIM); + + if (executableName.length() > 0) + data.compound["executableName"] = Data(executableName, Data::VERBATIM); + + if (executableXPath.length() > 0) { + data.compound["executableXPath"] = Data(executableXPath, Data::VERBATIM); + } + + if (element) + data.compound["xpath"] = Data(DOMUtils::xPathForNode(element, "*"), Data::VERBATIM); + + if (stateId.length() > 0) + data.compound["stateId"] = Data(stateId, Data::VERBATIM); + + if (transSourceId.length() > 0) + data.compound["transSourceId"] = Data(transSourceId, Data::VERBATIM); + + if (transTargetId.length() > 0) + data.compound["transTargetId"] = Data(transTargetId, Data::VERBATIM); + + if (condition.length() > 0) + data.compound["condition"] = Data(condition, Data::VERBATIM); + + return data; +} + +bool Breakpoint::matches(Interpreter interpreter, const Breakpoint& other) const { + // would we match the given breakpoint? + + if (subject != UNDEF_SUBJECT && + other.subject != subject) + return false; // subject does not match + + if (when != UNDEF_WHEN && + other.when != when) + return false; // time does not match + + if (action != UNDEF_ACTION && + other.action != action) + return false; // action does not match + + // when we have a qualifier it has to match + if(invokeId.length() > 0 && invokeId != other.invokeId) { + return false; + } + + if(invokeType.length() > 0 && invokeType != other.invokeType) { + return false; + } + + if(stateId.length() > 0 && stateId != other.stateId) { + return false; + } + + if(eventName.length() > 0 && !nameMatch(eventName, other.eventName)) { + return false; + } + + if(executableName.length() > 0 && executableName != other.executableName) { + return false; + } + +#if 0 + if(executableXPath.length()) { + Arabica::XPath::NodeSet nodes; + try { + nodes = interpreter.getNodeSetForXPath(executableXPath); + } catch (...) { + return false; + } + return InterpreterImpl::isMember(other.element, nodes); + } +#endif + + if(transSourceId.length() > 0 && transSourceId != other.transSourceId) { + return false; + } + + if(transTargetId.length() > 0 && transTargetId != other.transTargetId) { + return false; + } + + if (condition.length() > 0) { + try { + interpreter.getImpl()->isTrue(condition); + } catch (...) { + return false; + } + } + + return true; +} + +} \ No newline at end of file diff --git a/src/uscxml/debug/Breakpoint.h b/src/uscxml/debug/Breakpoint.h new file mode 100644 index 0000000..3809663 --- /dev/null +++ b/src/uscxml/debug/Breakpoint.h @@ -0,0 +1,100 @@ +/** + * @file + * @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see . + * @endcond + */ + +#ifndef BREAKPOINT_H_VR7K7T1X +#define BREAKPOINT_H_VR7K7T1X + +#include // for string +#include "uscxml/Common.h" // for USCXML_API +#include "uscxml/Interpreter.h" +//#include "DOM/Element.hpp" // for Element +#include "uscxml/messages/Data.h" // for Data + +// forward declare +namespace XERCESC_NS { + class DOMElement; +} + +namespace uscxml { + +class USCXML_API Breakpoint { +public: + + enum When { + UNDEF_WHEN, AFTER, BEFORE, ON + }; + + enum Subject { + UNDEF_SUBJECT, STATE, TRANSITION, STABLE, MICROSTEP, EVENT, INVOKER, EXECUTABLE + }; + + enum Action { + UNDEF_ACTION, ENTER, EXIT, INVOKE, UNINVOKE + }; + + Breakpoint() { + subject = UNDEF_SUBJECT; + when = UNDEF_WHEN; + action = UNDEF_ACTION; + } + Breakpoint(const Data& data); + + // would we match the given breakpoint as well? + bool matches(Interpreter interpreter, const Breakpoint& other) const; + + Data toData() const; + + bool operator<(const Breakpoint& other) const { + return (toData() < other.toData()); + } + + operator bool() { + return (subject != UNDEF_SUBJECT || + when != UNDEF_WHEN || + action != UNDEF_ACTION); + } + + mutable bool enabled; + + When when; + Subject subject; + Action action; + + const XERCESC_NS::DOMElement* element = NULL; + + std::string invokeId; + std::string invokeType; + + std::string eventName; + + std::string executableName; + std::string executableXPath; + + std::string stateId; + std::string transSourceId; + std::string transTargetId; + + std::string condition; +}; + +} + + + +#endif /* end of include guard: BREAKPOINT_H_VR7K7T1X */ diff --git a/src/uscxml/debug/DebugSession.cpp b/src/uscxml/debug/DebugSession.cpp new file mode 100644 index 0000000..ef4d469 --- /dev/null +++ b/src/uscxml/debug/DebugSession.cpp @@ -0,0 +1,373 @@ +/** + * @file + * @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see . + * @endcond + */ + +#include "uscxml/debug/DebugSession.h" +#include "uscxml/debug/Debugger.h" +#include "uscxml/util/Predicates.h" + +namespace uscxml { + +void DebugSession::checkBreakpoints(const std::list qualifiedBreakpoints) { + std::list::const_iterator qualifiedBreakpointIter = qualifiedBreakpoints.begin(); + + if (!_breakpointsEnabled) + return; + + while(qualifiedBreakpointIter != qualifiedBreakpoints.end()) { + const Breakpoint& qualifiedBreakpoint = *qualifiedBreakpointIter++; + + // check if one of the user-supplied breakpoints match + bool userBreakpointMatched = false; + Data replyData; + + if (_skipTo) { + if (_skipTo.matches(_interpreter, qualifiedBreakpoint)) { + replyData.compound["breakpoint"] = _skipTo.toData(); + replyData.compound["qualified"] = qualifiedBreakpoint.toData(); + breakExecution(replyData); + _skipTo = Breakpoint(); + } + continue; + } + + std::set::const_iterator breakpointIter = _breakPoints.begin(); + while(breakpointIter != _breakPoints.end()) { + const Breakpoint& breakpoint = *breakpointIter++; + if (!breakpoint.enabled) + continue; + if (breakpoint.matches(_interpreter, qualifiedBreakpoint)) { + // do we have a condition? + + replyData.compound["breakpoint"] = breakpoint.toData(); + replyData.compound["qualified"] = qualifiedBreakpoint.toData(); + + userBreakpointMatched = true; + breakExecution(replyData); + } + } + if (_isStepping && !userBreakpointMatched) { + replyData.compound["qualified"] = qualifiedBreakpoint.toData(); + breakExecution(replyData); + + } + } +} + +void DebugSession::breakExecution(Data replyData) { + std::lock_guard lock(_mutex); + + std::list configuration = _interpreter.getConfiguration(); + for (auto state : configuration) { + if (HAS_ATTR(state, "id")) { + replyData.compound["activeStates"].array.push_back(Data(ATTR(state, "id"), Data::VERBATIM)); + if (isAtomic(state)) { + replyData.compound["basicStates"].array.push_back(Data(ATTR(state, "id"), Data::VERBATIM)); + } + } + } + + replyData.compound["replyType"] = Data("breakpoint", Data::VERBATIM); + _debugger->pushData(shared_from_this(), replyData); + _resumeCond.wait(_mutex); +} + +Data DebugSession::debugPrepare(const Data& data) { + Data replyData; + + if (!data.hasKey("xml") && !data.hasKey("url")) { + replyData.compound["status"] = Data("failure", Data::VERBATIM); + replyData.compound["reason"] = Data("No XML or URL given", Data::VERBATIM); + return replyData; + } + + debugStop(data); + + _isAttached = false; + + if (data.hasKey("xml")) { + _interpreter = Interpreter::fromXML(data.at("xml").atom, (data.hasKey("url") ? data.at("url").atom : "")); + } else if (data.hasKey("url")) { + _interpreter = Interpreter::fromURL(data.at("url").atom); + } else { + _interpreter = Interpreter(); + } + + if (_interpreter) { + // register ourself as a monitor + _interpreter.addMonitor(_debugger); + _debugger->attachSession(_interpreter.getImpl().get(), shared_from_this()); + + replyData.compound["status"] = Data("success", Data::VERBATIM); + } else { + replyData.compound["status"] = Data("failure", Data::VERBATIM); + } + + return replyData; +} + +Data DebugSession::debugAttach(const Data& data) { + Data replyData; + _isAttached = true; + + if (!data.hasKey("attach")) { + replyData.compound["status"] = Data("failure", Data::VERBATIM); + replyData.compound["reason"] = Data("No id to attach to given", Data::VERBATIM); + return replyData; + } + + std::string interpreterId = data.at("attach").atom; + bool interpreterFound = false; + + // find interpreter for sessionid + std::map > instances = InterpreterImpl::getInstances(); + for (auto weakInstance : instances) { + + std::shared_ptr instance = weakInstance.second.lock(); + if (instance && instance->getSessionId() == interpreterId) { + _interpreter = instance; + _debugger->attachSession(_interpreter.getImpl().get(), shared_from_this()); + interpreterFound = true; + break; + } + } + + if (!interpreterFound) { + replyData.compound["status"] = Data("failure", Data::VERBATIM); + replyData.compound["reason"] = Data("No interpreter with given id found", Data::VERBATIM); + } else { + replyData.compound["xml"].node = _interpreter.getImpl()->getDocument(); + replyData.compound["status"] = Data("success", Data::VERBATIM); + } + + return replyData; +} + +Data DebugSession::debugDetach(const Data& data) { + Data replyData; + _isAttached = false; + + _debugger->detachSession(_interpreter.getImpl().get()); + replyData.compound["status"] = Data("success", Data::VERBATIM); + return replyData; +} + +Data DebugSession::debugStart(const Data& data) { + Data replyData; + + if (_isAttached) { + replyData.compound["reason"] = Data("Already started when attached", Data::VERBATIM); + replyData.compound["status"] = Data("failure", Data::VERBATIM); + } else if (!_interpreter) { + replyData.compound["reason"] = Data("No interpreter attached or loaded", Data::VERBATIM); + replyData.compound["status"] = Data("failure", Data::VERBATIM); + } else { + //_interpreter.start(); + assert(false); + + replyData.compound["status"] = Data("success", Data::VERBATIM); + } + + return replyData; +} + +Data DebugSession::debugStop(const Data& data) { + Data replyData; + + if (_interpreter) { + // detach from old intepreter + _debugger->detachSession(_interpreter.getImpl().get()); + } + + if (_interpreter && !_isAttached) + assert(false); + //_interpreter.stop(); + // unblock + _resumeCond.notify_all(); + + _skipTo = Breakpoint(); + replyData.compound["status"] = Data("success", Data::VERBATIM); + + // calls destructor + _interpreter = Interpreter(); + + return replyData; +} + +Data DebugSession::debugStep(const Data& data) { + std::lock_guard lock(_mutex); + + stepping(true); + _resumeCond.notify_one(); + + Data replyData; + if (_interpreter) { + // register ourself as a monitor + if (!_isRunning) + //_interpreter.start(); + assert(false); + + replyData.compound["status"] = Data("success", Data::VERBATIM); + } else { + replyData.compound["status"] = Data("failure", Data::VERBATIM); + } + return replyData; +} + +Data DebugSession::debugResume(const Data& data) { + std::lock_guard lock(_mutex); + + stepping(false); + + Data replyData; + replyData.compound["status"] = Data("success", Data::VERBATIM); + + _resumeCond.notify_one(); + return replyData; +} + + +Data DebugSession::debugPause(const Data& data) { + std::lock_guard lock(_mutex); + + _skipTo = Breakpoint(); + stepping(true); + + Data replyData; + replyData.compound["status"] = Data("success", Data::VERBATIM); + + return replyData; +} + +Data DebugSession::skipToBreakPoint(const Data& data) { + std::lock_guard lock(_mutex); + + _skipTo = Breakpoint(data); + + Data replyData; + replyData.compound["status"] = Data("success", Data::VERBATIM); + + _resumeCond.notify_one(); + return replyData; +} + +Data DebugSession::addBreakPoint(const Data& data) { + Breakpoint breakpoint(data); + + Data replyData; + if (_breakPoints.find(breakpoint) == _breakPoints.end()) { + _breakPoints.insert(breakpoint); + replyData.compound["status"] = Data("success", Data::VERBATIM); + + } else { + replyData.compound["reason"] = Data("Breakpoint already exists", Data::VERBATIM); + replyData.compound["status"] = Data("failure", Data::VERBATIM); + } + return replyData; +} + +Data DebugSession::removeBreakPoint(const Data& data) { + Breakpoint breakpoint(data); + + Data replyData; + if (_breakPoints.find(breakpoint) != _breakPoints.end()) { + _breakPoints.erase(breakpoint); + replyData.compound["status"] = Data("success", Data::VERBATIM); + } else { + replyData.compound["reason"] = Data("No such breakpoint", Data::VERBATIM); + replyData.compound["status"] = Data("failure", Data::VERBATIM); + } + return replyData; +} + +Data DebugSession::enableBreakPoint(const Data& data) { + Breakpoint breakpoint(data); + + Data replyData; + if (_breakPoints.find(breakpoint) != _breakPoints.end()) { + _breakPoints.find(breakpoint)->enabled = true; + replyData.compound["status"] = Data("success", Data::VERBATIM); + } else { + replyData.compound["reason"] = Data("No such breakpoint", Data::VERBATIM); + replyData.compound["status"] = Data("failure", Data::VERBATIM); + } + + return replyData; +} +Data DebugSession::disableBreakPoint(const Data& data) { + Breakpoint breakpoint(data); + + Data replyData; + if (_breakPoints.find(breakpoint) != _breakPoints.end()) { + _breakPoints.find(breakpoint)->enabled = false; + replyData.compound["status"] = Data("success", Data::VERBATIM); + } else { + replyData.compound["reason"] = Data("No such breakpoint", Data::VERBATIM); + replyData.compound["status"] = Data("failure", Data::VERBATIM); + } + + return replyData; +} +Data DebugSession::enableAllBreakPoints() { + Data replyData; + + _breakpointsEnabled = true; + replyData.compound["status"] = Data("success", Data::VERBATIM); + + return replyData; +} +Data DebugSession::disableAllBreakPoints() { + Data replyData; + + _breakpointsEnabled = false; + replyData.compound["status"] = Data("success", Data::VERBATIM); + + return replyData; +} + +Data DebugSession::debugEval(const Data& data) { + Data replyData; + + if (!data.hasKey("expression")) { + replyData.compound["status"] = Data("failure", Data::VERBATIM); + replyData.compound["reason"] = Data("No expression given", Data::VERBATIM); + return replyData; + } + + std::string expr = data.at("expression").atom; + + if (!_interpreter) { + replyData.compound["status"] = Data("failure", Data::VERBATIM); + replyData.compound["reason"] = Data("No interpreter running", Data::VERBATIM); + } else if (!_interpreter.getImpl()->_dataModel) { + replyData.compound["status"] = Data("failure", Data::VERBATIM); + replyData.compound["reason"] = Data("No datamodel available", Data::VERBATIM); + } else { + try { + replyData.compound["eval"] = _interpreter.getImpl()->getAsData(expr); + } catch (Event e) { + replyData.compound["eval"] = e.data; + replyData.compound["eval"].compound["error"] = Data(e.name, Data::VERBATIM); + } + replyData.compound["status"] = Data("success", Data::VERBATIM); + } + return replyData; +} + + +} \ No newline at end of file diff --git a/src/uscxml/debug/DebugSession.h b/src/uscxml/debug/DebugSession.h new file mode 100644 index 0000000..a1ecaa0 --- /dev/null +++ b/src/uscxml/debug/DebugSession.h @@ -0,0 +1,107 @@ +/** + * @file + * @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see . + * @endcond + */ + +#ifndef DEBUGSESSION_H_M8YHEGV6 +#define DEBUGSESSION_H_M8YHEGV6 + +#include "uscxml/debug/Breakpoint.h" +#include "uscxml/Interpreter.h" +#include +#include +#include +#include + +namespace uscxml { + +class Debugger; + +class USCXML_API DebugSession : public std::enable_shared_from_this { +public: + DebugSession() { + _isRunning = false; + _isStepping = false; + _isAttached = false; + _breakpointsEnabled = true; + _markedForDeletion = false; + _debugger = NULL; + } + + void stepping(bool enable) { + _isStepping = enable; + } + + void checkBreakpoints(const std::list qualifiedBreakpoints); + + Data debugPrepare(const Data& data); + Data debugAttach(const Data& data); + Data debugDetach(const Data& data); + Data debugStart(const Data& data); + Data debugStop(const Data& data); + Data debugStep(const Data& data); + Data debugResume(const Data& data); + Data debugPause(const Data& data); + Data skipToBreakPoint(const Data& data); + Data addBreakPoint(const Data& data); + Data removeBreakPoint(const Data& data); + Data enableBreakPoint(const Data& data); + Data disableBreakPoint(const Data& data); + Data enableAllBreakPoints(); + Data disableAllBreakPoints(); + Data debugEval(const Data& data); + + void setDebugger(Debugger* debugger) { + _debugger = debugger; + } + + Interpreter getInterpreter() { + return _interpreter; + } + + void markForDeletion(bool mark) { + _markedForDeletion = mark; + } + +protected: + void breakExecution(Data replyData); + + bool _isStepping; + bool _isAttached; + bool _breakpointsEnabled; + + std::condition_variable_any _resumeCond; + std::recursive_mutex _runMutex; + std::recursive_mutex _mutex; + + std::thread* _interpreterThread = NULL; + bool _isRunning; + + bool _markedForDeletion; + Debugger* _debugger; + Interpreter _interpreter; + std::set _breakPoints; + Breakpoint _skipTo; + + friend class Debugger; +}; + + +} + + +#endif /* end of include guard: DEBUGSESSION_H_M8YHEGV6 */ diff --git a/src/uscxml/debug/Debugger.cpp b/src/uscxml/debug/Debugger.cpp new file mode 100644 index 0000000..09c21e7 --- /dev/null +++ b/src/uscxml/debug/Debugger.cpp @@ -0,0 +1,258 @@ +/** +* @file +* @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) +* @copyright Simplified BSD +* +* @cond +* This program is free software: you can redistribute it and/or modify +* it under the terms of the FreeBSD license as published by the FreeBSD +* project. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +* +* You should have received a copy of the FreeBSD license along with this +* program. If not, see . +* @endcond +*/ + +#include "uscxml/debug/Debugger.h" +#include "uscxml/util/DOM.h" +#include "uscxml/util/Predicates.h" +#include "uscxml/debug/DebugSession.h" + +namespace uscxml { + +void Debugger::afterCompletion(InterpreterImpl* impl) { + std::shared_ptr session = getSession(impl); + if (!session) + return; + + Data msg; + msg.compound["replyType"] = Data("finished", Data::VERBATIM); + pushData(session, msg); +} + +void Debugger::beforeCompletion(InterpreterImpl* impl) {} + +std::list Debugger::getQualifiedStateBreakpoints(InterpreterImpl* impl, const XERCESC_NS::DOMElement* state, Breakpoint breakpointTemplate) { + std::list breakpoints; + + Breakpoint bp = breakpointTemplate; // copy base as template + bp.stateId = ATTR(state, "id"); + bp.element = state; + bp.subject = Breakpoint::STATE; + breakpoints.push_back(bp); + + return breakpoints; +} + +std::list Debugger::getQualifiedInvokeBreakpoints(InterpreterImpl* impl, const XERCESC_NS::DOMElement* invokeElem, const std::string invokeId, Breakpoint breakpointTemplate) { + std::list breakpoints; + + Breakpoint bp = breakpointTemplate; // copy base as template + bp.subject = Breakpoint::INVOKER; + bp.element = invokeElem; + bp.invokeId = invokeId; + + if (HAS_ATTR(invokeElem, "type")) { + bp.invokeType = ATTR(invokeElem, "type"); + } else if (HAS_ATTR(invokeElem, "typeexpr")) { + bp.invokeType = impl->evalAsData(ATTR(invokeElem, "typeexpr")).atom; + } + + breakpoints.push_back(bp); + + return breakpoints; +} + +std::list Debugger::getQualifiedTransBreakpoints(InterpreterImpl* impl, const XERCESC_NS::DOMElement* transition, Breakpoint breakpointTemplate) { + std::list breakpoints; + + XERCESC_NS::DOMElement* source = getSourceState(transition); + std::list targets = getTargetStates(transition, impl->_scxml); + + for (auto target : targets) { + + Breakpoint bp = breakpointTemplate; // copy base as template + bp.element = transition; + bp.transSourceId = ATTR(source, "id"); + bp.transTargetId = ATTR(target, "id"); + bp.subject = Breakpoint::TRANSITION; + + breakpoints.push_back(bp); + } + + return breakpoints; +} + +void Debugger::beforeTakingTransition(InterpreterImpl* impl, const XERCESC_NS::DOMElement* transition) { + handleTransition(impl, transition, Breakpoint::BEFORE); +} +void Debugger::afterTakingTransition(InterpreterImpl* impl, const XERCESC_NS::DOMElement* transition) { + handleTransition(impl, transition, Breakpoint::AFTER); +} +void Debugger::beforeExecutingContent(InterpreterImpl* impl, const XERCESC_NS::DOMElement* execContent) { + handleExecutable(impl, execContent, Breakpoint::BEFORE); +} +void Debugger::afterExecutingContent(InterpreterImpl* impl, const XERCESC_NS::DOMElement* execContent) { + handleExecutable(impl, execContent, Breakpoint::AFTER); +} +void Debugger::beforeExitingState(InterpreterImpl* impl, const XERCESC_NS::DOMElement* state) { + handleState(impl, state, Breakpoint::BEFORE, Breakpoint::EXIT); +} +void Debugger::afterExitingState(InterpreterImpl* impl, const XERCESC_NS::DOMElement* state) { + handleState(impl, state, Breakpoint::AFTER, Breakpoint::EXIT); +} +void Debugger::beforeEnteringState(InterpreterImpl* impl, const XERCESC_NS::DOMElement* state) { + handleState(impl, state, Breakpoint::BEFORE, Breakpoint::ENTER); +} +void Debugger::afterEnteringState(InterpreterImpl* impl, const XERCESC_NS::DOMElement* state) { + handleState(impl, state, Breakpoint::AFTER, Breakpoint::ENTER); +} +void Debugger::beforeUninvoking(InterpreterImpl* impl, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) { + handleInvoke(impl, invokeElem, invokeid, Breakpoint::BEFORE, Breakpoint::UNINVOKE); +} +void Debugger::afterUninvoking(InterpreterImpl* impl, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) { + handleInvoke(impl, invokeElem, invokeid, Breakpoint::AFTER, Breakpoint::UNINVOKE); +} +void Debugger::beforeInvoking(InterpreterImpl* impl, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) { + handleInvoke(impl, invokeElem, invokeid, Breakpoint::BEFORE, Breakpoint::INVOKE); +} +void Debugger::afterInvoking(InterpreterImpl* impl, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) { + handleInvoke(impl, invokeElem, invokeid, Breakpoint::AFTER, Breakpoint::INVOKE); +} +void Debugger::onStableConfiguration(InterpreterImpl* impl) { + handleStable(impl, Breakpoint::ON); +} +void Debugger::beforeMicroStep(InterpreterImpl* impl) { + handleMicrostep(impl, Breakpoint::BEFORE); +} +void Debugger::afterMicroStep(InterpreterImpl* impl) { + handleMicrostep(impl, Breakpoint::AFTER); +} +void Debugger::beforeProcessingEvent(InterpreterImpl* impl, const Event& event) { + handleEvent(impl, event, Breakpoint::BEFORE); +} + +void Debugger::handleExecutable(InterpreterImpl* impl, + const XERCESC_NS::DOMElement* execContentElem, + Breakpoint::When when) { + std::shared_ptr session = getSession(impl); + if (!session) + return; + if (!session->_isRunning) + return; + + std::list breakpoints; + + Breakpoint breakpoint; + breakpoint.when = when; + breakpoint.element = execContentElem; + breakpoint.executableName = X(execContentElem->getLocalName()).str(); + breakpoint.subject = Breakpoint::EXECUTABLE; + breakpoints.push_back(breakpoint); + + session->checkBreakpoints(breakpoints); + +} + +void Debugger::handleEvent(InterpreterImpl* impl, const Event& event, Breakpoint::When when) { + std::shared_ptr session = getSession(impl); + if (!session) + return; + if (!session->_isRunning) + return; + + std::list breakpoints; + + Breakpoint breakpoint; + breakpoint.when = when; + breakpoint.eventName = event.name; + breakpoint.subject = Breakpoint::EVENT; + breakpoints.push_back(breakpoint); + + session->checkBreakpoints(breakpoints); + +} + +void Debugger::handleStable(InterpreterImpl* impl, Breakpoint::When when) { + std::shared_ptr session = getSession(impl); + if (!session) + return; + if (!session->_isRunning) + return; + + std::list breakpoints; + + Breakpoint breakpoint; + breakpoint.when = when; + breakpoint.subject = Breakpoint::STABLE; + breakpoints.push_back(breakpoint); + + session->checkBreakpoints(breakpoints); +} + +void Debugger::handleMicrostep(InterpreterImpl* impl, Breakpoint::When when) { + std::shared_ptr session = getSession(impl); + if (!session) + return; + if (!session->_isRunning) + return; + + std::list breakpoints; + + Breakpoint breakpoint; + breakpoint.when = when; + breakpoint.subject = Breakpoint::MICROSTEP; + breakpoints.push_back(breakpoint); + + session->checkBreakpoints(breakpoints); +} + +void Debugger::handleTransition(InterpreterImpl* impl, const XERCESC_NS::DOMElement* transition, Breakpoint::When when) { + std::shared_ptr session = getSession(impl); + if (!session) + return; + if (!session->_isRunning) + return; + + Breakpoint breakpointTemplate; + breakpointTemplate.when = when; + std::list qualifiedBreakpoints = getQualifiedTransBreakpoints(impl, transition, breakpointTemplate); + session->checkBreakpoints(qualifiedBreakpoints); +} + +void Debugger::handleState(InterpreterImpl* impl, const XERCESC_NS::DOMElement* state, Breakpoint::When when, Breakpoint::Action action) { + std::shared_ptr session = getSession(impl); + if (!session) + return; + if (!session->_isRunning) + return; + + Breakpoint breakpointTemplate; + breakpointTemplate.when = when; + breakpointTemplate.action = action; + std::list qualifiedBreakpoints = getQualifiedStateBreakpoints(impl, state, breakpointTemplate); + session->checkBreakpoints(qualifiedBreakpoints); + +} + +void Debugger::handleInvoke(InterpreterImpl* impl, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeId, Breakpoint::When when, Breakpoint::Action action) { + std::shared_ptr session = getSession(impl); + if (!session) + return; + if (!session->_isRunning) + return; + + Breakpoint breakpointTemplate; + breakpointTemplate.when = when; + breakpointTemplate.action = action; + std::list qualifiedBreakpoints = getQualifiedInvokeBreakpoints(impl, invokeElem, invokeId, breakpointTemplate); + session->checkBreakpoints(qualifiedBreakpoints); + +} + + +} \ No newline at end of file diff --git a/src/uscxml/debug/Debugger.h b/src/uscxml/debug/Debugger.h new file mode 100644 index 0000000..08136d6 --- /dev/null +++ b/src/uscxml/debug/Debugger.h @@ -0,0 +1,116 @@ +/** + * @file + * @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see . + * @endcond + */ + +#ifndef DEBUGGERMONITOR_H_Z050WPFH +#define DEBUGGERMONITOR_H_Z050WPFH + +#include "uscxml/messages/Data.h" // for Data +#include "uscxml/messages/Event.h" // for Event +#include "uscxml/interpreter/InterpreterImpl.h" +#include "uscxml/debug/Breakpoint.h" + +namespace uscxml { + +class DebugSession; + +class USCXML_API Debugger : public InterpreterMonitor { +public: + Debugger() { + } + virtual ~Debugger() {} + + virtual void attachSession(InterpreterImpl* impl, std::shared_ptr session) { + std::lock_guard lock(_sessionMutex); + _sessionForInterpreter[impl] = session; + } + + virtual void detachSession(InterpreterImpl* impl) { + std::lock_guard lock(_sessionMutex); + _sessionForInterpreter.erase(impl); + } + + virtual std::shared_ptr getSession(InterpreterImpl* impl) { + std::lock_guard lock(_sessionMutex); + if (_sessionForInterpreter.find(impl) != _sessionForInterpreter.end()) + return _sessionForInterpreter[impl]; + return std::shared_ptr(); + } + + virtual void pushData(std::shared_ptr session, Data pushData) = 0; + + // InterpreterMonitor + virtual void beforeProcessingEvent(InterpreterImpl* impl, const Event& event); + virtual void beforeMicroStep(InterpreterImpl* impl); + virtual void beforeExitingState(InterpreterImpl* impl, const XERCESC_NS::DOMElement* state); + virtual void afterExitingState(InterpreterImpl* impl, const XERCESC_NS::DOMElement* state); + virtual void beforeExecutingContent(InterpreterImpl* impl, const XERCESC_NS::DOMElement* execContent); + virtual void afterExecutingContent(InterpreterImpl* impl, const XERCESC_NS::DOMElement* execContent); + virtual void beforeUninvoking(InterpreterImpl* impl, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid); + virtual void afterUninvoking(InterpreterImpl* impl, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid); + virtual void beforeTakingTransition(InterpreterImpl* impl, const XERCESC_NS::DOMElement* transition); + virtual void afterTakingTransition(InterpreterImpl* impl, const XERCESC_NS::DOMElement* transition); + virtual void beforeEnteringState(InterpreterImpl* impl, const XERCESC_NS::DOMElement* state); + virtual void afterEnteringState(InterpreterImpl* impl, const XERCESC_NS::DOMElement* state); + virtual void beforeInvoking(InterpreterImpl* impl, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid); + virtual void afterInvoking(InterpreterImpl* impl, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid); + virtual void afterMicroStep(InterpreterImpl* impl); + virtual void onStableConfiguration(InterpreterImpl* impl); + virtual void beforeCompletion(InterpreterImpl* impl); + virtual void afterCompletion(InterpreterImpl* impl); + +protected: + + void handleTransition(InterpreterImpl* impl, + const XERCESC_NS::DOMElement* transition, + Breakpoint::When when); + void handleState(InterpreterImpl* impl, + const XERCESC_NS::DOMElement* state, + Breakpoint::When when, + Breakpoint::Action action); + void handleInvoke(InterpreterImpl* impl, + const XERCESC_NS::DOMElement* invokeElem, + const std::string& invokeId, + Breakpoint::When when, + Breakpoint::Action action); + void handleExecutable(InterpreterImpl* impl, + const XERCESC_NS::DOMElement* execContentElem, + Breakpoint::When when); + void handleStable(InterpreterImpl* impl, Breakpoint::When when); + void handleMicrostep(InterpreterImpl* impl, Breakpoint::When when); + void handleEvent(InterpreterImpl* impl, const Event& event, Breakpoint::When when); + + std::list getQualifiedTransBreakpoints(InterpreterImpl* impl, + const XERCESC_NS::DOMElement* transition, + Breakpoint breakpointTemplate); + std::list getQualifiedStateBreakpoints(InterpreterImpl* impl, + const XERCESC_NS::DOMElement* state, + Breakpoint breakpointTemplate); + std::list getQualifiedInvokeBreakpoints(InterpreterImpl* impl, + const XERCESC_NS::DOMElement* invokeElem, + const std::string invokeId, + Breakpoint breakpointTemplate); + + std::recursive_mutex _sessionMutex; + std::map > _sessionForInterpreter; +}; + +} + + +#endif /* end of include guard: DEBUGGERMONITOR_H_Z050WPFH */ diff --git a/src/uscxml/debug/DebuggerServlet.cpp b/src/uscxml/debug/DebuggerServlet.cpp new file mode 100644 index 0000000..2b035a6 --- /dev/null +++ b/src/uscxml/debug/DebuggerServlet.cpp @@ -0,0 +1,261 @@ +/** + * @file + * @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see . + * @endcond + */ + +#include "uscxml/debug/DebuggerServlet.h" +#include "uscxml/debug/DebugSession.h" +#include "uscxml/util/UUID.h" +#include + +namespace uscxml { + +void DebuggerServlet::pushData(std::shared_ptr session, Data pushData) { + std::cout << "trying to push " << pushData.at("replyType").atom << std::endl; + + if (!session) { + if (_sendQueues.size() > 0) // logging is not aware of its interpreter + _sendQueues.begin()->second.push(pushData); + } else { + _sendQueues[session].push(pushData); + } + + serverPushData(session); +} + +void DebuggerServlet::serverPushData(std::shared_ptr session) { + if (_sendQueues[session].isEmpty()) + return; + + if (!_clientConns[session]) + return; + + Data reply = _sendQueues[session].pop(); + std::cout << "pushing " << reply.at("replyType").atom << std::endl; + returnData(_clientConns[session], reply); + _clientConns[session] = HTTPServer::Request(); +} + +void DebuggerServlet::returnData(const HTTPServer::Request& request, Data replyData) { + HTTPServer::Reply reply(request); + + if (!replyData.hasKey("status")) { + replyData.compound["status"] = Data("success", Data::VERBATIM); + } + + std::cout << "<- " << replyData << std::endl; + + reply.content = Data::toJSON(replyData); + reply.headers["Access-Control-Allow-Origin"] = "*"; + reply.headers["Content-Type"] = "application/json"; + HTTPServer::reply(reply); +} + +bool DebuggerServlet::isCORS(const HTTPServer::Request& request) { + return (request.data.at("type").atom == "options" && + request.data.at("header").hasKey("Origin") && + request.data.at("header").hasKey("Access-Control-Request-Method")); +} + +void DebuggerServlet::handleCORS(const HTTPServer::Request& request) { + HTTPServer::Reply corsReply(request); + if (request.data.at("header").hasKey("Origin")) { + corsReply.headers["Access-Control-Allow-Origin"] = request.data.at("header").at("Origin").atom; + } else { + corsReply.headers["Access-Control-Allow-Origin"] = "*"; + } + if (request.data.at("header").hasKey("Access-Control-Request-Method")) + corsReply.headers["Access-Control-Allow-Methods"] = request.data.at("header").at("Access-Control-Request-Method").atom; + if (request.data.at("header").hasKey("Access-Control-Request-Headers")) + corsReply.headers["Access-Control-Allow-Headers"] = request.data.at("header").at("Access-Control-Request-Headers").atom; + + // std::cout << "CORS!" << std::endl << request << std::endl; + HTTPServer::reply(corsReply); +} + +bool DebuggerServlet::httpRecvRequest(const HTTPServer::Request& request) { + if (!request.data.hasKey("path")) + return false; // returnError(request); + + if (isCORS(request)) { + handleCORS(request); + return true; + } + + std::cout << request.data["path"] << ": " << request.data["content"] << std::endl; + + Data replyData; + // process request that don't need a session + if (false) { + } else if (boost::istarts_with(request.data.at("path").atom, "/debug/connect")) { + processConnect(request); + return true; + } else if (boost::starts_with(request.data.at("path").atom, "/debug/sessions")) { + processListSessions(request); + return true; + } + + // get session or return error + if (false) { + } else if (!request.data.at("content").hasKey("session")) { + replyData.compound["status"] = Data("failure", Data::VERBATIM); + replyData.compound["reason"] = Data("No session given", Data::VERBATIM); + } else if (_sessionForId.find(request.data.at("content").at("session").atom) == _sessionForId.end()) { + replyData.compound["status"] = Data("failure", Data::VERBATIM); + replyData.compound["reason"] = Data("No such session", Data::VERBATIM); + } + if (!replyData.empty()) { + returnData(request, replyData); + return true; + } + + std::shared_ptr session = _sessionForId[request.data.at("content").at("session").atom]; + + if (false) { + } else if (boost::starts_with(request.data.at("path").atom, "/debug/poll")) { + // save long-standing client poll + _clientConns[session] = request; + serverPushData(session); + + } else if (boost::starts_with(request.data.at("path").atom, "/debug/disconnect")) { + processDisconnect(request); + + } else if (boost::starts_with(request.data.at("path").atom, "/debug/breakpoint/enable/all")) { + replyData = session->enableAllBreakPoints(); + } else if (boost::starts_with(request.data.at("path").atom, "/debug/breakpoint/disable/all")) { + replyData = session->disableAllBreakPoints(); + } else if (boost::starts_with(request.data.at("path").atom, "/debug/breakpoint/skipto")) { + replyData = session->skipToBreakPoint(request.data["content"]); + } else if (boost::starts_with(request.data.at("path").atom, "/debug/breakpoint/add")) { + replyData = session->addBreakPoint(request.data["content"]); + } else if (boost::starts_with(request.data.at("path").atom, "/debug/breakpoint/remove")) { + replyData = session->removeBreakPoint(request.data["content"]); + } else if (boost::starts_with(request.data.at("path").atom, "/debug/breakpoint/enable")) { + replyData = session->enableBreakPoint(request.data["content"]); + } else if (boost::starts_with(request.data.at("path").atom, "/debug/breakpoint/disable")) { + replyData = session->disableBreakPoint(request.data["content"]); + } else if (boost::starts_with(request.data.at("path").atom, "/debug/stop")) { + replyData = session->debugStop(request.data["content"]); + } else if (boost::starts_with(request.data.at("path").atom, "/debug/prepare")) { + replyData = session->debugPrepare(request.data["content"]); + } else if (boost::starts_with(request.data.at("path").atom, "/debug/attach")) { + replyData = session->debugAttach(request.data["content"]); + } else if (boost::starts_with(request.data.at("path").atom, "/debug/start")) { + replyData = session->debugStart(request.data["content"]); + } else if (boost::starts_with(request.data.at("path").atom, "/debug/step")) { + replyData = session->debugStep(request.data["content"]); + } else if (boost::starts_with(request.data.at("path").atom, "/debug/pause")) { + replyData = session->debugPause(request.data["content"]); + } else if (boost::starts_with(request.data.at("path").atom, "/debug/resume")) { + replyData = session->debugResume(request.data["content"]); + } else if (boost::starts_with(request.data.at("path").atom, "/debug/eval")) { + replyData = session->debugEval(request.data["content"]); + } + + if (!replyData.empty()) { + returnData(request, replyData); + return true; + } + + return true; +} + +// someone connected, create a new session +void DebuggerServlet::processConnect(const HTTPServer::Request& request) { + std::lock_guard lock(_mutex); + std::string sessionId = UUID::getUUID(); + + _sessionForId[sessionId] = std::shared_ptr(new DebugSession()); + _sessionForId[sessionId]->setDebugger(this); + + Data replyData; + replyData.compound["session"] = Data(sessionId, Data::VERBATIM); + replyData.compound["status"] = Data("success", Data::VERBATIM); + returnData(request, replyData); +} + +void DebuggerServlet::processDisconnect(const HTTPServer::Request& request) { + std::lock_guard lock(_mutex); + + Data replyData; + + if (!request.data.at("content").hasKey("session")) { + replyData.compound["status"] = Data("failure", Data::VERBATIM); + replyData.compound["reason"] = Data("No session given", Data::VERBATIM); + returnData(request, replyData); + } + + std::string sessionId = request.data.at("content").at("session").atom; + + if (_sessionForId.find(sessionId) == _sessionForId.end()) { + replyData.compound["status"] = Data("failure", Data::VERBATIM); + replyData.compound["reason"] = Data("No such session", Data::VERBATIM); + } else { + replyData.compound["status"] = Data("success", Data::VERBATIM); + detachSession(_sessionForId[sessionId]->getInterpreter().getImpl().get()); + _sessionForId[sessionId]->debugStop(request.data["content"]); + _clientConns.erase(_sessionForId[sessionId]); + _sendQueues.erase(_sessionForId[sessionId]); + _sessionForId.erase(sessionId); + } + + returnData(request, replyData); +} + +void DebuggerServlet::processListSessions(const HTTPServer::Request& request) { + Data replyData; + + std::map > instances = InterpreterImpl::getInstances(); + for (auto weakInstance : instances) { + + std::shared_ptr instance = weakInstance.second.lock(); + if (instance) { + Data sessionData; + sessionData.compound["name"] = Data(instance->getName(), Data::VERBATIM); + sessionData.compound["id"] = Data(instance->getSessionId(), Data::VERBATIM); + sessionData.compound["source"] = Data(instance->getBaseURL(), Data::VERBATIM); + sessionData.compound["xml"].node = instance->getDocument(); + + replyData.compound["sessions"].array.push_back(sessionData); + } + } + + replyData.compound["status"] = Data("success", Data::VERBATIM); + returnData(request, replyData); +} + +/* +void DebuggerServlet::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) { + + // _sendQueue is thread-safe, not sure about ToString though + + LogMessage msg(severity, + full_filename, + base_filename, + line, + tm_time, + std::string(message, message_len), + ToString(severity, base_filename, line, tm_time, message, message_len)); + msg.compound["replyType"] = Data("log", Data::VERBATIM); + pushData(std::shared_ptr(), msg); +} +*/ + +} \ No newline at end of file diff --git a/src/uscxml/debug/DebuggerServlet.h b/src/uscxml/debug/DebuggerServlet.h new file mode 100644 index 0000000..dc6b0ee --- /dev/null +++ b/src/uscxml/debug/DebuggerServlet.h @@ -0,0 +1,108 @@ +/** + * @file + * @author 2012-2014 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see . + * @endcond + */ + +#ifndef DEBUGGERSERVLET_H_ATUMDA3G +#define DEBUGGERSERVLET_H_ATUMDA3G + +#include "uscxml/Common.h" +#include +#include "uscxml/util/BlockingQueue.h" +#include "uscxml/server/HTTPServer.h" + +#include "uscxml/debug/Debugger.h" + +namespace uscxml { + +class USCXML_API DebuggerServlet : public Debugger, public HTTPServlet { +public: + class LogMessage : public Data { + public: +#if 0 + LogMessage(google::LogSeverity severity, const char* full_filename, + const char* base_filename, int line, + const struct ::tm* tm_time, + std::string message, std::string formatted) { + + compound["severity"] = severity; + compound["fullFilename"] = Data(full_filename, Data::VERBATIM); + compound["baseFilename"] = Data(base_filename, Data::VERBATIM); + compound["line"] = line; + compound["message"] = Data(message, Data::VERBATIM); + compound["time"] = Data(mktime((struct ::tm*)tm_time), Data::INTERPRETED); + compound["formatted"] = Data(formatted, Data::VERBATIM); + } +#endif + }; + + virtual ~DebuggerServlet() {} + + // from Debugger + virtual void addBreakpoint(const Breakpoint& breakpoint) {}; + + bool isCORS(const HTTPServer::Request& request); + void handleCORS(const HTTPServer::Request& request); + + bool httpRecvRequest(const HTTPServer::Request& request); + void setURL(const std::string& url) { + _url = url; + } + + void pushData(std::shared_ptr session, Data pushData); + void returnData(const HTTPServer::Request& request, Data replyData); + + void processDisconnect(const HTTPServer::Request& request); + void processConnect(const HTTPServer::Request& request); + void processListSessions(const HTTPServer::Request& request); + +// void processDebugPrepare(const HTTPServer::Request& request); +// void processDebugAttach(const HTTPServer::Request& request); +// void processDebugStart(const HTTPServer::Request& request); +// void processDebugStop(const HTTPServer::Request& request); + +// void processDebugEval(const HTTPServer::Request& request); +// void processDebugStart(const HTTPServer::Request& request); +// void processDebugStop(const HTTPServer::Request& request); +// void processDebugStep(const HTTPServer::Request& request); +// void processDebugResume(const HTTPServer::Request& request); +// void processDebugPause(const HTTPServer::Request& request); +// void processAddBreakPoint(const HTTPServer::Request& request); +// void processRemoveBreakPoint(const HTTPServer::Request& request); +// void processPoll(const HTTPServer::Request& request); + + // 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); +*/ +protected: + void serverPushData(std::shared_ptr); + + std::string _url; + std::map, HTTPServer::Request> _clientConns; + std::map, BlockingQueue > _sendQueues; + std::map > _sessionForId; + + std::recursive_mutex _mutex; +}; + +} + +#endif /* end of include guard: DEBUGGERSERVLET_H_ATUMDA3G */ diff --git a/src/uscxml/interpreter/BasicContentExecutor.cpp b/src/uscxml/interpreter/BasicContentExecutor.cpp index 58d3eae..0f5a67f 100644 --- a/src/uscxml/interpreter/BasicContentExecutor.cpp +++ b/src/uscxml/interpreter/BasicContentExecutor.cpp @@ -330,7 +330,7 @@ void BasicContentExecutor::process(XERCESC_NS::DOMElement* block, const X& xmlPr } try { - USCXML_MONITOR_CALLBACK1(_callbacks->getMonitor(), beforeExecutingContent, block); + USCXML_MONITOR_CALLBACK1(_callbacks->getMonitors(), beforeExecutingContent, block); if (false) { } else if (iequals(tagName, xmlPrefix.str() + "raise")) { @@ -358,12 +358,12 @@ void BasicContentExecutor::process(XERCESC_NS::DOMElement* block, const X& xmlPr Event e(exc); _callbacks->enqueueInternal(e); LOG(ERROR) << exc << std::endl; - USCXML_MONITOR_CALLBACK1(_callbacks->getMonitor(), afterExecutingContent, block); + USCXML_MONITOR_CALLBACK1(_callbacks->getMonitors(), afterExecutingContent, block); throw e; // will be catched in microstepper } - USCXML_MONITOR_CALLBACK1(_callbacks->getMonitor(), afterExecutingContent, block); + USCXML_MONITOR_CALLBACK1(_callbacks->getMonitors(), afterExecutingContent, block); } @@ -463,18 +463,18 @@ void BasicContentExecutor::invoke(XERCESC_NS::DOMElement* element) { finalize = finalizes.front(); } - USCXML_MONITOR_CALLBACK2(_callbacks->getMonitor(), beforeUninvoking, element, invokeEvent.invokeid); + USCXML_MONITOR_CALLBACK2(_callbacks->getMonitors(), beforeUninvoking, element, invokeEvent.invokeid); _callbacks->invoke(type, source, autoForward, finalize, invokeEvent); - USCXML_MONITOR_CALLBACK2(_callbacks->getMonitor(), afterUninvoking, element, invokeEvent.invokeid); + USCXML_MONITOR_CALLBACK2(_callbacks->getMonitors(), afterUninvoking, element, invokeEvent.invokeid); } void BasicContentExecutor::uninvoke(XERCESC_NS::DOMElement* invoke) { char* invokeId = (char*)invoke->getUserData(X("invokeid")); assert(invokeId != NULL); - USCXML_MONITOR_CALLBACK2(_callbacks->getMonitor(), beforeUninvoking, invoke, invokeId); + USCXML_MONITOR_CALLBACK2(_callbacks->getMonitors(), beforeUninvoking, invoke, invokeId); _callbacks->uninvoke(invokeId); - USCXML_MONITOR_CALLBACK2(_callbacks->getMonitor(), afterUninvoking, invoke, invokeId); + USCXML_MONITOR_CALLBACK2(_callbacks->getMonitors(), afterUninvoking, invoke, invokeId); invoke->setUserData(X("invokeid"), NULL, NULL); free(invokeId); diff --git a/src/uscxml/interpreter/BasicEventQueue.cpp b/src/uscxml/interpreter/BasicEventQueue.cpp index 7505f46..3cf4daf 100644 --- a/src/uscxml/interpreter/BasicEventQueue.cpp +++ b/src/uscxml/interpreter/BasicEventQueue.cpp @@ -37,24 +37,10 @@ Event BasicEventQueue::dequeue(size_t blockMs) { if (blockMs > 0) { // block for given milliseconds or until queue is filled - std::chrono::time_point updated, now; - std::chrono::milliseconds remain; + auto endTime = std::chrono::system_clock::now() + std::chrono::milliseconds(blockMs); - if (blockMs > (std::chrono::system_clock::duration::max)().count()) { - blockMs = (std::chrono::system_clock::duration::max)().count(); - } - - updated = now = std::chrono::system_clock::now(); - remain = std::chrono::milliseconds(blockMs); - - while (remain.count() > 0 && _queue.empty()) { - _cond.wait_for(_mutex, remain); - - now = std::chrono::system_clock::now(); - - auto elapsed = now - updated; - remain -= std::chrono::duration_cast(elapsed); - updated = now; + while (_queue.empty()) { + _cond.wait_until(_mutex, endTime); } } diff --git a/src/uscxml/interpreter/ContentExecutorImpl.h b/src/uscxml/interpreter/ContentExecutorImpl.h index e1a7e8c..0e12aff 100644 --- a/src/uscxml/interpreter/ContentExecutorImpl.h +++ b/src/uscxml/interpreter/ContentExecutorImpl.h @@ -65,7 +65,7 @@ public: virtual const Event& getCurrentEvent() = 0; /** Monitoring */ - virtual InterpreterMonitor* getMonitor() = 0; + virtual std::set getMonitors() = 0; }; diff --git a/src/uscxml/interpreter/FastMicroStep.cpp b/src/uscxml/interpreter/FastMicroStep.cpp index ea43c5d..826df93 100644 --- a/src/uscxml/interpreter/FastMicroStep.cpp +++ b/src/uscxml/interpreter/FastMicroStep.cpp @@ -89,68 +89,66 @@ FastMicroStep::~FastMicroStep() { } } -void FastMicroStep::resortStates(DOMNode* node, const X& xmlPrefix) { - if (node->getNodeType() != DOMNode::ELEMENT_NODE) - return; +void FastMicroStep::resortStates(DOMElement* element, const X& xmlPrefix) { - /** + /** initials deep histories shallow histories everything else */ - DOMElement* element = dynamic_cast(node); + DOMElement* child = element->getFirstElementChild(); + while(child) { + resortStates(child, xmlPrefix); + child = child->getNextElementSibling(); + } // shallow history states to top - DOMNode* child = element->getFirstChild(); + child = element->getFirstElementChild(); while(child) { - resortStates(child, xmlPrefix); - if (child->getNodeType() == DOMNode::ELEMENT_NODE && - TAGNAME_CAST(child) == xmlPrefix.str() + "history" && - (!HAS_ATTR(element, "type") || iequals(ATTR(element, "type"), "shallow"))) { + if (TAGNAME_CAST(child) == xmlPrefix.str() + "history" && + (!HAS_ATTR(element, "type") || iequals(ATTR(element, "type"), "shallow"))) { - DOMNode* tmp = child->getNextSibling(); + DOMElement* tmp = child->getNextElementSibling(); if (child != element->getFirstChild()) { element->insertBefore(child, element->getFirstChild()); } child = tmp; } else { - child = child->getNextSibling(); + child = child->getNextElementSibling(); } } // deep history states to top - child = element->getFirstChild(); + child = element->getFirstElementChild(); while(child) { - resortStates(child, xmlPrefix); if (child->getNodeType() == DOMNode::ELEMENT_NODE && TAGNAME_CAST(child) == xmlPrefix.str() + "history" && HAS_ATTR(element, "type") && iequals(ATTR(element, "type"), "deep")) { - DOMNode* tmp = child->getNextSibling(); + DOMElement* tmp = child->getNextElementSibling(); if (child != element->getFirstChild()) { element->insertBefore(child, element->getFirstChild()); } child = tmp; } else { - child = child->getNextSibling(); + child = child->getNextElementSibling(); } } // initial states on top of histories even - child = element->getFirstChild(); + child = element->getFirstElementChild(); while(child) { - resortStates(child, xmlPrefix); if (child->getNodeType() == DOMNode::ELEMENT_NODE && LOCALNAME_CAST(child) == "initial") { - DOMNode* tmp = child->getNextSibling(); + DOMElement* tmp = child->getNextElementSibling(); if (child != element->getFirstChild()) { element->insertBefore(child, element->getFirstChild()); } child = tmp; } else { - child = child->getNextSibling(); + child = child->getNextElementSibling(); } } } @@ -193,10 +191,9 @@ void FastMicroStep::init(XERCESC_NS::DOMElement* scxml) { } resortStates(_scxml, _xmlPrefix); - - // TODO: https://github.com/tklab-tud/uscxml/blob/master/src/uscxml/transform/ChartToC.cpp#L244 - - +// assert(false); +// throw NULL; + /** -- All things states -- */ std::list tmp; @@ -467,7 +464,7 @@ InterpreterState FastMicroStep::step(size_t blockMs) { return USCXML_FINISHED; if (_flags & USCXML_CTX_TOP_LEVEL_FINAL) { - USCXML_MONITOR_CALLBACK(_callbacks->getMonitor(), beforeCompletion); + USCXML_MONITOR_CALLBACK(_callbacks->getMonitors(), beforeCompletion); /* exit all remaining states */ i = USCXML_NUMBER_STATES; @@ -498,7 +495,7 @@ InterpreterState FastMicroStep::step(size_t blockMs) { _flags |= USCXML_CTX_FINISHED; - USCXML_MONITOR_CALLBACK(_callbacks->getMonitor(), afterCompletion); + USCXML_MONITOR_CALLBACK(_callbacks->getMonitors(), afterCompletion); return USCXML_FINISHED; } @@ -508,7 +505,7 @@ InterpreterState FastMicroStep::step(size_t blockMs) { targetSet |= USCXML_GET_STATE(0).completion; _flags |= USCXML_CTX_SPONTANEOUS | USCXML_CTX_INITIALIZED; - USCXML_MONITOR_CALLBACK(_callbacks->getMonitor(), beforeMicroStep); + USCXML_MONITOR_CALLBACK(_callbacks->getMonitors(), beforeMicroStep); goto ESTABLISH_ENTRYSET; } @@ -520,7 +517,7 @@ InterpreterState FastMicroStep::step(size_t blockMs) { if ((_event = _callbacks->dequeueInternal())) { - USCXML_MONITOR_CALLBACK1(_callbacks->getMonitor(), beforeProcessingEvent, _event); + USCXML_MONITOR_CALLBACK1(_callbacks->getMonitors(), beforeProcessingEvent, _event); goto SELECT_TRANSITIONS; } @@ -551,13 +548,13 @@ InterpreterState FastMicroStep::step(size_t blockMs) { // we dequeued all internal events and ought to signal stable configuration if (!(_flags & USCXML_CTX_STABLE)) { - USCXML_MONITOR_CALLBACK(_callbacks->getMonitor(), onStableConfiguration); + USCXML_MONITOR_CALLBACK(_callbacks->getMonitors(), onStableConfiguration); _microstepConfigurations.clear(); _flags |= USCXML_CTX_STABLE; } if ((_event = _callbacks->dequeueExternal(blockMs))) { - USCXML_MONITOR_CALLBACK1(_callbacks->getMonitor(), beforeProcessingEvent, _event); + USCXML_MONITOR_CALLBACK1(_callbacks->getMonitors(), beforeProcessingEvent, _event); goto SELECT_TRANSITIONS; } @@ -629,7 +626,7 @@ SELECT_TRANSITIONS: return USCXML_MACROSTEPPED; } - USCXML_MONITOR_CALLBACK(_callbacks->getMonitor(), beforeMicroStep); + USCXML_MONITOR_CALLBACK(_callbacks->getMonitors(), beforeMicroStep); #ifdef USCXML_VERBOSE std::cerr << "Targets: "; @@ -793,7 +790,7 @@ ESTABLISH_ENTRYSET: while(i-- > 0) { if (BIT_HAS(i, exitSet) && BIT_HAS(i, _configuration)) { - USCXML_MONITOR_CALLBACK1(_callbacks->getMonitor(), beforeExitingState, USCXML_GET_STATE(i).element); + USCXML_MONITOR_CALLBACK1(_callbacks->getMonitors(), beforeExitingState, USCXML_GET_STATE(i).element); /* call all on exit handlers */ for (auto exitIter = USCXML_GET_STATE(i).onExit.begin(); exitIter != USCXML_GET_STATE(i).onExit.end(); exitIter++) { @@ -805,7 +802,7 @@ ESTABLISH_ENTRYSET: } BIT_CLEAR(i, _configuration); - USCXML_MONITOR_CALLBACK1(_callbacks->getMonitor(), afterExitingState, USCXML_GET_STATE(i).element); + USCXML_MONITOR_CALLBACK1(_callbacks->getMonitors(), afterExitingState, USCXML_GET_STATE(i).element); } } @@ -814,7 +811,7 @@ ESTABLISH_ENTRYSET: i = transSet.find_first(); while(i != boost::dynamic_bitset<>::npos) { if ((USCXML_GET_TRANS(i).type & (USCXML_TRANS_HISTORY | USCXML_TRANS_INITIAL)) == 0) { - USCXML_MONITOR_CALLBACK1(_callbacks->getMonitor(), beforeTakingTransition, USCXML_GET_TRANS(i).element); + USCXML_MONITOR_CALLBACK1(_callbacks->getMonitors(), beforeTakingTransition, USCXML_GET_TRANS(i).element); if (USCXML_GET_TRANS(i).onTrans != NULL) { @@ -826,7 +823,7 @@ ESTABLISH_ENTRYSET: } } - USCXML_MONITOR_CALLBACK1(_callbacks->getMonitor(), afterTakingTransition, USCXML_GET_TRANS(i).element); + USCXML_MONITOR_CALLBACK1(_callbacks->getMonitors(), afterTakingTransition, USCXML_GET_TRANS(i).element); } i = transSet.find_next(i); @@ -856,7 +853,7 @@ ESTABLISH_ENTRYSET: continue; } - USCXML_MONITOR_CALLBACK1(_callbacks->getMonitor(), beforeEnteringState, USCXML_GET_STATE(i).element); + USCXML_MONITOR_CALLBACK1(_callbacks->getMonitors(), beforeEnteringState, USCXML_GET_STATE(i).element); BIT_SET_AT(i, _configuration); @@ -877,7 +874,7 @@ ESTABLISH_ENTRYSET: } } - USCXML_MONITOR_CALLBACK1(_callbacks->getMonitor(), afterEnteringState, USCXML_GET_STATE(i).element); + USCXML_MONITOR_CALLBACK1(_callbacks->getMonitors(), afterEnteringState, USCXML_GET_STATE(i).element); /* take history and initial transitions */ for (j = 0; j < USCXML_NUMBER_TRANS; j++) { @@ -885,7 +882,7 @@ ESTABLISH_ENTRYSET: (USCXML_GET_TRANS(j).type & (USCXML_TRANS_HISTORY | USCXML_TRANS_INITIAL)) && USCXML_GET_STATE(USCXML_GET_TRANS(j).source).parent == i) { - USCXML_MONITOR_CALLBACK1(_callbacks->getMonitor(), beforeTakingTransition, USCXML_GET_TRANS(j).element); + USCXML_MONITOR_CALLBACK1(_callbacks->getMonitors(), beforeTakingTransition, USCXML_GET_TRANS(j).element); /* call executable content in transition */ if (USCXML_GET_TRANS(j).onTrans != NULL) { @@ -896,7 +893,7 @@ ESTABLISH_ENTRYSET: } } - USCXML_MONITOR_CALLBACK1(_callbacks->getMonitor(), afterTakingTransition, USCXML_GET_TRANS(j).element); + USCXML_MONITOR_CALLBACK1(_callbacks->getMonitors(), afterTakingTransition, USCXML_GET_TRANS(j).element); } } @@ -941,15 +938,17 @@ ESTABLISH_ENTRYSET: } } } - USCXML_MONITOR_CALLBACK(_callbacks->getMonitor(), afterMicroStep); + USCXML_MONITOR_CALLBACK(_callbacks->getMonitors(), afterMicroStep); // are we running in circles? if (_microstepConfigurations.find(_configuration) != _microstepConfigurations.end()) { - USCXML_MONITOR_CALLBACK1(_callbacks->getMonitor(), + InterpreterIssue issue("Reentering same configuration during microstep - possible endless loop", + NULL, + InterpreterIssue::USCXML_ISSUE_WARNING); + + USCXML_MONITOR_CALLBACK1(_callbacks->getMonitors(), reportIssue, - InterpreterIssue("Reentering same configuration during microstep - possible endless loop", - NULL, - InterpreterIssue::USCXML_ISSUE_WARNING)); + issue); } _microstepConfigurations.insert(_configuration); diff --git a/src/uscxml/interpreter/FastMicroStep.h b/src/uscxml/interpreter/FastMicroStep.h index a3cdf9c..dd58480 100644 --- a/src/uscxml/interpreter/FastMicroStep.h +++ b/src/uscxml/interpreter/FastMicroStep.h @@ -125,7 +125,7 @@ protected: private: std::list getHistoryCompletion(const XERCESC_NS::DOMElement* state); - void resortStates(XERCESC_NS::DOMNode* node, const X& xmlPrefix); + void resortStates(XERCESC_NS::DOMElement* node, const X& xmlPrefix); bool conflictsCached(const XERCESC_NS::DOMElement* t1, const XERCESC_NS::DOMElement* t2, const XERCESC_NS::DOMElement* root); ///< overrides implementation Predicates::conflicts for speed diff --git a/src/uscxml/interpreter/InterpreterImpl.cpp b/src/uscxml/interpreter/InterpreterImpl.cpp index 0547f12..5cbae3c 100644 --- a/src/uscxml/interpreter/InterpreterImpl.cpp +++ b/src/uscxml/interpreter/InterpreterImpl.cpp @@ -69,7 +69,7 @@ void InterpreterImpl::addInstance(std::shared_ptr interpreterIm _instances[interpreterImpl->getSessionId()] = interpreterImpl; } -InterpreterImpl::InterpreterImpl() : _isInitialized(false), _document(NULL), _scxml(NULL), _state(USCXML_INSTANTIATED), _monitor(NULL) { +InterpreterImpl::InterpreterImpl() : _isInitialized(false), _document(NULL), _scxml(NULL), _state(USCXML_INSTANTIATED) { try { ::xercesc_3_1::XMLPlatformUtils::Initialize(); } catch (const XERCESC_NS::XMLException& toCatch) { diff --git a/src/uscxml/interpreter/InterpreterImpl.h b/src/uscxml/interpreter/InterpreterImpl.h index be33306..6d047ec 100644 --- a/src/uscxml/interpreter/InterpreterImpl.h +++ b/src/uscxml/interpreter/InterpreterImpl.h @@ -97,10 +97,14 @@ public: return _microStepper.getConfiguration(); } - void setMonitor(InterpreterMonitor* monitor) { - _monitor = monitor; + void addMonitor(InterpreterMonitor* monitor) { + _monitors.insert(monitor); } + void removeMonitor(InterpreterMonitor* monitor) { + _monitors.erase(monitor); + } + /** MicrostepCallbacks */ @@ -132,8 +136,8 @@ public: _execContent.uninvoke(invoke); } - virtual InterpreterMonitor* getMonitor() { - return _monitor; + virtual std::set getMonitors() { + return _monitors; } /** @@ -255,7 +259,9 @@ protected: friend class InterpreterIssue; friend class TransformerImpl; friend class USCXMLInvoker; - friend class SCXMLIOProcessor; + friend class SCXMLIOProcessor; + friend class DebugSession; + friend class Debugger; X _xmlPrefix; X _xmlNS; @@ -280,7 +286,7 @@ protected: std::map _ioProcs; std::map _invokers; std::set _autoForwarders; - InterpreterMonitor* _monitor; + std::set _monitors; private: void setupDOM(); diff --git a/src/uscxml/interpreter/InterpreterMonitor.h b/src/uscxml/interpreter/InterpreterMonitor.h index 8dac445..ff2e7cb 100644 --- a/src/uscxml/interpreter/InterpreterMonitor.h +++ b/src/uscxml/interpreter/InterpreterMonitor.h @@ -33,14 +33,14 @@ catch (std::bad_weak_ptr e) { LOG(ERROR) << "Unclean shutdown " << std::endl; } catch (...) { LOG(ERROR) << "An exception occurred when calling " #callback " on monitors"; } \ if (_state == USCXML_DESTROYED) { throw std::bad_weak_ptr(); } -#define USCXML_MONITOR_CALLBACK(callback, function) \ -if (callback) { callback->function(); } +#define USCXML_MONITOR_CALLBACK(callbacks, function) \ +for (auto callback : callbacks) { callback->function(NULL); } -#define USCXML_MONITOR_CALLBACK1(callback, function, arg1) \ -if (callback) { callback->function(arg1); } +#define USCXML_MONITOR_CALLBACK1(callbacks, function, arg1) \ +for (auto callback : callbacks) { callback->function(NULL, arg1); } -#define USCXML_MONITOR_CALLBACK2(callback, function, arg1, arg2) \ -if (callback) { callback->function(arg1, arg2); } +#define USCXML_MONITOR_CALLBACK2(callbacks, function, arg1, arg2) \ +for (auto callback : callbacks) { callback->function(NULL, arg1, arg2); } // forward declare namespace XERCESC_NS { @@ -54,34 +54,34 @@ public: InterpreterMonitor() : _copyToInvokers(false) {} virtual ~InterpreterMonitor() {} - virtual void beforeProcessingEvent(const Event& event) {} - virtual void beforeMicroStep() {} + virtual void beforeProcessingEvent(InterpreterImpl* impl, const Event& event) {} + virtual void beforeMicroStep(InterpreterImpl* impl) {} - virtual void beforeExitingState(const XERCESC_NS::DOMElement* state) {} - virtual void afterExitingState(const XERCESC_NS::DOMElement* state) {} + virtual void beforeExitingState(InterpreterImpl* impl, const XERCESC_NS::DOMElement* state) {} + virtual void afterExitingState(InterpreterImpl* impl, const XERCESC_NS::DOMElement* state) {} - virtual void beforeExecutingContent(const XERCESC_NS::DOMElement* execContent) {} - virtual void afterExecutingContent(const XERCESC_NS::DOMElement* execContent) {} + virtual void beforeExecutingContent(InterpreterImpl* impl, const XERCESC_NS::DOMElement* execContent) {} + virtual void afterExecutingContent(InterpreterImpl* impl, const XERCESC_NS::DOMElement* execContent) {} - virtual void beforeUninvoking(const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) {} - virtual void afterUninvoking(const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) {} + virtual void beforeUninvoking(InterpreterImpl* impl, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) {} + virtual void afterUninvoking(InterpreterImpl* impl, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) {} - virtual void beforeTakingTransition(const XERCESC_NS::DOMElement* transition) {} - virtual void afterTakingTransition(const XERCESC_NS::DOMElement* transition) {} + virtual void beforeTakingTransition(InterpreterImpl* impl, const XERCESC_NS::DOMElement* transition) {} + virtual void afterTakingTransition(InterpreterImpl* impl, const XERCESC_NS::DOMElement* transition) {} - virtual void beforeEnteringState(const XERCESC_NS::DOMElement* state) {} - virtual void afterEnteringState(const XERCESC_NS::DOMElement* state) {} + virtual void beforeEnteringState(InterpreterImpl* impl, const XERCESC_NS::DOMElement* state) {} + virtual void afterEnteringState(InterpreterImpl* impl, const XERCESC_NS::DOMElement* state) {} - virtual void beforeInvoking(const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) {} - virtual void afterInvoking(const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) {} + virtual void beforeInvoking(InterpreterImpl* impl, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) {} + virtual void afterInvoking(InterpreterImpl* impl, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) {} - virtual void afterMicroStep() {} - virtual void onStableConfiguration() {} + virtual void afterMicroStep(InterpreterImpl* impl) {} + virtual void onStableConfiguration(InterpreterImpl* impl) {} - virtual void beforeCompletion() {} - virtual void afterCompletion() {} + virtual void beforeCompletion(InterpreterImpl* impl) {} + virtual void afterCompletion(InterpreterImpl* impl) {} - virtual void reportIssue(const InterpreterIssue& issue) {} + virtual void reportIssue(InterpreterImpl* impl, const InterpreterIssue& issue) {} void copyToInvokers(bool copy) { _copyToInvokers = copy; @@ -101,13 +101,13 @@ public: StateTransitionMonitor() {} virtual ~StateTransitionMonitor() {} - virtual void beforeTakingTransition(const XERCESC_NS::DOMElement* transition); - virtual void beforeExecutingContent(const XERCESC_NS::DOMElement* element); - virtual void onStableConfiguration(); - virtual void beforeProcessingEvent(const uscxml::Event& event); - virtual void beforeExitingState(const XERCESC_NS::DOMElement* state); - virtual void beforeEnteringState(const XERCESC_NS::DOMElement* state); - virtual void beforeMicroStep(); + virtual void beforeTakingTransition(InterpreterImpl* impl, const XERCESC_NS::DOMElement* transition); + virtual void beforeExecutingContent(InterpreterImpl* impl, const XERCESC_NS::DOMElement* element); + virtual void onStableConfiguration(InterpreterImpl* impl); + virtual void beforeProcessingEvent(InterpreterImpl* impl, const uscxml::Event& event); + virtual void beforeExitingState(InterpreterImpl* impl, const XERCESC_NS::DOMElement* state); + virtual void beforeEnteringState(InterpreterImpl* impl, const XERCESC_NS::DOMElement* state); + virtual void beforeMicroStep(InterpreterImpl* impl); protected: static std::recursive_mutex _mutex; diff --git a/src/uscxml/interpreter/MicroStepImpl.h b/src/uscxml/interpreter/MicroStepImpl.h index be9d974..cb4aec6 100644 --- a/src/uscxml/interpreter/MicroStepImpl.h +++ b/src/uscxml/interpreter/MicroStepImpl.h @@ -23,6 +23,7 @@ #include "uscxml/config.h" #include +#include #include #include @@ -33,6 +34,8 @@ namespace uscxml { +class InterpreterMonitor; + /** * @ingroup microstep * @ingroup callback @@ -57,7 +60,7 @@ public: virtual void uninvoke(XERCESC_NS::DOMElement* invoke) = 0; /** Monitoring */ - virtual InterpreterMonitor* getMonitor() = 0; + virtual std::set getMonitors() = 0; }; /** diff --git a/src/uscxml/util/BlockingQueue.h b/src/uscxml/util/BlockingQueue.h new file mode 100644 index 0000000..2f0ab2e --- /dev/null +++ b/src/uscxml/util/BlockingQueue.h @@ -0,0 +1,77 @@ +/** + * @file + * @author 2012-2013 Stefan Radomski (stefan.radomski@cs.tu-darmstadt.de) + * @copyright Simplified BSD + * + * @cond + * This program is free software: you can redistribute it and/or modify + * it under the terms of the FreeBSD license as published by the FreeBSD + * project. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the FreeBSD license along with this + * program. If not, see . + * @endcond + */ + +#ifndef BLOCKINGQUEUE_H_4LEVMY0N +#define BLOCKINGQUEUE_H_4LEVMY0N + +#include "uscxml/Common.h" +#include +#include + +namespace uscxml { + +template +class BlockingQueue { +public: + BlockingQueue() {} + virtual ~BlockingQueue() { + } + + virtual void push(const T& elem) { + std::lock_guard lock(_mutex); + _queue.push_back(elem); + _cond.notify_all(); + } + + virtual void push_front(const T& elem) { + std::lock_guard lock(_mutex); + _queue.push_front(elem); + _cond.notify_all(); + } + + virtual T pop() { + std::lock_guard lock(_mutex); +// std::cout << "Popping from " << this << std::endl; + while (_queue.empty()) { + _cond.wait(_mutex); + } + T ret = _queue.front(); + _queue.pop_front(); + return ret; + } + + virtual void clear() { + std::lock_guard lock(_mutex); + _queue.clear(); + } + + virtual bool isEmpty() { + std::lock_guard lock(_mutex); + return _queue.empty(); + } + +protected: + std::mutex _mutex; + std::condition_variable_any _cond; + std::list _queue; +}; + +} + +#endif /* end of include guard: BLOCKINGQUEUE_H_4LEVMY0N */ diff --git a/src/uscxml/util/DOM.h b/src/uscxml/util/DOM.h index 0b35f40..86fa813 100644 --- a/src/uscxml/util/DOM.h +++ b/src/uscxml/util/DOM.h @@ -127,17 +127,20 @@ class USCXML_API X { public : X(X const &other) { + _localForm = other._localForm; _unicodeForm = XERCESC_NS::XMLString::replicate(other._unicodeForm); _deallocOther = true; } void operator=(X const &other) { // did we maybe leak before? + _localForm = other._localForm; _unicodeForm = XERCESC_NS::XMLString::replicate(other._unicodeForm); _deallocOther = true; } X(const XMLCh* const toTranscode) { + if (toTranscode != NULL) { // Call the private transcoding method char* tmp = XERCESC_NS::XMLString::transcode(toTranscode); @@ -149,6 +152,7 @@ public : } X(const std::string& fromTranscode) { + // Call the private transcoding method _localForm = fromTranscode; _unicodeForm = XERCESC_NS::XMLString::transcode(fromTranscode.c_str()); @@ -156,6 +160,7 @@ public : } X(const char* const fromTranscode) { + // Call the private transcoding method _localForm = fromTranscode; _unicodeForm = XERCESC_NS::XMLString::transcode(fromTranscode); @@ -163,22 +168,25 @@ public : } X(char* fromTranscode) { - // Call the private transcoding method + + // Call the private transcoding method _localForm = fromTranscode; _unicodeForm = XERCESC_NS::XMLString::transcode(fromTranscode); _deallocOther = true; } X() { + _unicodeForm = NULL; _deallocOther = false; } ~X() { + if (_deallocOther) XERCESC_NS::XMLString::release(&_unicodeForm); } - + const std::string& str() const { return _localForm; } @@ -195,7 +203,7 @@ public : operator std::string () { return _localForm; } - + protected: friend USCXML_API std::ostream& operator<< (std::ostream& os, const X& data); @@ -277,6 +285,32 @@ private: }; #endif + +#if 0 +static const X kElementScxmlName = X("scxml"); +static const X kElementStateName = X("state"); +static const X kElementParallelName = X("parallel"); +static const X kElementTransitionName = X("transition"); +static const X kElementInitialName = X("initial"); +static const X kElementFinalName = X("final"); +static const X kElementOnEntryName = X("onentry"); +static const X kElementOnExitName = X("onexit"); +static const X kElementHistoryName = X("history"); + +static const X kElementRaiseName = X("raise"); +static const X kElementIfName = X("if"); +static const X kElementElseIfName = X("elseif"); +static const X kElementElseName = X("else"); +static const X kElementForEachName = X("foreach"); +static const X kElementLogName = X("log"); + +static const X kElementDataModelName = X("datamodel"); +static const X kElementDataName = X("data"); +static const X kElementAssignName = X("assign"); +static const X kElementContentName = X("content"); +static const X kElementParamName = X("param"); +static const X kElementScriptName = X("script"); +#endif USCXML_API std::ostream& operator<< (std::ostream& os, const X& xmlString); USCXML_API std::ostream& operator<< (std::ostream& os, const XERCESC_NS::DOMNode& node); diff --git a/test/issues/test-issue87.scxml.todo b/test/issues/test-issue87.scxml.todo new file mode 100644 index 0000000..55f0b60 --- /dev/null +++ b/test/issues/test-issue87.scxml.todo @@ -0,0 +1,189 @@ + + + { + test = 'a', + b = 25, + t_test = { + xxx = '23', + zzz = 45 + } +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { + dDiff = 0, + dEnter = os.clock(), + dInterval = 2000 +} + + { + dDiff = 0, + dEnter = os.clock(), + dInterval = 2000 +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { + dDiff = 0, + dEnter = os.clock(), + dInterval = 2000 +} + + { + dDiff = 0, + dEnter = os.clock(), + dInterval = 2000 +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/src/test-lifecycle.cpp b/test/src/test-lifecycle.cpp index 14ebd94..81fc363 100644 --- a/test/src/test-lifecycle.cpp +++ b/test/src/test-lifecycle.cpp @@ -48,67 +48,67 @@ std::list callBackSeq; class SequenceCheckingMonitor : public InterpreterMonitor { - virtual void beforeProcessingEvent(const Event& event) { + virtual void beforeProcessingEvent(InterpreterImpl* impl, const Event& event) { CHECK_CALLBACK_TYPE(USCXML_BEFOREPROCESSINGEVENT); } - virtual void beforeMicroStep() { + virtual void beforeMicroStep(InterpreterImpl* impl) { CHECK_CALLBACK_TYPE(USCXML_BEFOREMICROSTEP); } - virtual void beforeExitingState(const XERCESC_NS::DOMElement* state) { + virtual void beforeExitingState(InterpreterImpl* impl, const XERCESC_NS::DOMElement* state) { CHECK_CALLBACK_TYPE(USCXML_BEFOREEXITINGSTATE); } - virtual void afterExitingState(const XERCESC_NS::DOMElement* state) { + virtual void afterExitingState(InterpreterImpl* impl, const XERCESC_NS::DOMElement* state) { CHECK_CALLBACK_TYPE(USCXML_AFTEREXITINGSTATE); } - virtual void beforeExecutingContent(const XERCESC_NS::DOMElement* element) { + virtual void beforeExecutingContent(InterpreterImpl* impl, const XERCESC_NS::DOMElement* element) { CHECK_CALLBACK_TYPE(USCXML_BEFOREEXECUTINGCONTENT); } - virtual void afterExecutingContent(const XERCESC_NS::DOMElement* element) { + virtual void afterExecutingContent(InterpreterImpl* impl, const XERCESC_NS::DOMElement* element) { CHECK_CALLBACK_TYPE(USCXML_AFTEREXECUTINGCONTENT); } - virtual void beforeUninvoking(const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) { + virtual void beforeUninvoking(InterpreterImpl* impl, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) { CHECK_CALLBACK_TYPE(USCXML_BEFOREUNINVOKING); } - virtual void afterUninvoking(const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) { + virtual void afterUninvoking(InterpreterImpl* impl, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) { CHECK_CALLBACK_TYPE(USCXML_AFTERUNINVOKING); } - virtual void beforeTakingTransition(const XERCESC_NS::DOMElement* transition) { + virtual void beforeTakingTransition(InterpreterImpl* impl, const XERCESC_NS::DOMElement* transition) { CHECK_CALLBACK_TYPE(USCXML_BEFORETAKINGTRANSITION); } - virtual void afterTakingTransition(const XERCESC_NS::DOMElement* transition) { + virtual void afterTakingTransition(InterpreterImpl* impl, const XERCESC_NS::DOMElement* transition) { CHECK_CALLBACK_TYPE(USCXML_AFTERTAKINGTRANSITION); } - virtual void beforeEnteringState(const XERCESC_NS::DOMElement* state) { + virtual void beforeEnteringState(InterpreterImpl* impl, const XERCESC_NS::DOMElement* state) { CHECK_CALLBACK_TYPE(USCXML_BEFOREENTERINGSTATE); } - virtual void afterEnteringState(const XERCESC_NS::DOMElement* state) { + virtual void afterEnteringState(InterpreterImpl* impl, const XERCESC_NS::DOMElement* state) { CHECK_CALLBACK_TYPE(USCXML_AFTERENTERINGSTATE); } - virtual void beforeInvoking(const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) { + virtual void beforeInvoking(InterpreterImpl* impl, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) { CHECK_CALLBACK_TYPE(USCXML_BEFOREINVOKING); } - virtual void afterInvoking(const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) { + virtual void afterInvoking(InterpreterImpl* impl, const XERCESC_NS::DOMElement* invokeElem, const std::string& invokeid) { CHECK_CALLBACK_TYPE(USCXML_AFTERINVOKING); } - virtual void afterMicroStep() { + virtual void afterMicroStep(InterpreterImpl* impl) { CHECK_CALLBACK_TYPE(USCXML_AFTERMICROSTEP); } - virtual void onStableConfiguration() { + virtual void onStableConfiguration(InterpreterImpl* impl) { CHECK_CALLBACK_TYPE(USCXML_ONSTABLECONFIGURATION); } - virtual void beforeCompletion() { + virtual void beforeCompletion(InterpreterImpl* impl) { CHECK_CALLBACK_TYPE(USCXML_BEFORECOMPLETION); } - virtual void afterCompletion() { + virtual void afterCompletion(InterpreterImpl* impl) { CHECK_CALLBACK_TYPE(USCXML_AFTERCOMPLETION); } @@ -139,7 +139,7 @@ int main(int argc, char** argv) { try { const char* xml = ""; Interpreter interpreter = Interpreter::fromXML(xml, ""); - interpreter.setMonitor(&mon); + interpreter.addMonitor(&mon); assert(interpreter.getState() == USCXML_INSTANTIATED); interpreter.step(); assert(false); @@ -159,7 +159,7 @@ int main(int argc, char** argv) { " " ""; Interpreter interpreter = Interpreter::fromXML(xml, ""); - interpreter.setMonitor(&mon); + interpreter.addMonitor(&mon); assert(interpreter.getState() == USCXML_INSTANTIATED); interpreter.step(); assert(false); @@ -182,7 +182,7 @@ int main(int argc, char** argv) { ""; Interpreter interpreter = Interpreter::fromXML(xml, ""); - interpreter.setMonitor(&mon); + interpreter.addMonitor(&mon); callBackSeq.push_back(USCXML_BEFOREMICROSTEP); callBackSeq.push_back(USCXML_BEFOREENTERINGSTATE); // scxml @@ -232,7 +232,7 @@ int main(int argc, char** argv) { ""; Interpreter interpreter = Interpreter::fromXML(xml, ""); - interpreter.setMonitor(&mon); + interpreter.addMonitor(&mon); callBackSeq.push_back(USCXML_BEFOREMICROSTEP); callBackSeq.push_back(USCXML_BEFOREENTERINGSTATE); // scxml @@ -304,7 +304,7 @@ int main(int argc, char** argv) { ""; Interpreter interpreter = Interpreter::fromXML(xml, ""); - interpreter.setMonitor(&mon); + interpreter.addMonitor(&mon); callBackSeq.push_back(USCXML_BEFOREMICROSTEP); callBackSeq.push_back(USCXML_BEFOREENTERINGSTATE); // scxml callBackSeq.push_back(USCXML_AFTERENTERINGSTATE); diff --git a/test/src/test-state-pass.cpp b/test/src/test-state-pass.cpp index ac52a0b..afda506 100644 --- a/test/src/test-state-pass.cpp +++ b/test/src/test-state-pass.cpp @@ -73,11 +73,11 @@ int main(int argc, char** argv) { // interpreter.setActionLanguage(al); StateTransitionMonitor mon; - interpreter.setMonitor(&mon); + interpreter.addMonitor(&mon); InterpreterState state = InterpreterState::USCXML_UNDEF; while(state != USCXML_FINISHED) { - state = interpreter.step(true); + state = interpreter.step(); } assert(interpreter.isInState("pass")); } catch (Event e) { diff --git a/test/src/test-stress.cpp b/test/src/test-stress.cpp index 8bd2e4a..dc54f4e 100644 --- a/test/src/test-stress.cpp +++ b/test/src/test-stress.cpp @@ -16,7 +16,7 @@ int startedAt; int lastTransitionAt; class StatusMonitor : public uscxml::InterpreterMonitor { - void beforeTakingTransition(const XERCESC_NS::DOMElement* transition) { + void beforeTakingTransition(uscxml::InterpreterImpl* impl, const XERCESC_NS::DOMElement* transition) { lastTransitionAt = time(NULL); } @@ -81,7 +81,7 @@ int main(int argc, char** argv) { LOG(INFO) << "Processing " << interpreter.getImpl()->getBaseURL(); if (interpreter) { - interpreter.setMonitor(&vm); + interpreter.addMonitor(&vm); InterpreterState state = InterpreterState::USCXML_UNDEF; int now = time(NULL); diff --git a/test/src/test-url.cpp b/test/src/test-url.cpp index aa559f3..0e1d115 100644 --- a/test/src/test-url.cpp +++ b/test/src/test-url.cpp @@ -137,7 +137,7 @@ int main(int argc, char** argv) { // assert(foo.isAbsolute()); - HTTPServer::getInstance(8099, 8100); + HTTPServer::getInstance(8199, 8200); std::string exeName = argv[0]; exeName = exeName.substr(exeName.find_last_of("\\/") + 1); diff --git a/test/src/test-validating.cpp b/test/src/test-validating.cpp index 840a105..c3bc693 100644 --- a/test/src/test-validating.cpp +++ b/test/src/test-validating.cpp @@ -31,7 +31,7 @@ public: IssueMonitor() { runtimeIssues = 0; } - void reportIssue(const InterpreterIssue& issue) { + void reportIssue(InterpreterImpl* impl, const InterpreterIssue& issue) { runtimeIssues++; } }; @@ -60,7 +60,7 @@ int main(int argc, char** argv) { IssueMonitor monitor; Interpreter interpreter = Interpreter::fromXML(xml, ""); - interpreter.setMonitor(&monitor); + interpreter.addMonitor(&monitor); while(interpreter.step() > 0) {} -- cgit v0.12