SCP: Add a RUNLIMIT command to bound simulator execution time

This commit is contained in:
Mark Pizzolato 2020-01-08 11:49:22 -08:00
parent 35252369eb
commit 7581b92f9d
5 changed files with 145 additions and 8 deletions

View file

@ -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. 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 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. 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 #### Command Processing Enhancements

Binary file not shown.

148
scp.c
View file

@ -545,6 +545,7 @@ t_stat dep_addr (int32 flag, const char *cptr, t_addr addr, DEVICE *dptr,
UNIT *uptr, int32 dfltinc); UNIT *uptr, int32 dfltinc);
void fprint_fields (FILE *stream, t_value before, t_value after, BITFIELD* bitdefs); void fprint_fields (FILE *stream, t_value before, t_value after, BITFIELD* bitdefs);
t_stat step_svc (UNIT *ptr); t_stat step_svc (UNIT *ptr);
t_stat runlimit_svc (UNIT *ptr);
t_stat expect_svc (UNIT *ptr); t_stat expect_svc (UNIT *ptr);
t_stat flush_svc (UNIT *ptr); t_stat flush_svc (UNIT *ptr);
t_stat shift_args (char *do_arg[], size_t arg_count); 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_brk_ins = 0;
int32 sim_quiet = 0; int32 sim_quiet = 0;
int32 sim_step = 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 = NULL; /* Copy of pre-substitution buffer contents */
char *sim_sub_instr_buf = NULL; /* Buffer address that substitutions were saved in */ char *sim_sub_instr_buf = NULL; /* Buffer address that substitutions were saved in */
size_t sim_sub_instr_size = 0; /* substitution buffer size */ size_t sim_sub_instr_size = 0; /* substitution buffer size */
@ -662,6 +667,31 @@ DEVICE sim_step_dev = {
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
sim_int_step_description}; 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) static const char *sim_int_expect_description (DEVICE *dptr)
{ {
return "Expect facility"; return "Expect facility";
@ -764,6 +794,7 @@ const struct scp_error {
{"INVEXPR", "invalid expression"}, {"INVEXPR", "invalid expression"},
{"SIGTERM", "SIGTERM received"}, {"SIGTERM", "SIGTERM received"},
{"FSSIZE", "File System size larger than disk size"}, {"FSSIZE", "File System size larger than disk size"},
{"RUNTIME", "Run time limit exhausted"},
}; };
const size_t size_map[] = { sizeof (int8), 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" " \"SET NODEBUG\" commands. Additionally, support is provided that is\n"
" equivalent to the \"SET <dev> DEBUG=opt1{;opt2}\" and\n" " equivalent to the \"SET <dev> DEBUG=opt1{;opt2}\" and\n"
" \"SET <dev> NODEBUG=opt1{;opt2}\" commands.\n\n" " \"SET <dev> 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 *************************/ /***************** 80 character line width template *************************/
"2Connecting and Disconnecting Devices\n" "2Connecting and Disconnecting Devices\n"
" Except for main memory and network devices, units are simulated as\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" " Invalid remote console command\n"
"5 AMBREG\n" "5 AMBREG\n"
" Ambiguous register\n" " Ambiguous register\n"
"5 RUNTIME\n"
" Run time limit exhausted\n"
#define HLP_SHIFT "*Commands Executing_Command_Files SHIFT" #define HLP_SHIFT "*Commands Executing_Command_Files SHIFT"
"3SHIFT\n" "3SHIFT\n"
"++shift shift the command file's positional parameters\n" "++shift shift the command file's positional parameters\n"
@ -2389,6 +2436,8 @@ static CTAB cmd_table[] = {
#if defined(USE_SIM_VIDEO) #if defined(USE_SIM_VIDEO)
{ "SCREENSHOT", &screenshot_cmd,0, HLP_SCREENSHOT, NULL, NULL }, { "SCREENSHOT", &screenshot_cmd,0, HLP_SCREENSHOT, NULL, NULL },
#endif #endif
{ "RUNLIMIT", &runlimit_cmd, 1, HLP_RUNLIMIT, NULL, NULL },
{ "NORUNLIMIT", &runlimit_cmd, 0, HLP_RUNLIMIT, NULL, NULL },
{ NULL, NULL, 0, NULL, 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_expect_dev);
sim_register_internal_device (&sim_step_dev); sim_register_internal_device (&sim_step_dev);
sim_register_internal_device (&sim_flush_dev); sim_register_internal_device (&sim_flush_dev);
sim_register_internal_device (&sim_runlimit_dev);
if ((stat = sim_ttinit ()) != SCPE_OK) { if ((stat = sim_ttinit ()) != SCPE_OK) {
fprintf (stderr, "Fatal terminal initialization error\n%s\n", fprintf (stderr, "Fatal terminal initialization error\n%s\n",
@ -6827,6 +6877,69 @@ if (dptr->reset != NULL)
else return SCPE_OK; 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 /* Reset devices start..end
Inputs: Inputs:
@ -7950,6 +8063,11 @@ t_stat r;
DEVICE *dptr; DEVICE *dptr;
UNIT *uptr; 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 */ GET_SWITCHES (cptr); /* get switches */
sim_step = 0; sim_step = 0;
if ((flag == RU_RUN) || (flag == RU_GO)) { /* run or go */ if ((flag == RU_RUN) || (flag == RU_GO)) { /* run or go */
@ -8168,6 +8286,13 @@ if ((SCPE_BARE_STATUS(r) == SCPE_STOP) &&
sigterm_received) sigterm_received)
r = SCPE_SIGTERM; 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() */ 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][SCPE_STOP] == NULL) &&/* without a handler for a STOP condition */
(sim_on_actions[sim_do_depth][0] == NULL)) (sim_on_actions[sim_do_depth][0] == NULL))
@ -8298,6 +8423,14 @@ t_stat step_svc (UNIT *uptr)
return SCPE_STEP; 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. /* Unit service to facilitate expect matching to stop simulation.
Return expect SCP code, will cause simulation to stop */ Return expect SCP code, will cause simulation to stop */
@ -10893,13 +11026,14 @@ do {
} }
AIO_EVENT_COMPLETE(uptr, reason); AIO_EVENT_COMPLETE(uptr, reason);
bare_reason = SCPE_BARE_STATUS (reason); bare_reason = SCPE_BARE_STATUS (reason);
if ((bare_reason != SCPE_OK) && /* Provide context for unexpected errors */ if ((bare_reason != SCPE_OK) && /* Provide context for unexpected errors */
(bare_reason >= SCPE_BASE) && (bare_reason >= SCPE_BASE) &&
(bare_reason != SCPE_EXPECT) && (bare_reason != SCPE_EXPECT) &&
(bare_reason != SCPE_REMOTE) && (bare_reason != SCPE_REMOTE) &&
(bare_reason != SCPE_MTRLNT) && (bare_reason != SCPE_MTRLNT) &&
(bare_reason != SCPE_STOP) && (bare_reason != SCPE_STOP) &&
(bare_reason != SCPE_STEP) && (bare_reason != SCPE_STEP) &&
(bare_reason != SCPE_RUNTIME) &&
(bare_reason != SCPE_EXIT)) (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)); 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) && } while ((reason == SCPE_OK) &&

1
scp.h
View file

@ -116,6 +116,7 @@ t_stat spawn_cmd (int32 flag, CONST char *ptr);
t_stat echo_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 echof_cmd (int32 flag, CONST char *ptr);
t_stat debug_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 */ /* Allow compiler to help validate printf style format arguments */
#if !defined __GNUC__ #if !defined __GNUC__

View file

@ -413,8 +413,9 @@ typedef uint32 t_addr;
#define SCPE_INVEXPR (SCPE_BASE + 47) /* invalid expression */ #define SCPE_INVEXPR (SCPE_BASE + 47) /* invalid expression */
#define SCPE_SIGTERM (SCPE_BASE + 48) /* SIGTERM has been received */ #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_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_KFLAG 0x10000000 /* tti data flag */
#define SCPE_BREAK 0x20000000 /* tti break flag */ #define SCPE_BREAK 0x20000000 /* tti break flag */
#define SCPE_NOMESSAGE 0x40000000 /* message display supression flag */ #define SCPE_NOMESSAGE 0x40000000 /* message display supression flag */