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.
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

Binary file not shown.

134
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);
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 <dev> DEBUG=opt1{;opt2}\" and\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 *************************/
"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 */
@ -10900,6 +11033,7 @@ do {
(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) &&

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 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__

View file

@ -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 */