FRONTPANEL: sim_frontpanel API release 12

- Cleanup session reading logic to properly handle all cases of TCP send
   coalesced data.
- Use Remote Console EXECUTE Command to consolidate register query activity
   when halted.
- Use -16 switch for EXAMINE and DEPOSIT commands to be sure to present data
   in a reliable simulator independent way.
- Add sim_panel_halt_text and sim_panel_device_debug_mode APIs
- Better cleanup of simulator process during panel shutdown on Win32
This commit is contained in:
Mark Pizzolato 2018-01-12 10:07:15 -08:00
parent 3eda8f909c
commit 2efc3a17ec
2 changed files with 212 additions and 117 deletions

View file

@ -153,9 +153,9 @@ struct PANEL {
pthread_mutex_t io_send_lock;
pthread_mutex_t io_command_lock;
int command_count;
int io_reg_query_pending;
int io_waiting;
char *io_response;
char *halt_reason;
size_t io_response_data;
size_t io_response_size;
const char *completion_string;
@ -177,6 +177,7 @@ struct PANEL {
FILE *Debug;
#if defined(_WIN32)
HANDLE hProcess;
DWORD dwProcessId;
#else
pid_t pidProcess;
#endif
@ -221,7 +222,7 @@ static const char *register_collect_mid3 = " percent ";
static const char *register_get_postfix = "sampleout";
static const char *register_get_start = "# REGISTERS-START";
static const char *register_get_end = "# REGISTERS-DONE";
static const char *register_repeat_start = "# REGISTERS-REPEAT-START\r";
static const char *register_repeat_start = "# REGISTERS-REPEAT-START";
static const char *register_repeat_end = "# REGISTERS-REPEAT-DONE";
static const char *register_dev_echo = "# REGISTERS-FOR-DEVICE:";
static const char *register_ind_echo = "# REGISTER-INDIRECT:";
@ -408,9 +409,9 @@ pthread_mutex_lock (&p->io_lock);
while (p->sock != INVALID_SOCKET) {
pthread_mutex_unlock (&p->io_lock);
msleep (1000);
pthread_mutex_lock (&p->io_lock);
if (0 == (sleeps++)%flush_interval)
sim_panel_flush_debug (p);
pthread_mutex_lock (&p->io_lock);
}
pthread_mutex_unlock (&p->io_lock);
pthread_mutex_lock (&p->io_lock);
@ -484,7 +485,7 @@ size_t i, j, buf_data, buf_needed = 0, reg_count = 0, bit_reg_count = 0;
const char *dev;
pthread_mutex_lock (&panel->io_lock);
buf_needed = 3 +
buf_needed = 3 + 7 + /* EXECUTE */
strlen (register_get_start) + /* # REGISTERS-START */
strlen (register_get_prefix); /* SHOW TIME */
for (i=0; i<panel->reg_count; i++) {
@ -492,7 +493,7 @@ for (i=0; i<panel->reg_count; i++) {
++bit_reg_count;
else {
++reg_count;
buf_needed += 9 + strlen (panel->regs[i].name) + (panel->regs[i].device_name ? strlen (panel->regs[i].device_name) : 0);
buf_needed += 10 + strlen (panel->regs[i].name) + (panel->regs[i].device_name ? strlen (panel->regs[i].device_name) : 0);
if (panel->regs[i].element_count > 0)
buf_needed += 4 + 6 /* 6 digit register array index */;
if (panel->regs[i].indirect)
@ -514,7 +515,7 @@ if (buf_needed > *buf_size) {
}
buf_data = 0;
if (reg_count) {
sprintf (*buf + buf_data, "%s\r%s\r", register_get_start, register_get_prefix);
sprintf (*buf + buf_data, "EXECUTE %s;%s;", register_get_start, register_get_prefix);
buf_data += strlen (*buf + buf_data);
}
dev = "";
@ -536,7 +537,7 @@ for (i=j=0; i<panel->reg_count; i++) {
strcpy (tbuf, *buf);
free (*buf);
*buf = tbuf;
sprintf (*buf + buf_data, "%s%s%s\r", (i == 0)? "" : "\r", register_dev_echo, reg_dev);
sprintf (*buf + buf_data, "%s%s%s;", (i == 0)? "" : ";", register_dev_echo, reg_dev);
buf_data += strlen (*buf + buf_data);
dev = reg_dev;
j = 0;
@ -544,21 +545,21 @@ for (i=j=0; i<panel->reg_count; i++) {
}
if (panel->regs[i].element_count == 0) {
if (j == 0)
sprintf (*buf + buf_data, "E -H %s %s", dev, panel->regs[i].name);
sprintf (*buf + buf_data, "E -16 %s %s", dev, panel->regs[i].name);
else
sprintf (*buf + buf_data, ",%s", panel->regs[i].name);
}
else {
if (j == 0)
sprintf (*buf + buf_data, "E -H %s %s[0:%d]", dev, panel->regs[i].name, (int)(panel->regs[i].element_count-1));
sprintf (*buf + buf_data, "E -16 %s %s[0:%d]", dev, panel->regs[i].name, (int)(panel->regs[i].element_count-1));
else
sprintf (*buf + buf_data, ",%s[0:%d]", panel->regs[i].name, (int)(panel->regs[i].element_count-1));
}
++j;
buf_data += strlen (*buf + buf_data);
}
if (buf_data && ((*buf)[buf_data-1] != '\r')) {
strcpy (*buf + buf_data, "\r");
if (buf_data && ((*buf)[buf_data-1] != ';')) {
strcpy (*buf + buf_data, ";");
buf_data += strlen (*buf + buf_data);
}
for (i=j=0; i<panel->reg_count; i++) {
@ -566,13 +567,13 @@ for (i=j=0; i<panel->reg_count; i++) {
if ((!panel->regs[i].indirect) || (panel->regs[i].bits))
continue;
sprintf (*buf + buf_data, "%s%s\rE -H %s %s,$\r", register_ind_echo, panel->regs[i].name, reg_dev, panel->regs[i].name);
sprintf (*buf + buf_data, "%s%s;E -16 %s %s,$;", register_ind_echo, panel->regs[i].name, reg_dev, panel->regs[i].name);
buf_data += strlen (*buf + buf_data);
}
if (bit_reg_count) {
strcpy (*buf + buf_data, register_get_postfix);
buf_data += strlen (*buf + buf_data);
strcpy (*buf + buf_data, "\r");
strcpy (*buf + buf_data, ";");
buf_data += strlen (*buf + buf_data);
}
strcpy (*buf + buf_data, register_get_end);
@ -855,6 +856,7 @@ if (!simulator_panel) {
if (CreateProcessA(NULL, cmd, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &StartupInfo, &ProcessInfo)) {
CloseHandle (ProcessInfo.hThread);
p->hProcess = ProcessInfo.hProcess;
p->dwProcessId = ProcessInfo.dwProcessId;
}
else { /* Creation Problem */
sim_panel_set_error (NULL, "CreateProcess Error: %d", GetLastError());
@ -1074,6 +1076,8 @@ if (panel) {
pthread_cond_destroy (&panel->io_done);
#if defined(_WIN32)
if (panel->hProcess) {
GenerateConsoleCtrlEvent (CTRL_BREAK_EVENT, panel->dwProcessId);
msleep (200);
TerminateProcess (panel->hProcess, 0);
WaitForSingleObject (panel->hProcess, INFINITE);
CloseHandle (panel->hProcess);
@ -1106,6 +1110,7 @@ if (panel) {
free (panel->regs);
free (panel->reg_query);
free (panel->io_response);
free (panel->halt_reason);
free (panel->simulator_version);
if ((panel->Debug) && (!panel->parent))
fclose (panel->Debug);
@ -1353,12 +1358,9 @@ if (panel->reg_query_size != _panel_send (panel, panel->reg_query, panel->reg_qu
pthread_mutex_unlock (&panel->io_command_lock);
return -1;
}
while (panel->io_reg_query_pending != 0) {
pthread_mutex_unlock (&panel->io_lock);
msleep (100);
pthread_mutex_lock (&panel->io_lock);
}
++panel->io_reg_query_pending;
if (panel->io_response_data)
_panel_debug (panel, DBG_RCV, "Receive Data Discarded: ", panel->io_response, panel->io_response_data);
panel->io_response_data = 0;
panel->io_waiting = 1;
while (panel->io_waiting)
pthread_cond_wait (&panel->io_done, &panel->io_lock);
@ -1372,7 +1374,7 @@ return 0;
int
sim_panel_get_registers (PANEL *panel, unsigned long long *simulation_time)
{
return _panel_get_registers (panel, 0, simulation_time);
return _panel_get_registers (panel, (panel->State == Halt), simulation_time);
}
int
@ -1472,6 +1474,15 @@ if (panel->State == Run) {
return 0;
}
const char *
sim_panel_halt_text (PANEL *panel)
{
if (!panel || !panel->halt_reason)
return "";
return panel->halt_reason;
}
int
sim_panel_exec_boot (PANEL *panel, const char *device)
{
@ -1538,7 +1549,7 @@ if ((simtime = strstr (response, "Time:"))) {
}
free (response);
panel->simulation_time_base += panel->simulation_time;
if (_panel_sendf_completion (panel, NULL, "Simulator Running...", "RUN -Q\r", 5)) {
if (_panel_sendf_completion (panel, NULL, "Simulator Running...", "RUN\r", 5)) {
_panel_debug (panel, DBG_THR, "Unable to start simulator: %s", NULL, 0, sim_panel_get_error());
return -1;
}
@ -1768,6 +1779,40 @@ free (response);
return 0;
}
int
sim_panel_device_debug_mode (PANEL *panel,
const char *device,
int set_unset,
const char *mode_bits)
{
char *response = NULL;
int cmd_stat;
if (!panel || (panel->State == Error)) {
sim_panel_set_error (NULL, "Invalid Panel");
return -1;
}
if (_panel_sendf (panel, &cmd_stat, &response, "SHOW %s", device) ||
(cmd_stat)) {
sim_panel_set_error (NULL, "Can't %s Debug Mode: '%s' on Device '%s': %s",
set_unset ? "Enable" : "Disable", mode_bits ? mode_bits : "", device, response);
free (response);
return -1;
}
free (response);
response = NULL;
if (_panel_sendf (panel, &cmd_stat, &response, "%sDEBUG %s %s",
set_unset ? "" : "NO", device, mode_bits ? mode_bits : "") ||
(cmd_stat)) {
sim_panel_set_error (NULL, "Can't %s Debug Mode: '%s' on Device '%s': %s",
set_unset ? "Enable" : "Disable", mode_bits ? mode_bits : "", device, response);
free (response);
return -1;
}
free (response);
return 0;
}
/**
sim_panel_gen_deposit
@ -2069,7 +2114,7 @@ struct sched_param sched_priority;
char buf[4096];
int buf_data = 0;
int processing_register_output = 0;
int io_wait_done;
int io_wait_done = 0;
/*
Boost Priority for this response processing thread to quickly digest
@ -2119,6 +2164,7 @@ while ((p->sock != INVALID_SOCKET) &&
int new_data;
char *s, *e, *eol;
if (NULL == strchr (buf, '\n')) {
pthread_mutex_unlock (&p->io_lock);
new_data = sim_read_sock (p->sock, &buf[buf_data], sizeof(buf)-(buf_data+1));
pthread_mutex_lock (&p->io_lock);
@ -2131,6 +2177,7 @@ while ((p->sock != INVALID_SOCKET) &&
_panel_debug (p, DBG_RCV, "Received %d bytes: ", &buf[buf_data], new_data, new_data);
buf_data += new_data;
buf[buf_data] = '\0';
}
s = buf;
while ((eol = strchr (s, '\n'))) {
/* Line to process */
@ -2258,42 +2305,43 @@ while ((p->sock != INVALID_SOCKET) &&
/* Unexpected Register Data Found (or other output containing a : character) */
}
}
if (!strcmp (s + strlen (sim_prompt), register_repeat_end)) {
_panel_debug (p, DBG_RCV, "*Repeat Block Complete", NULL, 0);
if ((strlen (s) > strlen (sim_prompt)) && (!strcmp (s + strlen (sim_prompt), register_repeat_end))) {
_panel_debug (p, DBG_RCV, "*Repeat Block Complete (Accumulated Data = %d)", NULL, 0, (int)p->io_response_data);
if (p->callback) {
pthread_mutex_unlock (&p->io_lock);
p->callback (p, p->simulation_time_base + p->simulation_time, p->callback_context);
pthread_mutex_lock (&p->io_lock);
}
processing_register_output = 0;
p->io_response_data = 0;
p->io_response[p->io_response_data] = '\0';
goto Start_Next_Line;
}
if ((!strcmp (s + strlen (sim_prompt), register_repeat_start)) ||
(!strcmp (s + strlen (sim_prompt), register_get_start))) {
if ((strlen (s) > strlen (sim_prompt)) &&
((!strcmp (s + strlen (sim_prompt), register_repeat_start)) ||
(!strcmp (s + strlen (sim_prompt), register_get_start)))) {
_panel_debug (p, DBG_RCV, "*Repeat/Register Block Starting", NULL, 0);
processing_register_output = 1;
goto Start_Next_Line;
}
if (!strcmp (s + strlen (sim_prompt), register_get_end)) {
if ((strlen (s) > strlen (sim_prompt)) &&
(!strcmp (s + strlen (sim_prompt), register_get_end))) {
_panel_debug (p, DBG_RCV, "*Register Block Complete", NULL, 0);
--p->io_reg_query_pending;
p->io_waiting = 0;
processing_register_output = 0;
pthread_cond_signal (&p->io_done);
goto Start_Next_Line;
}
if (!strcmp (s + strlen (sim_prompt), command_done_echo)) {
if ((strlen (s) > strlen (sim_prompt)) && (!strcmp (s + strlen (sim_prompt), command_done_echo))) {
_panel_debug (p, DBG_RCV, "*Received Command Complete", NULL, 0);
p->io_waiting = 0;
pthread_cond_signal (&p->io_done);
goto Start_Next_Line;
}
/* Non Register Data Found (echo of EXAMINE or other commands and/or command output) */
if (p->io_waiting) {
char *t;
if (p->io_response_data + strlen (s) + 3 > p->io_response_size) {
t = (char *)_panel_malloc (p->io_response_data + strlen (s) + 3);
char *t = (char *)_panel_malloc (p->io_response_data + strlen (s) + 3);
if (t == NULL) {
_panel_debug (p, DBG_RCV, "%s", NULL, 0, sim_panel_get_error());
p->State = Error;
@ -2313,17 +2361,16 @@ while ((p->sock != INVALID_SOCKET) &&
(p->completion_string) &&
(!memcmp (s, p->completion_string, strlen (p->completion_string)))) {
_panel_debug (p, DBG_RCV, "Match with potentially coalesced additional data: '%s'", NULL, 0, p->completion_string);
if (eol < &buf[buf_data])
memset (s + strlen (s), ' ', eol - (s + strlen (s)));
break;
}
}
else
_panel_debug (p, DBG_RCV, "Receive Data Discarded: '%s'", NULL, 0, s);
Start_Next_Line:
s = eol;
while (isspace(0xFF & (*s)))
++s;
}
memmove (buf, s, strlen (s) + 1);
memmove (buf, s, buf_data - (s - buf) + 1);
buf_data = strlen (buf);
if (buf_data)
_panel_debug (p, DBG_RSP, "Remnant Buffer Contents: '%s'", NULL, 0, buf);
@ -2337,11 +2384,12 @@ Start_Next_Line:
_panel_debug (p, DBG_RSP, "State transitioning to Run", NULL, 0);
p->State = Run;
buf_data -= 20;
buf[buf_data] = '\0';
if (buf_data) {
memmove (buf, buf + 20, strlen (buf + 20) + 1);
buf_data = strlen (buf);
memmove (buf, buf + 20, buf_data + 1);
_panel_debug (p, DBG_RSP, "Remnant Buffer Contents: '%s'", NULL, 0, buf);
}
else
buf[buf_data] = '\0';
if (io_wait_done) { /* someone waiting for this? */
_panel_debug (p, DBG_RCV, "*Match Command Complete - Match signaling waiting thread", NULL, 0);
io_wait_done = 0;
@ -2355,10 +2403,17 @@ Start_Next_Line:
pthread_mutex_lock (&p->io_lock);
}
}
}
if ((p->State == Run) && (!strcmp (buf, sim_prompt))) {
_panel_debug (p, DBG_RSP, "State transitioning to Halt", NULL, 0);
_panel_debug (p, DBG_RSP, "State transitioning to Halt: io_wait_done: %d", NULL, 0, io_wait_done);
p->State = Halt;
free (p->halt_reason);
p->halt_reason = (char *)_panel_malloc (1 + strlen (p->io_response));
if (p->halt_reason == NULL) {
_panel_debug (p, DBG_RCV, "%s", NULL, 0, sim_panel_get_error());
p->State = Error;
break;
}
strcpy (p->halt_reason, p->io_response);
}
if (io_wait_done) {
_panel_debug (p, DBG_RCV, "*Match Command Complete - Match signaling waiting thread", NULL, 0);
@ -2426,28 +2481,30 @@ while ((p->sock != INVALID_SOCKET) &&
msleep (500);
pthread_mutex_lock (&p->io_lock);
if (new_register) {
if (p->io_reg_query_pending == 0) {
size_t repeat_data = strlen (register_repeat_prefix) + /* prefix */
20 + /* max int width */
strlen (register_repeat_units) + /* units and spacing */
buf_data + /* command contents */
1 + /* carriage return */
1 + /* ; */
strlen (register_repeat_start) + /* auto repeat begin */
1 + /* carriage return */
1 + /* ; */
strlen (register_repeat_end) + /* auto repeat completion */
1 + /* carriage return */
1; /* NUL */
char *repeat = (char *)malloc (repeat_data);
char *c;
c = strstr (buf, register_get_start); /* remove register_get_start string and anything before it */
if (c) { /* always true */
buf_data -= (c - buf) + strlen (register_get_start);
c += strlen (register_get_start);
}
sprintf (repeat, "%s%d%s%s%*.*s", register_repeat_prefix,
p->usecs_between_callbacks,
register_repeat_units,
register_repeat_start,
(int)buf_data, (int)buf_data, buf);
(int)buf_data, (int)buf_data, c);
pthread_mutex_unlock (&p->io_lock);
for (c = strchr (repeat, '\r'); c != NULL; c = strchr (c, '\r'))
*c = ';'; /* replace carriage returns with semicolons */
c = strstr (repeat, register_get_end); /* remove register_done_echo string and */
if (c) /* always true */
strcpy (c, register_repeat_end); /* replace it with the register_repeat_end string */
@ -2459,11 +2516,6 @@ while ((p->sock != INVALID_SOCKET) &&
pthread_mutex_lock (&p->io_lock);
free (repeat);
}
else { /* already busy */
p->new_register = 1; /* retry later */
_panel_debug (p, DBG_XMT, "Waiting on prior command completion before specifying repeat interval", NULL, 0);
}
}
/* when halted, we directly poll the halted system to get updated */
/* register state which may have changed due to panel activities */
if (p->State == Halt) {
@ -2545,7 +2597,7 @@ while (1) { /* format passed string, arg
if (len >= (int)(sim_panel_error_bufsize-1)) {
free (sim_panel_error_buf);
sim_panel_error_bufsize = sim_panel_error_bufsize * 2;
while ((int)sim_panel_error_bufsize < len + 1)
while ((int)sim_panel_error_bufsize < len + 2)
sim_panel_error_bufsize = sim_panel_error_bufsize * 2;
sim_panel_error_buf = (char *) malloc (sim_panel_error_bufsize);
if (sim_panel_error_buf == NULL) {
@ -2610,6 +2662,8 @@ if (completion_status || completion_string) {
}
pthread_mutex_lock (&p->io_lock);
p->completion_string = completion_string;
if (p->io_response_data)
_panel_debug (p, DBG_RCV, "Receive Data Discarded: ", p->io_response, p->io_response_data);
p->io_response_data = 0;
p->io_waiting = 1;
}

View file

@ -56,7 +56,7 @@ extern "C" {
#if !defined(__VAX) /* Unsupported platform */
#define SIM_FRONTPANEL_VERSION 11
#define SIM_FRONTPANEL_VERSION 12
/**
@ -284,6 +284,19 @@ sim_panel_exec_run (PANEL *panel);
int
sim_panel_exec_step (PANEL *panel);
/**
A simulator often displays some useful information as it stops
executing instructions.
sim_panel_halt_text - Returns the simulator output immediately prior
to the most recent transition to the Halt state.
*/
const char *
sim_panel_halt_text (PANEL *panel);
/**
When a front panel application wants to describe conditions that
@ -472,6 +485,34 @@ sim_panel_get_history (PANEL *panel,
char *buffer);
/**
A front panel application might want some details of simulator
and/or device behavior that is provided by a particular simulator
via debug information. Debugging for particular device(s)
and/or simulator debug settings can be controlled via the
sim_panel_device_debug_mode API.
*/
/**
sim_panel_device_debug_mode
device the device whose debug mode is to change
set_untset 1 to set debug flags, 0 to clear debug flags
mode_bits character string with different debug mode bits
to enable or disable. An empty string will
enable or disable all mode bits for the specified
device
*/
int
sim_panel_device_debug_mode (PANEL *panel,
const char *device,
int set_unset,
const char *mode_bits);
/**
When a front panel application needs to change the media