diff --git a/README.md b/README.md index 3503dd32..46980cbd 100644 --- a/README.md +++ b/README.md @@ -405,6 +405,7 @@ Device simulator authors can easily schedule their device polling activities to RUN UNTIL "output-string" ... Establish the specified "output-string" as an EXPECT and run until it is encountered. GO UNTIL breakpoint Establish the breakpoint specified and go until it is encountered GO UNTIL "output-string" ... Establish the specified "output-string" as an EXPECT and go until it is encountered. + RUNLIMIT Bound simulator execution time #### Command Processing Enhancements diff --git a/doc/simh_doc.doc b/doc/simh_doc.doc index 3f11ff96..feaa0a54 100644 Binary files a/doc/simh_doc.doc and b/doc/simh_doc.doc differ diff --git a/scp.c b/scp.c index 5ddf5d0c..3556f90c 100644 --- a/scp.c +++ b/scp.c @@ -545,6 +545,7 @@ t_stat dep_addr (int32 flag, const char *cptr, t_addr addr, DEVICE *dptr, UNIT *uptr, int32 dfltinc); void fprint_fields (FILE *stream, t_value before, t_value after, BITFIELD* bitdefs); t_stat step_svc (UNIT *ptr); +t_stat runlimit_svc (UNIT *ptr); t_stat expect_svc (UNIT *ptr); t_stat flush_svc (UNIT *ptr); t_stat shift_args (char *do_arg[], size_t arg_count); @@ -596,6 +597,10 @@ int32 sim_brk_lnt = 0; int32 sim_brk_ins = 0; int32 sim_quiet = 0; int32 sim_step = 0; +int32 sim_runlimit = 0; +double sim_runlimit_d = 0.0; +int32 sim_runlimit_switches = 0; +t_bool sim_runlimit_enabled = FALSE; char *sim_sub_instr = NULL; /* Copy of pre-substitution buffer contents */ char *sim_sub_instr_buf = NULL; /* Buffer address that substitutions were saved in */ size_t sim_sub_instr_size = 0; /* substitution buffer size */ @@ -662,6 +667,31 @@ DEVICE sim_step_dev = { NULL, NULL, NULL, NULL, NULL, NULL, sim_int_step_description}; +static const char *sim_int_runlimit_description (DEVICE *dptr) +{ +return "Run time limit facility"; +} + +static t_stat sim_int_runlimit_reset (DEVICE *dptr) +{ +if (sim_runlimit_enabled) { + if (sim_runlimit_switches & SWMASK ('T')) + return sim_activate_after_d (dptr->units, sim_runlimit_d); + else + return sim_activate (dptr->units, sim_runlimit); + } +return SCPE_OK; +} + +static UNIT sim_runlimit_unit = { UDATA (&runlimit_svc, UNIT_IDLE, 0) }; +DEVICE sim_runlimit_dev = { + "INT-RUNLIMIT", &sim_runlimit_unit, NULL, NULL, + 1, 0, 0, 0, 0, 0, + NULL, NULL, &sim_int_runlimit_reset, NULL, NULL, NULL, + NULL, DEV_NOSAVE, 0, + NULL, NULL, NULL, NULL, NULL, NULL, + sim_int_runlimit_description}; + static const char *sim_int_expect_description (DEVICE *dptr) { return "Expect facility"; @@ -764,6 +794,7 @@ const struct scp_error { {"INVEXPR", "invalid expression"}, {"SIGTERM", "SIGTERM received"}, {"FSSIZE", "File System size larger than disk size"}, + {"RUNTIME", "Run time limit exhausted"}, }; const size_t size_map[] = { sizeof (int8), @@ -1072,6 +1103,20 @@ static const char simh_help[] = " \"SET NODEBUG\" commands. Additionally, support is provided that is\n" " equivalent to the \"SET DEBUG=opt1{;opt2}\" and\n" " \"SET NODEBUG=opt1{;opt2}\" commands.\n\n" +#define HLP_RUNLIMIT "*Commands Stopping_The_Simulator User_Specified_Stop_Conditions RUNLIMIT" + "4RUNLIMIT\n" + " A simulator user may want to limit the maximum execution time that a\n" + " simulator may run for. This might be appropriate to limit a runaway\n" + " diagnostic which didn't achieve explicit success or failure within\n" + " some user specified time. The RUNLIMIT command provides ways to\n" + " limit execution.\n\n" + "++RUNLIMIT n {CYCLES|MICROSECONDS|SECONDS|MINUTES|HOURS}\n" + "++NORUNLIMIT\n\n" + " If the units of the run limit are not specified, the default units are\n" + " cycles. Once an execution run limit has beenn reached, any subsequent\n" + " GO, RUN, CONTINUE, STEP or BOOT commands will cause the simulator to\n" + " exit. A previously defined RUNLIMIT can be cleared with the NORUNLIMIT\n" + " command or the establishment of a new run limit.\n" /***************** 80 character line width template *************************/ "2Connecting and Disconnecting Devices\n" " Except for main memory and network devices, units are simulated as\n" @@ -1802,6 +1847,8 @@ static const char simh_help[] = " Invalid remote console command\n" "5 AMBREG\n" " Ambiguous register\n" + "5 RUNTIME\n" + " Run time limit exhausted\n" #define HLP_SHIFT "*Commands Executing_Command_Files SHIFT" "3SHIFT\n" "++shift shift the command file's positional parameters\n" @@ -2389,6 +2436,8 @@ static CTAB cmd_table[] = { #if defined(USE_SIM_VIDEO) { "SCREENSHOT", &screenshot_cmd,0, HLP_SCREENSHOT, NULL, NULL }, #endif + { "RUNLIMIT", &runlimit_cmd, 1, HLP_RUNLIMIT, NULL, NULL }, + { "NORUNLIMIT", &runlimit_cmd, 0, HLP_RUNLIMIT, NULL, NULL }, { NULL, NULL, 0, NULL, NULL, NULL } }; @@ -2596,6 +2645,7 @@ sim_register_internal_device (&sim_scp_dev); sim_register_internal_device (&sim_expect_dev); sim_register_internal_device (&sim_step_dev); sim_register_internal_device (&sim_flush_dev); +sim_register_internal_device (&sim_runlimit_dev); if ((stat = sim_ttinit ()) != SCPE_OK) { fprintf (stderr, "Fatal terminal initialization error\n%s\n", @@ -6827,6 +6877,69 @@ if (dptr->reset != NULL) else return SCPE_OK; } +t_stat runlimit_cmd (int32 flag, CONST char *cptr) +{ +char gbuf[CBUFSIZE]; +int32 num; +t_stat r; +double usec_factor = 1.0; + +GET_SWITCHES (cptr); /* get switches */ +if (0 == flag) { + if (*cptr) + return sim_messagef (SCPE_ARG, "NORUNLIMIT expects no arguments: %s\n", cptr); + sim_runlimit = 0; + sim_runlimit_switches = 0; + sim_runlimit_enabled = FALSE; + sim_cancel (&sim_runlimit_unit); + return SCPE_OK; + } + +cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ +num = (int32) get_uint (gbuf, 10, INT_MAX, &r); +if ((r != SCPE_OK) || (num == 0)) /* error? */ + return sim_messagef (SCPE_ARG, "Invalid argument: %s\n", gbuf); +cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ +if (MATCH_CMD (gbuf, "CYCLES") == 0) + sim_switches &= ~SWMASK ('T'); +else { + int i; + struct { + const char *name; + double usec_factor; + } time_units[] = { + {"MICROSECONDS", 1.0}, + {"USECONDS", 1.0}, + {"SECONDS", 1000000.0}, + {"MINUTES", 60*1000000.0}, + {"HOURS", 60*60*1000000.0}, + {NULL, 0.0}}; + + for (i=0; time_units[i].name; i++) { + if (MATCH_CMD (gbuf, time_units[i].name) == 0) { + sim_switches |= SWMASK ('T'); + usec_factor = time_units[i].usec_factor; + break; + } + } + if (time_units[i].name == NULL) + return sim_messagef (SCPE_2MARG, "Too many arguments: %s %s\n", gbuf, cptr); + } +if (*cptr) + return sim_messagef (SCPE_2MARG, "Too many arguments: %s\n", cptr); +sim_runlimit_enabled = TRUE; +sim_cancel (&sim_runlimit_unit); +sim_runlimit_switches = sim_switches; +if (sim_runlimit_switches & SWMASK ('T')) { + sim_runlimit_d = num * usec_factor; + return sim_activate_after_d (&sim_runlimit_unit, sim_runlimit_d); + } +else { + sim_runlimit = num; + return sim_activate (&sim_runlimit_unit, sim_runlimit); + } +} + /* Reset devices start..end Inputs: @@ -7950,6 +8063,11 @@ t_stat r; DEVICE *dptr; UNIT *uptr; +if (sim_runlimit_enabled && /* If the run limit has been hit? */ + (!sim_is_active (&sim_runlimit_unit))) { + sim_messagef (SCPE_RUNTIME, "Execution limit exceeded, can't proceed. Exiting...\n"); + exit (SCPE_RUNTIME); /* Execution can't proceed */ + } GET_SWITCHES (cptr); /* get switches */ sim_step = 0; if ((flag == RU_RUN) || (flag == RU_GO)) { /* run or go */ @@ -8168,6 +8286,13 @@ if ((SCPE_BARE_STATUS(r) == SCPE_STOP) && sigterm_received) r = SCPE_SIGTERM; +if (sim_runlimit_enabled) { + if (sim_runlimit_switches & SWMASK ('T')) + sim_runlimit_d = sim_activate_time_usecs (&sim_runlimit_unit); + else + sim_runlimit = sim_activate_time (&sim_runlimit_unit); + } + if ((SCPE_BARE_STATUS(r) == SCPE_STOP) && /* WRU exit from sim_instr() */ (sim_on_actions[sim_do_depth][SCPE_STOP] == NULL) &&/* without a handler for a STOP condition */ (sim_on_actions[sim_do_depth][0] == NULL)) @@ -8298,6 +8423,14 @@ t_stat step_svc (UNIT *uptr) return SCPE_STEP; } +/* Unit service for run for timeout, originally scheduled by RUNFOR n command + Return runlimit timeout SCP code, will cause simulation to stop */ + +t_stat runlimit_svc (UNIT *uptr) +{ +return SCPE_RUNTIME; +} + /* Unit service to facilitate expect matching to stop simulation. Return expect SCP code, will cause simulation to stop */ @@ -10893,13 +11026,14 @@ do { } AIO_EVENT_COMPLETE(uptr, reason); bare_reason = SCPE_BARE_STATUS (reason); - if ((bare_reason != SCPE_OK) && /* Provide context for unexpected errors */ - (bare_reason >= SCPE_BASE) && - (bare_reason != SCPE_EXPECT) && - (bare_reason != SCPE_REMOTE) && - (bare_reason != SCPE_MTRLNT) && - (bare_reason != SCPE_STOP) && - (bare_reason != SCPE_STEP) && + if ((bare_reason != SCPE_OK) && /* Provide context for unexpected errors */ + (bare_reason >= SCPE_BASE) && + (bare_reason != SCPE_EXPECT) && + (bare_reason != SCPE_REMOTE) && + (bare_reason != SCPE_MTRLNT) && + (bare_reason != SCPE_STOP) && + (bare_reason != SCPE_STEP) && + (bare_reason != SCPE_RUNTIME) && (bare_reason != SCPE_EXIT)) sim_messagef (reason, "\nUnexpected internal error while processing event for %s which returned %d - %s\n", sim_uname (uptr), reason, sim_error_text (reason)); } while ((reason == SCPE_OK) && diff --git a/scp.h b/scp.h index 6b07009e..7309da66 100644 --- a/scp.h +++ b/scp.h @@ -116,6 +116,7 @@ t_stat spawn_cmd (int32 flag, CONST char *ptr); t_stat echo_cmd (int32 flag, CONST char *ptr); t_stat echof_cmd (int32 flag, CONST char *ptr); t_stat debug_cmd (int32 flag, CONST char *ptr); +t_stat runlimit_cmd (int32 flag, CONST char *ptr); /* Allow compiler to help validate printf style format arguments */ #if !defined __GNUC__ diff --git a/sim_defs.h b/sim_defs.h index f68d0a6f..a90eb2d6 100644 --- a/sim_defs.h +++ b/sim_defs.h @@ -413,8 +413,9 @@ typedef uint32 t_addr; #define SCPE_INVEXPR (SCPE_BASE + 47) /* invalid expression */ #define SCPE_SIGTERM (SCPE_BASE + 48) /* SIGTERM has been received */ #define SCPE_FSSIZE (SCPE_BASE + 49) /* File System size larger than disk size */ +#define SCPE_RUNTIME (SCPE_BASE + 50) /* Run Time Limit Exhausted */ -#define SCPE_MAX_ERR (SCPE_BASE + 49) /* Maximum SCPE Error Value */ +#define SCPE_MAX_ERR (SCPE_BASE + 50) /* Maximum SCPE Error Value */ #define SCPE_KFLAG 0x10000000 /* tti data flag */ #define SCPE_BREAK 0x20000000 /* tti break flag */ #define SCPE_NOMESSAGE 0x40000000 /* message display supression flag */