diff --git a/.github/workflows/code_coverage.yml b/.github/workflows/code_coverage.yml index 63f27163..27f90b37 100644 --- a/.github/workflows/code_coverage.yml +++ b/.github/workflows/code_coverage.yml @@ -9,6 +9,7 @@ on: - '.github/workflows/**' - '!.github/workflows/code_coverage.yml' pull_request: + workflow_dispatch: jobs: code-coverage: diff --git a/docs/developer_docs/Developer-Docs-Home.md b/docs/developer_docs/Developer-Docs-Home.md index e67c9c3c..b78710f9 100644 --- a/docs/developer_docs/Developer-Docs-Home.md +++ b/docs/developer_docs/Developer-Docs-Home.md @@ -8,3 +8,4 @@ Link documentation for Trick internals, processes, and plans here. - [Testing](Testing) - [How to make a new Trick release on GitHub](How-To-Make-A-Release) +- [Tooling and Sanitizers](Tooling-and-Sanitizers) diff --git a/docs/developer_docs/Tooling-and-Sanitizers.md b/docs/developer_docs/Tooling-and-Sanitizers.md new file mode 100644 index 00000000..19c569e6 --- /dev/null +++ b/docs/developer_docs/Tooling-and-Sanitizers.md @@ -0,0 +1,35 @@ +| [Home](/trick) → [Developer Docs](Developer-Docs-Home) → Tooling and Sanitizers | +|------------------------------------------------------------------| + +Lots of development and debugging tools require a binary to be instrumented with compiler flags. Trick does compiling and linking steps separately and uses several variables to propogate flags to different parts of the build. The following is a convenience function that can be added to your bashrc to easily modify the flags in your environment: + +``` +add-trickenv () { + export CFLAGS="$CFLAGS $1" + export CXXFLAGS="$CXXFLAGS $1" + export LDFLAGS="$LDFLAGS $1" + export TRICK_CFLAGS="$TRICK_CFLAGS $1" + export TRICK_CXXFLAGS="$TRICK_CXXFLAGS $1" + export TRICK_LDFLAGS="$TRICK_LDFLAGS $1" + export TRICK_SYSTEM_CFLAGS="$TRICK_SYSTEM_CFLAGS $1" + export TRICK_SYSTEM_CXXFLAGS="$TRICK_SYSTEM_CXXFLAGS $1" + export TRICK_SYSTEM_LDFLAGS="$TRICK_SYSTEM_LDFLAGS $1" +} +``` + +To debug a sim, you will likely need to run a clean build of all of Trick with these flags set. + +## Tools that are known to work well with Trick + +GDB/LLDB: `-g` + +gcov: `-fprofile-arcs -ftest-coverage -O0` + +tsan: `-g -fsanitize=thread` + +asan: `-g -fsanitize=address -fsanitize-recover=address` + +Suggest running asan instrumented sims with: + +`ASAN_OPTIONS=halt_on_error=0 ./S_main* ` + diff --git a/docs/documentation/building_a_simulation/Model-Source-Code.md b/docs/documentation/building_a_simulation/Model-Source-Code.md index 06efc40c..15c56389 100644 --- a/docs/documentation/building_a_simulation/Model-Source-Code.md +++ b/docs/documentation/building_a_simulation/Model-Source-Code.md @@ -162,7 +162,7 @@ The `ICG IGNORE TYPES` field lists the structs or classes to be ignored. Any par ###### `PYTHON_MODULE` -Specifying a `python_module` name will place any class/struct and function definitions in this header file in a python module of the same name. All classes and functions are flattened into the python `trick` namespace by default. This capability allows users to avoid possible name collisions between names when they are flattened. +Specifying a `python_module` name will place any class/struct and function definitions in this header file in a python module of the same name. All classes and functions are flattened into the python `trick` namespace by default. This capability allows users to avoid possible name collisions between names when they are flattened. An empty `python_module` statement will be ignored. ##### Compiler Directives diff --git a/docs/documentation/running_a_simulation/Input-File.md b/docs/documentation/running_a_simulation/Input-File.md index 02a9fd80..68cfd5b4 100644 --- a/docs/documentation/running_a_simulation/Input-File.md +++ b/docs/documentation/running_a_simulation/Input-File.md @@ -449,6 +449,9 @@ For information on how Trick processes events during runtime, see [Event Process # Add the event to the input processor's list of events (it will be processed at top of frame before scheduled jobs) trick.add_event() + +# Tell trick whether to terminate the sim if an error occurs while parsing Python code. Defaults to False +trick.terminate_on_event_parse_error() ``` #### Advanced Event (Malfunction) Usage diff --git a/include/trick/IPPythonEvent.hh b/include/trick/IPPythonEvent.hh index 7a512d4b..f62fe3b9 100644 --- a/include/trick/IPPythonEvent.hh +++ b/include/trick/IPPythonEvent.hh @@ -184,6 +184,15 @@ namespace Trick { */ static void set_event_info_msg_off() ; + /** + @brief @userdesc Command to set whether the sim should check error codes from + Python parsing and terminate if an error is detected. Set to false by default + @par Python Usage: + @code trick.terminate_on_event_parse_error(True|False) @endcode + @return always 0 + */ + static void terminate_on_event_parse_error(bool on_off); + /** @brief called by the event manager when the event is loaded from a checkpoint */ @@ -402,7 +411,7 @@ namespace Trick { any events instantiated yet */ static void set_python_processor(Trick::IPPython * in_ip) ; static void set_mtv(Trick::MTV * in_mtv) ; - + private: /* A static pointer to the python input processor set at the S_define level */ @@ -411,6 +420,8 @@ namespace Trick { /* A static pointer to the MTV set at the S_define level */ static Trick::MTV * mtv ; + /* Defaults to false */ + static bool terminate_sim_on_event_python_error; } ; } diff --git a/libexec/trick/pm/html.pm b/libexec/trick/pm/html.pm index a6da5c62..d9524373 100644 --- a/libexec/trick/pm/html.pm +++ b/libexec/trick/pm/html.pm @@ -31,7 +31,7 @@ sub extract_trick_header($$$$) { $header{icg_ignore} = $2 if $trick_header =~ /ICG[ _]IGNORE[ _]TYPE(S)?:[^(]*(.*?)\)([A-Z _\t\n\r]+:|[ \t\n\r]*$)/si ; $header{swig} = $1 if $trick_header =~ /SWIG:[^(]*\((.*?)\)([A-Z _\t\n\r]+:|[ \t\n\r]*$)/si ; $header{default_data} = $1 if $trick_header =~ /DEFAULT[ _]DATA:[^(]*(.*?)\)([A-Z _\t\n\r]+:|[ \t\n\r]*$)/si ; - $header{python_module} = $1 if $trick_header =~ /PYTHON[ _]MODULE:[^(]*\((.*?)\)([A-Z _\t\n\r]+:|[ \t\n\r]*$)/si ; + $header{python_module} = $1 if $trick_header =~ /PYTHON[ _]MODULE:[^(]*\((.+?)\)([A-Z _\t\n\r]+:|[ \t\n\r]*$)/si ; $header{programmers} = $1 if $trick_header =~ /PROGRAMMERS:[^(]*(.*?)\)([A-Z _\t\n\r]+:|[ \t\n\r]*$)/si ; $header{language} = $1 if $trick_header =~ /LANGUAGE:[^(]*(.*?)\)([A-Z _\t\n\r]+:|[ \t\n\r]*$)/si ; diff --git a/share/trick/swig/shortcuts.py b/share/trick/swig/shortcuts.py index eaa6c783..7bd36f0d 100644 --- a/share/trick/swig/shortcuts.py +++ b/share/trick/swig/shortcuts.py @@ -44,6 +44,7 @@ if hasattr(top.cvar, 'trick_ip'): set_event_info_msg_on = trick.IPPythonEvent.set_event_info_msg_on set_event_info_msg_off = trick.IPPythonEvent.set_event_info_msg_off +terminate_on_event_parse_error = trick.IPPythonEvent.terminate_on_event_parse_error # bind pyton input_processor event routines to shortcut names. new_event = trick.ippython_new_event diff --git a/test/SIM_events/RUN_test/unit_test_error1.py b/test/SIM_events/RUN_test/unit_test_error1.py new file mode 100644 index 00000000..c72fd8e8 --- /dev/null +++ b/test/SIM_events/RUN_test/unit_test_error1.py @@ -0,0 +1,7 @@ +trick.stop(1.0) + +# print(dir(trick)) +trick.terminate_on_event_parse_error(True) + +# Error in add read +trick.add_read(0.1, "a = b") \ No newline at end of file diff --git a/test/SIM_events/RUN_test/unit_test_error2.py b/test/SIM_events/RUN_test/unit_test_error2.py new file mode 100644 index 00000000..c7e7af3f --- /dev/null +++ b/test/SIM_events/RUN_test/unit_test_error2.py @@ -0,0 +1,12 @@ +trick.terminate_on_event_parse_error(True) + +# Error in condition +event1 = trick.new_event("event1") +event1.condition(0, "this is a syntax error") +event1.action(0, "print (\"event1\");") +event1.action(1, "event1.activate()") +event1.set_cycle(1.0) +event1.activate() +trick.add_event(event1) + +trick.stop(10) \ No newline at end of file diff --git a/test/SIM_events/RUN_test/unit_test_error3.py b/test/SIM_events/RUN_test/unit_test_error3.py new file mode 100644 index 00000000..65d3954c --- /dev/null +++ b/test/SIM_events/RUN_test/unit_test_error3.py @@ -0,0 +1,12 @@ +trick.terminate_on_event_parse_error(True) + +# Error in event action +event1 = trick.new_event("event1") +event1.condition(0, "True") +event1.action(0, "this is a syntax error") +event1.action(1, "event1.activate()") +event1.set_cycle(1.0) +event1.activate() +trick.add_event(event1) + +trick.stop(10) \ No newline at end of file diff --git a/test/SIM_python_namespace/RUN_test/unit_test.py b/test/SIM_python_namespace/RUN_test/unit_test.py index b50e2da3..00b5c529 100644 --- a/test/SIM_python_namespace/RUN_test/unit_test.py +++ b/test/SIM_python_namespace/RUN_test/unit_test.py @@ -43,6 +43,13 @@ def main(): yummy.yummy = trick.Foo.Doughnuts TRICK_EXPECT_EQ( yummy.yummy , 2, test_suite , "additional file in same namespace" ) + # new class from TrickFood + trickfood = trick.Food() + trickfood.print_me() + TRICK_EXPECT_EQ( trickfood.fast , 2, test_suite , "blank python_module statement" ) + trickfood.fast = trick.Pizza + TRICK_EXPECT_EQ( trickfood.fast , 0, test_suite , "blank python_module statement" ) + if __name__ == "__main__": main() diff --git a/test/SIM_python_namespace/S_define b/test/SIM_python_namespace/S_define index ff42c34c..ec9da89a 100644 --- a/test/SIM_python_namespace/S_define +++ b/test/SIM_python_namespace/S_define @@ -1,6 +1,6 @@ /************************TRICK HEADER************************* PURPOSE: - (blah blah blah) + (Test different combinations of Python modules and C++ namespaces) *************************************************************/ #include "sim_objects/default_trick_sys.sm" @@ -9,6 +9,7 @@ PURPOSE: ##include "FooInnerFood.hh" ##include "BarFood.hh" ##include "FooYummyFood.hh" +##include "TrickFood.hh" class SimObj : public Trick::SimObject { @@ -17,6 +18,7 @@ class SimObj : public Trick::SimObject { Foo::Inner::Food foo_inner_food ; Bar::Food bar_food ; Foo::YummyFood foo_yummyfood ; + Food trick_food; /** Constructor to add the jobs */ SimObj() { diff --git a/test/SIM_python_namespace/models/TrickFood.hh b/test/SIM_python_namespace/models/TrickFood.hh new file mode 100644 index 00000000..0a86c475 --- /dev/null +++ b/test/SIM_python_namespace/models/TrickFood.hh @@ -0,0 +1,32 @@ +/** +@file + +@verbatim +PURPOSE: (Test that an empty PYTHON_MODULE won't cause errors or have any effect.) +PYTHON_MODULE: () +@endverbatim +*******************************************************************************/ + +#ifndef TRICKFOOD_HH +#define TRICKFOOD_HH + +#include + + + +enum Fast { + Pizza, + Burger, + Taco +}; + +class Food { + public: + Food() : fast(Taco) {} + void print_me() { std::cout << "Food::print_me!" << std::endl; } + Fast fast; +}; + + +#endif + diff --git a/test_sims.yml b/test_sims.yml index 56c67985..68df7f32 100644 --- a/test_sims.yml +++ b/test_sims.yml @@ -42,13 +42,6 @@ SIM_demo_sdefine: runs: RUN_test/unit_test.py: returns: 0 -SIM_events: - path: test/SIM_events - build_command: "trick-CP -t" - binary: "T_main_{cpu}_test.exe" - runs: - RUN_test/unit_test.py: - returns: 0 SIM_exec_set_time_tic_value: path: test/SIM_exec_set_time_tic_value build_command: "trick-CP -t" @@ -267,6 +260,21 @@ SIM_checkpoint_data_recording: # compare: # - test/SIM_checkpoint_data_recording/RUN_test6/ref_log_foo2.csv vs. test/SIM_checkpoint_data_recording/RUN_test6/log_foo2.csv +SIM_events: + path: test/SIM_events + build_command: "trick-CP -t" + binary: "T_main_{cpu}_test.exe" + runs: + RUN_test/unit_test.py: + returns: 0 + RUN_test/unit_test_error1.py: + returns: 255 + RUN_test/unit_test_error2.py: + returns: 255 + RUN_test/unit_test_error3.py: + returns: 255 + + # The variable server client and SIM_amoeba sometimes fail to connect and need to be retried SIM_test_varserv: path: test/SIM_test_varserv diff --git a/trick_sims/SIM_aircraft/RUN_test/input.py b/trick_sims/SIM_aircraft/RUN_test/input.py index abf26810..5d3c19e6 100644 --- a/trick_sims/SIM_aircraft/RUN_test/input.py +++ b/trick_sims/SIM_aircraft/RUN_test/input.py @@ -11,7 +11,7 @@ dyn.aircraft.desired_speed = 200 # meters per second # Start the Satellite Graphics Client #========================================== varServerPort = trick.var_server_get_port(); -AircraftDisplay_path = "models/graphics/dist/AircraftDisplay.jar" +AircraftDisplay_path = "models/graphics/build/AircraftDisplay.jar" if (os.path.isfile(AircraftDisplay_path)) : AircraftDisplay_cmd = "java -jar " \ diff --git a/trick_sims/SIM_aircraft/models/graphics/Makefile b/trick_sims/SIM_aircraft/models/graphics/Makefile old mode 100644 new mode 100755 index a80e3a13..d3e0cbc0 --- a/trick_sims/SIM_aircraft/models/graphics/Makefile +++ b/trick_sims/SIM_aircraft/models/graphics/Makefile @@ -1,36 +1,6 @@ -SHELL = /bin/sh -PROJECT_NAME = AircraftDisplay -SRC_DIR = src -BUILD_DIR = build -CLASSES_DIR = $(BUILD_DIR)/classes -JAR_DIR = dist -MAIN_CLASS = trick.AircraftDisplay - -all: jar +all: + mvn package clean: - rm -rf $(BUILD_DIR) - rm -f manifest - -spotless: clean - rm -rf dist - -$(CLASSES_DIR): - @ mkdir -p $(CLASSES_DIR) - -compile: | $(CLASSES_DIR) - javac -sourcepath $(SRC_DIR) -d $(CLASSES_DIR) $(SRC_DIR)/trick/AircraftDisplay.java - -manifest: - @ echo "Main-Class: $(MAIN_CLASS)" > $@ - -$(JAR_DIR): - @ mkdir -p $(JAR_DIR) - -jar: compile manifest | $(JAR_DIR) - jar cvfm $(JAR_DIR)/$(PROJECT_NAME).jar manifest -C $(CLASSES_DIR) . - @ echo "-------------------------------------------------------------------------------" - @ echo " BUILD COMPLETE" - @ echo "The Java jar file (the Java Executable) is located at: $(JAR_DIR)/$(PROJECT_NAME).jar" - @ echo "-------------------------------------------------------------------------------" + rm -rf build diff --git a/trick_sims/SIM_aircraft/models/graphics/pom.xml b/trick_sims/SIM_aircraft/models/graphics/pom.xml new file mode 100644 index 00000000..d3d3e1d9 --- /dev/null +++ b/trick_sims/SIM_aircraft/models/graphics/pom.xml @@ -0,0 +1,121 @@ + + + + 4.0.0 + + trick-java + trick-java + 23.0.0-beta + + trick-java + + https://github.com/nasa/trick + + + UTF-8 + 1.8 + 1.8 + + + + + junit + junit + 4.13.1 + test + + + + + + AircraftDisplay + + build + + + org.apache.maven.plugins + maven-javadoc-plugin + 3.1.1 + + ${java.home}/bin/javadoc + ../../share/doc/trick/java + + + + + + + + + + maven-clean-plugin + 3.1.0 + + + + + maven-resources-plugin + 3.0.2 + + + + maven-compiler-plugin + 3.8.0 + + + -g + -Xlint:unchecked + -Xlint:deprecation + + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.1.0 + + + + true + lib/ + AircraftDisplay + + + + + + + maven-surefire-plugin + 2.22.1 + + + + maven-install-plugin + 2.5.2 + + + + maven-deploy-plugin + 2.8.2 + + + + + maven-site-plugin + 3.7.1 + + + + + + + + diff --git a/trick_sims/SIM_aircraft/models/graphics/src/trick/AircraftDisplay.java b/trick_sims/SIM_aircraft/models/graphics/src/main/java/trick/aircraftDisplay/AircraftDisplay.java similarity index 99% rename from trick_sims/SIM_aircraft/models/graphics/src/trick/AircraftDisplay.java rename to trick_sims/SIM_aircraft/models/graphics/src/main/java/trick/aircraftDisplay/AircraftDisplay.java index 75ec2b22..16eae817 100644 --- a/trick_sims/SIM_aircraft/models/graphics/src/trick/AircraftDisplay.java +++ b/trick_sims/SIM_aircraft/models/graphics/src/main/java/trick/aircraftDisplay/AircraftDisplay.java @@ -3,7 +3,7 @@ * 2016 (c) National Aeronautics and Space Administration (NASA) */ -package trick; +/* package trick;*/ import java.awt.Graphics2D; import java.awt.Graphics; diff --git a/trick_source/sim_services/InputProcessor/IPPython.cpp b/trick_source/sim_services/InputProcessor/IPPython.cpp index a26b01ed..665b5772 100644 --- a/trick_source/sim_services/InputProcessor/IPPython.cpp +++ b/trick_source/sim_services/InputProcessor/IPPython.cpp @@ -179,11 +179,11 @@ int Trick::IPPython::parse_condition(std::string in_string, int & cond_return_va pthread_mutex_lock(&ip_mutex); in_string = std::string("trick_ip.ip.return_val = ") + in_string + "\n" ; // Running the simple string will set return_val. - PyRun_SimpleString(in_string.c_str()) ; + int py_ret = PyRun_SimpleString(in_string.c_str()) ; cond_return_val = return_val ; pthread_mutex_unlock(&ip_mutex); - return 0 ; + return py_ret ; } diff --git a/trick_source/sim_services/InputProcessor/IPPythonEvent.cpp b/trick_source/sim_services/InputProcessor/IPPythonEvent.cpp index 3db54c32..c822ddf9 100644 --- a/trick_source/sim_services/InputProcessor/IPPythonEvent.cpp +++ b/trick_source/sim_services/InputProcessor/IPPythonEvent.cpp @@ -29,6 +29,7 @@ extern Trick::MemoryManager * trick_MM ; Trick::IPPython * Trick::IPPythonEvent::ip ; Trick::MTV * Trick::IPPythonEvent::mtv ; bool Trick::IPPythonEvent::info_msg = false ; +bool Trick::IPPythonEvent::terminate_sim_on_event_python_error = false; Trick::condition_t::condition_t() { enabled = 0 ; @@ -169,6 +170,10 @@ void Trick::IPPythonEvent::set_event_info_msg_off() { info_msg = false; } +void Trick::IPPythonEvent::terminate_on_event_parse_error(bool on_off) { + terminate_sim_on_event_python_error = on_off; +} + void Trick::IPPythonEvent::restart() { int jj ; @@ -515,7 +520,10 @@ int Trick::IPPythonEvent::process( long long curr_time ) { } else { // it's a read event active = false ; - ip->parse(action_list[0]->str) ; + int ret = ip->parse(action_list[0]->str) ; + if (ret != 0 && terminate_sim_on_event_python_error) { + exec_terminate_with_return( ret , __FILE__ , __LINE__ , "Python error in event processing" ) ; + } // keep stats so mtv will show when it ran fired_count++ ; fired_time = curr_time ; @@ -572,7 +580,10 @@ bool Trick::IPPythonEvent::process_user_event( long long curr_time ) { } else { // otherwise use python to evaluate string std::string full_in_string ; - ip->parse_condition(condition_list[ii]->str, return_val) ; + int python_ret = ip->parse_condition(condition_list[ii]->str, return_val) ; + if (python_ret != 0 && terminate_sim_on_event_python_error) { + exec_terminate_with_return( python_ret , __FILE__ , __LINE__ , "Python error in event condition processing" ) ; + } } if (return_val) { //TODO: write to log/send_hs that trigger fired @@ -639,8 +650,11 @@ bool Trick::IPPythonEvent::process_user_event( long long curr_time ) { break; } } else { - // otherwise use python to evaluate string - ip->parse(action_list[ii]->str) ; + // otherwise use python to evaluate string + int ret = ip->parse(action_list[ii]->str) ; + if (ret != 0 && terminate_sim_on_event_python_error) { + exec_terminate_with_return( ret , __FILE__ , __LINE__ , "Python error in event action processing" ) ; + } } it_ran = true ; action_list[ii]->ran = true ;