Line data Source code
1 : /*
2 : This program is free software: you can redistribute it and/or modify
3 : it under the terms of the GNU General Public License as published by
4 : the Free Software Foundation, either version 3 of the License, or
5 : (at your option) any later version.
6 :
7 : This program is distributed in the hope that it will be useful,
8 : but WITHOUT ANY WARRANTY; without even the implied warranty of
9 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 : GNU General Public License for more details.
11 :
12 : You should have received a copy of the GNU General Public License
13 : along with this program. If not, see <http://www.gnu.org/licenses/>.
14 : */
15 :
16 : #include "AP_Scripting_config.h"
17 :
18 : #if AP_SCRIPTING_ENABLED
19 :
20 : #include "lua_scripts.h"
21 : #include <AP_HAL/AP_HAL.h>
22 : #include "AP_Scripting.h"
23 : #include <AP_Logger/AP_Logger.h>
24 :
25 : #include <AP_Scripting/lua_generated_bindings.h>
26 :
27 : #define DISABLE_INTERRUPTS_FOR_SCRIPT_RUN 0
28 :
29 : extern const AP_HAL::HAL& hal;
30 : #define ENABLE_DEBUG_MODULE 0
31 :
32 : bool lua_scripts::overtime;
33 : char *lua_scripts::error_msg_buf;
34 : HAL_Semaphore lua_scripts::error_msg_buf_sem;
35 : uint8_t lua_scripts::print_error_count;
36 : uint32_t lua_scripts::last_print_ms;
37 :
38 : uint32_t lua_scripts::loaded_checksum;
39 : uint32_t lua_scripts::running_checksum;
40 : HAL_Semaphore lua_scripts::crc_sem;
41 :
42 : // return string error message for error object at top of stack
43 0 : static const char *get_error_object_message(lua_State *L) {
44 0 : const char *m = lua_tostring(L, -1);
45 0 : if (!m) { // error object is not stringifiable
46 0 : return "<error>";
47 : }
48 0 : return m;
49 : }
50 :
51 97 : lua_scripts::lua_scripts(const AP_Int32 &vm_steps, const AP_Int32 &heap_size, AP_Int8 &debug_options)
52 97 : : _vm_steps(vm_steps),
53 97 : _debug_options(debug_options)
54 : {
55 97 : const bool allow_heap_expansion = !option_is_set(AP_Scripting::DebugOption::DISABLE_HEAP_EXPANSION);
56 97 : _heap.create(heap_size, 10, allow_heap_expansion, 20*1024);
57 97 : }
58 :
59 56 : lua_scripts::~lua_scripts() {
60 56 : _heap.destroy();
61 56 : }
62 :
63 1 : void lua_scripts::hook(lua_State *L, lua_Debug *ar) {
64 1 : lua_scripts::overtime = true;
65 :
66 : // we need to aggressively bail out as we are over time
67 : // so we will aggressively trap errors until we clear out
68 1 : lua_sethook(L, hook, LUA_MASKCOUNT, 1);
69 :
70 1 : luaL_error(L, "Exceeded CPU time");
71 0 : }
72 :
73 438 : void lua_scripts::print_error(MAV_SEVERITY severity) {
74 438 : error_msg_buf_sem.take_blocking();
75 438 : if (error_msg_buf == nullptr) {
76 435 : error_msg_buf_sem.give();
77 435 : return;
78 : }
79 3 : last_print_ms = AP_HAL::millis();
80 3 : GCS_SEND_TEXT(severity, "Lua: %s", error_msg_buf);
81 3 : error_msg_buf_sem.give();
82 : }
83 :
84 1 : void lua_scripts::set_and_print_new_error_message(MAV_SEVERITY severity, const char *fmt, ...) {
85 1 : error_msg_buf_sem.take_blocking();
86 :
87 : // reset buffer and print count
88 1 : print_error_count = 0;
89 1 : if (error_msg_buf) {
90 0 : _heap.deallocate(error_msg_buf);
91 0 : error_msg_buf = nullptr;
92 : }
93 :
94 : // generate va_list and create a copy
95 : va_list arg_list, arg_list_copy;
96 1 : va_start(arg_list, fmt);
97 1 : va_copy(arg_list_copy, arg_list);
98 :
99 : // dry run to work out the required length
100 1 : int len = hal.util->vsnprintf(nullptr, 0, fmt, arg_list_copy);
101 :
102 : // finished with copy
103 1 : va_end(arg_list_copy);
104 :
105 1 : if (len <= 0) {
106 : // nothing to print, something has gone wrong
107 0 : va_end(arg_list);
108 0 : error_msg_buf_sem.give();
109 0 : return;
110 : }
111 :
112 : // allocate buffer on scripting heap
113 1 : error_msg_buf = (char *)_heap.allocate(len+1);
114 1 : if (!error_msg_buf) {
115 : // allocation failed
116 0 : va_end(arg_list);
117 0 : error_msg_buf_sem.give();
118 0 : return;
119 : }
120 :
121 : // do actual print to buffer and clear va list
122 1 : hal.util->vsnprintf(error_msg_buf, len+1, fmt, arg_list);
123 1 : va_end(arg_list);
124 :
125 : // print to cosole and GCS
126 1 : DEV_PRINTF("Lua: %s\n", error_msg_buf);
127 :
128 1 : error_msg_buf_sem.give();
129 1 : print_error(severity);
130 : }
131 :
132 : // helper for print and log of runtime stats
133 90481 : void lua_scripts::update_stats(const char *name, uint32_t run_time, int total_mem, int run_mem)
134 : {
135 90481 : if (option_is_set(AP_Scripting::DebugOption::RUNTIME_MSG)) {
136 0 : GCS_SEND_TEXT(MAV_SEVERITY_DEBUG, "Lua: Time: %u Mem: %d + %d",
137 : (unsigned int)run_time,
138 : (int)total_mem,
139 : (int)run_mem);
140 : }
141 : #if HAL_LOGGING_ENABLED
142 90481 : if (option_is_set(AP_Scripting::DebugOption::LOG_RUNTIME)) {
143 54 : struct log_Scripting pkt {
144 : LOG_PACKET_HEADER_INIT(LOG_SCRIPTING_MSG),
145 54 : time_us : AP_HAL::micros64(),
146 : name : {},
147 : run_time : run_time,
148 : total_mem : total_mem,
149 : run_mem : run_mem
150 54 : };
151 54 : const char * name_short = strrchr(name, '/');
152 54 : if ((strlen(name) > sizeof(pkt.name)) && (name_short != nullptr)) {
153 54 : strncpy_noterm(pkt.name, name_short+1, sizeof(pkt.name));
154 : } else {
155 0 : strncpy_noterm(pkt.name, name, sizeof(pkt.name));
156 : }
157 54 : AP::logger().WriteBlock(&pkt, sizeof(pkt));
158 : }
159 : #endif // HAL_LOGGING_ENABLED
160 90481 : }
161 :
162 92 : bool lua_scripts::load_script(lua_State *L, script_info *new_script) {
163 92 : const char *filename = new_script->name;
164 :
165 92 : if (int error = luaL_loadfile(L, filename)) {
166 0 : switch (error) {
167 0 : case LUA_ERRSYNTAX:
168 0 : set_and_print_new_error_message(MAV_SEVERITY_CRITICAL, "Error: %s", get_error_object_message(L));
169 0 : lua_pop(L, lua_gettop(L));
170 0 : return false;
171 0 : case LUA_ERRMEM:
172 0 : set_and_print_new_error_message(MAV_SEVERITY_CRITICAL, "Insufficent memory loading %s", filename);
173 0 : lua_pop(L, lua_gettop(L));
174 0 : return false;
175 0 : case LUA_ERRFILE:
176 0 : set_and_print_new_error_message(MAV_SEVERITY_CRITICAL, "Unable to load the file: %s", get_error_object_message(L));
177 0 : lua_pop(L, lua_gettop(L));
178 0 : return false;
179 0 : default:
180 0 : set_and_print_new_error_message(MAV_SEVERITY_CRITICAL, "Unknown error (%d) loading %s", error, filename);
181 0 : lua_pop(L, lua_gettop(L));
182 0 : return false;
183 : }
184 : }
185 :
186 92 : const int loadMem = lua_gc(L, LUA_GCCOUNT, 0) * 1024 + lua_gc(L, LUA_GCCOUNTB, 0);
187 92 : const uint32_t loadStart = AP_HAL::micros();
188 :
189 92 : create_sandbox(L);
190 92 : lua_pushvalue(L, -1); // duplicate environment for reference below
191 92 : lua_setupvalue(L, -3, 1);
192 :
193 92 : const uint32_t loadEnd = AP_HAL::micros();
194 92 : const int endMem = lua_gc(L, LUA_GCCOUNT, 0) * 1024 + lua_gc(L, LUA_GCCOUNTB, 0);
195 :
196 92 : update_stats(filename, loadEnd-loadStart, endMem, loadMem);
197 :
198 92 : new_script->env_ref = luaL_ref(L, LUA_REGISTRYINDEX); // store reference to script's environment
199 92 : new_script->run_ref = luaL_ref(L, LUA_REGISTRYINDEX); // store reference to function to run
200 92 : new_script->next_run_ms = AP_HAL::millis64() - 1; // force the script to be stale
201 :
202 : // Get checksum of file
203 92 : uint32_t crc = 0;
204 92 : if (AP::FS().crc32(filename, crc)) {
205 : // Record crc of this script
206 92 : new_script->crc = crc;
207 : {
208 : // Apply crc to checksum of all scripts
209 92 : WITH_SEMAPHORE(crc_sem);
210 92 : loaded_checksum ^= crc;
211 92 : running_checksum ^= crc;
212 92 : }
213 : }
214 :
215 92 : return true;
216 : }
217 :
218 92 : void lua_scripts::create_sandbox(lua_State *L) {
219 92 : lua_newtable(L);
220 92 : luaopen_base_sandbox(L);
221 :
222 : #if ENABLE_DEBUG_MODULE
223 : lua_pushstring(L, "debug");
224 : luaopen_debug(L);
225 : lua_settable(L, -3);
226 : #endif
227 92 : lua_pushstring(L, "math");
228 92 : luaopen_math(L);
229 92 : lua_settable(L, -3);
230 92 : lua_pushstring(L, "table");
231 92 : luaopen_table(L);
232 92 : lua_settable(L, -3);
233 92 : lua_pushstring(L, "string");
234 92 : luaopen_string(L);
235 92 : lua_settable(L, -3);
236 92 : lua_pushstring(L, "io");
237 92 : luaopen_io(L);
238 92 : lua_settable(L, -3);
239 92 : lua_pushstring(L, "package");
240 92 : luaopen_package(L);
241 92 : lua_settable(L, -3);
242 :
243 92 : load_generated_sandbox(L);
244 92 : }
245 :
246 97 : void lua_scripts::load_all_scripts_in_dir(lua_State *L, const char *dirname) {
247 97 : if (dirname == nullptr) {
248 0 : return;
249 : }
250 97 : auto *d = AP::FS().opendir(dirname);
251 97 : if (d == nullptr) {
252 : // this disk_space check will return 0 if we don't have a real
253 : // filesystem (ie. no Posix or FatFs). Do not warn in this case.
254 0 : if (AP::FS().disk_space(dirname) != 0) {
255 0 : GCS_SEND_TEXT(MAV_SEVERITY_WARNING, "Lua: open directory (%s) failed", dirname);
256 : }
257 0 : return;
258 : }
259 :
260 : // load anything that ends in .lua
261 572 : for (struct dirent *de=AP::FS().readdir(d); de; de=AP::FS().readdir(d)) {
262 475 : uint8_t length = strlen(de->d_name);
263 475 : if (length < 5) {
264 : // not long enough
265 194 : continue;
266 : }
267 :
268 281 : if ((de->d_name[0] == '.') || strncmp(&de->d_name[length-4], ".lua", 4)) {
269 : // starts with . (hidden file) or doesn't end in .lua
270 189 : continue;
271 : }
272 :
273 : // FIXME: because chunk name fetching is not working we are allocating and storing an extra string we shouldn't need to
274 92 : size_t size = strlen(dirname) + strlen(de->d_name) + 2;
275 92 : char * filename = (char *) _heap.allocate(size);
276 92 : script_info *script = (script_info *)_heap.allocate(sizeof(script_info));
277 92 : if ((filename == nullptr) || (script == nullptr)) {
278 : // unlikely to be out of memory for these, just ignore the script...
279 0 : _heap.deallocate(filename);
280 0 : _heap.deallocate(script);
281 0 : continue;
282 : }
283 92 : snprintf(filename, size, "%s/%s", dirname, de->d_name);
284 :
285 : // provisionally link the script_info into our list so it will be freed
286 : // if there is an uncaught Lua error during loading
287 92 : script->env_ref = LUA_NOREF;
288 92 : script->run_ref = LUA_NOREF;
289 92 : script->crc = 0; // ensure removing it has no effect on the CRC
290 92 : script->name = filename;
291 92 : script->next = scripts;
292 92 : scripts = script;
293 :
294 : // attempt to load the script, may raise Lua error
295 92 : bool success = load_script(L, script);
296 : // no Lua error, unlink the script_info so we can handle it properly
297 92 : scripts = script->next;
298 92 : script->next = nullptr;
299 :
300 92 : if (!success) { // discard if load failed
301 0 : _heap.deallocate(filename);
302 0 : _heap.deallocate(script);
303 0 : continue;
304 : }
305 92 : reschedule_script(script); // reschedule if load succeeded
306 :
307 : #if HAL_LOGGER_FILE_CONTENTS_ENABLED
308 92 : if (!option_is_set(AP_Scripting::DebugOption::SUPPRESS_SCRIPT_LOG)) {
309 92 : AP::logger().log_file_content(filename);
310 : }
311 : #endif
312 : }
313 97 : AP::FS().closedir(d);
314 : }
315 :
316 90393 : void lua_scripts::reset_loop_overtime(lua_State *L) {
317 90393 : overtime = false;
318 : // reset the hook to clear the counter
319 90393 : const int32_t vm_steps = MAX(_vm_steps, 1000);
320 90393 : lua_sethook(L, hook, LUA_MASKCOUNT, vm_steps);
321 90393 : }
322 :
323 90393 : void lua_scripts::run_next_script(lua_State *L) {
324 90393 : if (scripts == nullptr) {
325 : #if defined(AP_SCRIPTING_CHECKS) && AP_SCRIPTING_CHECKS >= 1
326 0 : AP_HAL::panic("Lua: Attempted to run a script without any scripts queued");
327 : #endif // defined(AP_SCRIPTING_CHECKS) && AP_SCRIPTING_CHECKS >= 1
328 : return;
329 : }
330 :
331 90393 : uint64_t start_time_ms = AP_HAL::millis64();
332 : // strip the selected script out of the list
333 90393 : script_info *script = scripts;
334 90393 : scripts = script->next;
335 :
336 : // reset the hook to clear the counter
337 90393 : reset_loop_overtime(L);
338 :
339 : // store top of stack so we can calculate the number of return values
340 90393 : int stack_top = lua_gettop(L);
341 :
342 : // pop the function to the top of the stack
343 90393 : lua_rawgeti(L, LUA_REGISTRYINDEX, script->run_ref);
344 : // set current environment for other users
345 90393 : AP::scripting()->set_current_env_ref(script->env_ref);
346 :
347 90393 : if(lua_pcall(L, 0, LUA_MULTRET, 0)) {
348 1 : if (overtime) {
349 : // script has consumed an excessive amount of CPU time
350 1 : set_and_print_new_error_message(MAV_SEVERITY_CRITICAL, "%s exceeded time limit", script->name);
351 : } else {
352 0 : set_and_print_new_error_message(MAV_SEVERITY_CRITICAL, "%s", get_error_object_message(L));
353 : }
354 1 : remove_script(L, script);
355 1 : lua_pop(L, 1);
356 1 : return;
357 : } else {
358 90388 : int returned = lua_gettop(L) - stack_top;
359 90388 : switch (returned) {
360 11 : case 0:
361 : // no time to reschedule so bail out
362 11 : remove_script(L, script);
363 11 : break;
364 90377 : case 2:
365 : {
366 : // sanity check the return types
367 90377 : if (lua_type(L, -1) != LUA_TNUMBER) {
368 0 : set_and_print_new_error_message(MAV_SEVERITY_CRITICAL, "%s did not return a delay (0x%d)", script->name, lua_type(L, -1));
369 0 : lua_pop(L, 2);
370 0 : remove_script(L, script);
371 0 : return;
372 : }
373 90377 : if (lua_type(L, -2) != LUA_TFUNCTION) {
374 0 : set_and_print_new_error_message(MAV_SEVERITY_CRITICAL, "%s did not return a function (0x%d)", script->name, lua_type(L, -2));
375 0 : lua_pop(L, 2);
376 0 : remove_script(L, script);
377 0 : return;
378 : }
379 :
380 : // types match the expectations, go ahead and reschedule
381 90377 : script->next_run_ms = start_time_ms + (uint64_t)luaL_checknumber(L, -1);
382 90377 : lua_pop(L, 1);
383 90377 : luaL_unref(L, LUA_REGISTRYINDEX, script->run_ref);
384 : // cannot cause error as we just made a free ref slot above
385 : // so there won't be any need to allocate more
386 90377 : script->run_ref = luaL_ref(L, LUA_REGISTRYINDEX);
387 90377 : reschedule_script(script);
388 90377 : break;
389 : }
390 0 : default:
391 : {
392 0 : set_and_print_new_error_message(MAV_SEVERITY_CRITICAL, "%s returned bad result count (%d)", script->name, returned);
393 0 : remove_script(L, script);
394 : // pop all the results we got that we didn't expect
395 0 : lua_pop(L, returned);
396 0 : break;
397 : }
398 : }
399 : }
400 : }
401 :
402 61 : void lua_scripts::remove_script(lua_State *L, script_info *script) {
403 61 : if (script == nullptr) {
404 0 : return;
405 : }
406 :
407 : // ensure that the script isn't in the loaded list for any reason
408 61 : if (scripts == nullptr) {
409 : // nothing to do, already not in the list
410 50 : } else if (scripts == script) {
411 49 : scripts = script->next;
412 : } else {
413 2 : for(script_info * current = scripts; current->next != nullptr; current = current->next) {
414 1 : if (current->next == script) {
415 0 : current->next = script->next;
416 0 : break;
417 : }
418 : }
419 : }
420 :
421 : {
422 : // Remove from running checksum
423 61 : WITH_SEMAPHORE(crc_sem);
424 61 : running_checksum ^= script->crc;
425 61 : }
426 :
427 61 : if (L != nullptr) {
428 : // state will be nullptr when we are tearing down
429 12 : luaL_unref(L, LUA_REGISTRYINDEX, script->env_ref);
430 12 : luaL_unref(L, LUA_REGISTRYINDEX, script->run_ref);
431 : }
432 61 : _heap.deallocate(script->name);
433 61 : _heap.deallocate(script);
434 : }
435 :
436 90469 : void lua_scripts::reschedule_script(script_info *script) {
437 90469 : if (script == nullptr) {
438 : #if defined(AP_SCRIPTING_CHECKS) && AP_SCRIPTING_CHECKS >= 1
439 0 : AP_HAL::panic("Lua: Attempted to schedule a null pointer");
440 : #endif // defined(AP_SCRIPTING_CHECKS) && AP_SCRIPTING_CHECKS >= 1
441 : return;
442 : }
443 :
444 90469 : script->next = nullptr;
445 90469 : if (scripts == nullptr) {
446 85623 : scripts = script;
447 85623 : return;
448 : }
449 :
450 4846 : uint64_t next_run_ms = script->next_run_ms;
451 :
452 4846 : if (scripts->next_run_ms > next_run_ms) {
453 20 : script->next = scripts;
454 20 : scripts = script;
455 20 : return;
456 : }
457 :
458 4826 : script_info *previous = scripts;
459 4934 : while (previous->next != nullptr) {
460 118 : if (previous->next->next_run_ms > next_run_ms) {
461 10 : script->next = previous->next;
462 10 : previous->next = script;
463 10 : return;
464 : }
465 108 : previous = previous->next;
466 : }
467 :
468 4816 : previous->next = script;
469 : }
470 :
471 : MultiHeap lua_scripts::_heap;
472 :
473 3641915 : void *lua_scripts::alloc(void *ud, void *ptr, size_t osize, size_t nsize) {
474 : (void)ud; /* not used */
475 3641915 : return _heap.change_size(ptr, osize, nsize);
476 : }
477 :
478 97 : void lua_scripts::run(void) {
479 97 : if (!_heap.available()) {
480 0 : GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "Lua: Unable to allocate a heap");
481 0 : return;
482 : }
483 :
484 97 : lua_State *L = lua_newstate(alloc, NULL);
485 97 : if (L == nullptr) {
486 0 : GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "Lua: Couldn't allocate a lua state");
487 0 : return;
488 : }
489 : // initialize the state with a pointer to us for ls_* callback trampolines
490 : // (see ls_object_from_state)
491 97 : *static_cast<lua_scripts**>(lua_getextraspace(L)) = this;
492 :
493 : // call main engine function in protected mode now that Lua itself is ready.
494 : // this catches any errors raised by the code between here and Lua scripts.
495 : // our current function must not use any Lua API which can raise an error!
496 97 : lua_pushcfunction(L, &ls_run_engine);
497 97 : if (lua_pcall(L, 0, 0, 0) != LUA_OK) { // no args, returns, or msg handler
498 0 : set_and_print_new_error_message(MAV_SEVERITY_CRITICAL,
499 : "Engine Error: %s", get_error_object_message(L));
500 : }
501 : // we are now finished with Lua, tear everything down
502 :
503 56 : lua_close(L); // shut down the state
504 56 : L = nullptr;
505 :
506 105 : while (scripts != nullptr) { // remove all scripts from the engine list
507 49 : remove_script(nullptr, scripts);
508 : }
509 :
510 : // free error message
511 56 : error_msg_buf_sem.take_blocking();
512 56 : if (error_msg_buf != nullptr) {
513 1 : _heap.deallocate(error_msg_buf);
514 1 : error_msg_buf = nullptr;
515 : }
516 56 : error_msg_buf_sem.give();
517 :
518 : // heap is now empty
519 : }
520 :
521 97 : int lua_scripts::run_engine(lua_State *L) {
522 : // run our scripting engine now that the Lua state is initialized. we are in
523 : // Lua protected mode and can safely call functions that may raise errors.
524 :
525 : // set up string metatable. we set up one for all scripts that no script has
526 : // access to, as it's impossible to set up one per-script and we don't want
527 : // any script to be able to mess with it.
528 97 : lua_pushliteral(L, ""); /* dummy string */
529 97 : lua_createtable(L, 0, 1); /* table to be metatable for strings */
530 97 : luaopen_string(L); /* get string library */
531 97 : lua_setfield(L, -2, "__index"); /* metatable.__index = string */
532 97 : lua_setmetatable(L, -2); /* set table as metatable for strings */
533 97 : lua_pop(L, 1); /* pop dummy string */
534 :
535 97 : if (option_is_set(AP_Scripting::DebugOption::RUNTIME_MSG)) {
536 0 : const int loaded_mem = lua_gc(L, LUA_GCCOUNT, 0) * 1024 + lua_gc(L, LUA_GCCOUNTB, 0);
537 0 : GCS_SEND_TEXT(MAV_SEVERITY_DEBUG, "Lua: State memory usage: %i\n", loaded_mem);
538 : }
539 :
540 : // Scan the filesystem in an appropriate manner and autostart scripts
541 : // Skip those directores disabled with SCR_DIR_DISABLE param
542 97 : uint16_t dir_disable = AP_Scripting::get_singleton()->get_disabled_dir();
543 97 : bool loaded = false;
544 97 : if ((dir_disable & uint16_t(AP_Scripting::SCR_DIR::SCRIPTS)) == 0) {
545 97 : load_all_scripts_in_dir(L, SCRIPTING_DIRECTORY);
546 97 : loaded = true;
547 : }
548 : #ifdef HAL_HAVE_AP_ROMFS_EMBEDDED_LUA
549 : if ((dir_disable & uint16_t(AP_Scripting::SCR_DIR::ROMFS)) == 0) {
550 : load_all_scripts_in_dir(L, "@ROMFS/scripts");
551 : loaded = true;
552 : }
553 : #endif
554 97 : if (!loaded) {
555 0 : GCS_SEND_TEXT(MAV_SEVERITY_CRITICAL, "Lua: All directory's disabled see SCR_DIR_DISABLE");
556 : }
557 :
558 97 : uint32_t expansion_size = 0;
559 :
560 91303 : while (AP_Scripting::get_singleton()->should_run()) {
561 : #if defined(AP_SCRIPTING_CHECKS) && AP_SCRIPTING_CHECKS >= 1
562 91247 : if (lua_gettop(L) != 0) {
563 0 : AP_HAL::panic("Lua: Stack should be empty before running scripts");
564 : }
565 : #endif // defined(AP_SCRIPTING_CHECKS) && AP_SCRIPTING_CHECKS >= 1
566 :
567 91247 : if (scripts != nullptr) {
568 : #if defined(AP_SCRIPTING_CHECKS) && AP_SCRIPTING_CHECKS >= 1
569 : // Sanity check that the scripts list is ordered correctly
570 90424 : script_info *sanity = scripts;
571 95396 : while (sanity->next != nullptr) {
572 4972 : if (sanity->next_run_ms > sanity->next->next_run_ms) {
573 0 : AP_HAL::panic("Lua: Script tasking order has been violated");
574 : }
575 4972 : sanity = sanity->next;
576 : }
577 : #endif // defined(AP_SCRIPTING_CHECKS) && AP_SCRIPTING_CHECKS >= 1
578 :
579 : // compute delay time
580 90424 : const uint64_t now_ms = AP_HAL::millis64();
581 90424 : if (now_ms < scripts->next_run_ms) {
582 88812 : const uint32_t delay_ms = scripts->next_run_ms - now_ms;
583 88812 : if (delay_ms > UINT16_MAX) {
584 : // The delay method takes a uint16, so any longer delays must be done in sections (>65.535s)
585 4 : hal.scheduler->delay(UINT16_MAX);
586 :
587 : // Restart loop, this means delay is re-calculated to make up for any drift
588 : // This also means `should_run` is re-checked allowing a possible stop or restart
589 4 : continue;
590 : }
591 88808 : hal.scheduler->delay(delay_ms);
592 : }
593 :
594 90393 : if (option_is_set(AP_Scripting::DebugOption::RUNTIME_MSG)) {
595 0 : GCS_SEND_TEXT(MAV_SEVERITY_DEBUG, "Lua: Running %s", scripts->name);
596 : }
597 : // take a copy of the script name for the purposes of
598 : // logging statistics. "scripts" may become invalid
599 : // during the "run_next_script" call, below.
600 90393 : char script_name[128+1] {};
601 90393 : strncpy_noterm(script_name, scripts->name, 128);
602 :
603 : #if DISABLE_INTERRUPTS_FOR_SCRIPT_RUN
604 : void *istate = hal.scheduler->disable_interrupts_save();
605 : #endif
606 :
607 90393 : const int startMem = lua_gc(L, LUA_GCCOUNT, 0) * 1024 + lua_gc(L, LUA_GCCOUNTB, 0);
608 90393 : const uint32_t loadEnd = AP_HAL::micros();
609 :
610 : // NOTE! the base pointer of our scripts linked list,
611 : // *and all its contents* may become invalid as part of
612 : // "run_next_script"! So do *NOT* attempt to access
613 : // anything that was in *scripts after this call.
614 90393 : run_next_script(L);
615 :
616 90389 : const uint32_t runEnd = AP_HAL::micros();
617 90389 : const int endMem = lua_gc(L, LUA_GCCOUNT, 0) * 1024 + lua_gc(L, LUA_GCCOUNTB, 0);
618 :
619 : #if DISABLE_INTERRUPTS_FOR_SCRIPT_RUN
620 : hal.scheduler->restore_interrupts(istate);
621 : #endif
622 :
623 90389 : update_stats(script_name, runEnd - loadEnd, endMem, endMem - startMem);
624 :
625 :
626 : // garbage collect after each script, this shouldn't matter, but seems to resolve a memory leak
627 90389 : lua_gc(L, LUA_GCCOLLECT, 0);
628 :
629 : } else {
630 823 : if (option_is_set(AP_Scripting::DebugOption::NO_SCRIPTS_TO_RUN)) {
631 0 : GCS_SEND_TEXT(MAV_SEVERITY_DEBUG, "Lua: No scripts to run");
632 : }
633 823 : hal.scheduler->delay(1000);
634 : }
635 :
636 : /*
637 : report a warning if SCR_HEAP_SIZE wasn't adequate and we
638 : expanded at runtime, so the user can fix it for future
639 : flights
640 : */
641 91202 : const uint32_t new_expansion_size = _heap.get_expansion_size();
642 91202 : if (new_expansion_size > expansion_size) {
643 0 : expansion_size = new_expansion_size;
644 0 : set_and_print_new_error_message(MAV_SEVERITY_WARNING, "Required SCR_HEAP_SIZE over %u", unsigned(expansion_size));
645 : }
646 :
647 : // re-print the latest error message every 10 seconds 10 times
648 91202 : const uint8_t error_prints = 10;
649 91202 : if ((print_error_count < error_prints) && (AP_HAL::millis() - last_print_ms > 10000)) {
650 : // note that we do not clear the buffer after we have finished printing, this allows it to be used for a pre-arm check
651 437 : print_error(MAV_SEVERITY_DEBUG);
652 437 : print_error_count++;
653 : }
654 : }
655 :
656 56 : return 0; // no results
657 : }
658 :
659 : // Return the file checksums of running and loaded scripts
660 0 : uint32_t lua_scripts::get_loaded_checksum()
661 : {
662 0 : WITH_SEMAPHORE(crc_sem);
663 0 : return loaded_checksum;
664 0 : }
665 :
666 0 : uint32_t lua_scripts::get_running_checksum()
667 : {
668 0 : WITH_SEMAPHORE(crc_sem);
669 0 : return running_checksum;
670 0 : }
671 :
672 : #endif // AP_SCRIPTING_ENABLED
|