2000-04-27 22:03:57 +02:00
|
|
|
/**********
|
|
|
|
|
Copyright 1990 Regents of the University of California. All rights reserved.
|
|
|
|
|
Author: 1985 Wayne A. Christopher, U. C. Berkeley CAD Group
|
|
|
|
|
**********/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Code to deal with breakpoints and tracing.
|
|
|
|
|
*/
|
|
|
|
|
|
2011-12-11 19:05:00 +01:00
|
|
|
#include "ngspice/ngspice.h"
|
|
|
|
|
#include "ngspice/cpdefs.h"
|
|
|
|
|
#include "ngspice/ftedefs.h"
|
2025-03-29 20:08:32 +01:00
|
|
|
#include "ngspice/cktdefs.h"
|
2011-12-11 19:05:00 +01:00
|
|
|
#include "ngspice/dvec.h"
|
|
|
|
|
#include "ngspice/ftedebug.h"
|
2000-04-27 22:03:57 +02:00
|
|
|
#include "breakp.h"
|
2005-05-30 22:28:29 +02:00
|
|
|
#include "breakp2.h"
|
2010-10-16 19:09:46 +02:00
|
|
|
#include "runcoms2.h"
|
2024-11-30 13:37:36 +01:00
|
|
|
#include "com_plot.h"
|
2000-04-27 22:03:57 +02:00
|
|
|
|
2000-05-06 16:12:51 +02:00
|
|
|
#include "completion.h"
|
2000-04-27 22:03:57 +02:00
|
|
|
|
2012-09-20 20:30:53 +02:00
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
static bool satisfied(struct dbcomm *d, struct plot *plot);
|
|
|
|
|
static void printcond(struct dbcomm *d, FILE *fp);
|
|
|
|
|
|
|
|
|
|
static int howmanysteps = 0;
|
|
|
|
|
static int steps = 0;
|
2021-09-01 12:26:49 +02:00
|
|
|
static bool interpolated = FALSE;
|
2000-04-27 22:03:57 +02:00
|
|
|
|
2012-09-20 20:30:53 +02:00
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
/* Set a breakpoint. Possible commands are:
|
|
|
|
|
* stop after n
|
|
|
|
|
* stop when var cond val
|
|
|
|
|
*
|
|
|
|
|
* If more than one is given on a command line, then this is a conjunction.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
com_stop(wordlist *wl)
|
|
|
|
|
{
|
2019-06-15 00:34:11 +02:00
|
|
|
/* Check for an active circuit */
|
|
|
|
|
if (ft_curckt == (struct circ *) NULL) {
|
|
|
|
|
fprintf(cp_err, "No circuit loaded. Stopping is not possible.\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-01 12:26:49 +02:00
|
|
|
/* Check to see if we have to consider interpolated data. */
|
|
|
|
|
if (cp_getvar("interp", CP_BOOL, NULL, 0)) {
|
|
|
|
|
interpolated = TRUE;
|
|
|
|
|
fprintf(cp_out, "Note: Stop condition has to fit the interpolated time data!\n\n");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
interpolated = FALSE;
|
|
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
struct dbcomm *thisone = NULL;
|
|
|
|
|
struct dbcomm *d = NULL;
|
|
|
|
|
char *s, buf[64];
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
while (wl) {
|
2012-09-20 20:30:53 +02:00
|
|
|
if (thisone == NULL) {
|
2016-03-12 12:20:16 +01:00
|
|
|
thisone = d = TMALLOC(struct dbcomm, 1);
|
2012-09-20 20:30:53 +02:00
|
|
|
} else {
|
2016-03-12 12:20:16 +01:00
|
|
|
d->db_also = TMALLOC(struct dbcomm, 1);
|
2000-04-27 22:03:57 +02:00
|
|
|
d = d->db_also;
|
|
|
|
|
}
|
2023-08-08 12:56:13 +02:00
|
|
|
d->db_also = NULL;
|
2000-04-27 22:03:57 +02:00
|
|
|
|
|
|
|
|
/* Figure out what the first condition is. */
|
2012-09-20 20:30:53 +02:00
|
|
|
d->db_analysis = NULL;
|
2000-04-27 22:03:57 +02:00
|
|
|
if (eq(wl->wl_word, "after") && wl->wl_next) {
|
|
|
|
|
d->db_type = DB_STOPAFTER;
|
|
|
|
|
d->db_number = debugnumber;
|
2012-09-20 20:30:53 +02:00
|
|
|
if (!wl->wl_next->wl_word) {
|
|
|
|
|
i = 0;
|
|
|
|
|
} else {
|
2000-04-27 22:03:57 +02:00
|
|
|
#ifdef HAVE_CTYPE_H
|
2012-09-20 20:30:53 +02:00
|
|
|
for (s = wl->wl_next->wl_word, i = 0; *s; s++)
|
2016-03-08 21:20:11 +01:00
|
|
|
if (!isdigit_c(*s))
|
2012-09-20 20:30:53 +02:00
|
|
|
goto bad;
|
|
|
|
|
else
|
|
|
|
|
i = i * 10 + (*s - '0');
|
2000-04-27 22:03:57 +02:00
|
|
|
#else
|
2012-09-20 20:30:53 +02:00
|
|
|
i = atoi(wl->wl_next->wl_word); /* etoi ??? */
|
2000-04-27 22:03:57 +02:00
|
|
|
#endif
|
2012-09-20 20:30:53 +02:00
|
|
|
}
|
2000-04-27 22:03:57 +02:00
|
|
|
d->db_iteration = i;
|
|
|
|
|
wl = wl->wl_next->wl_next;
|
2010-12-30 20:32:24 +01:00
|
|
|
} else if (eq(wl->wl_word, "when") && wl->wl_next) {
|
2012-09-20 20:30:53 +02:00
|
|
|
/* cp_lexer(string) will not discriminate '=', so we have
|
2010-12-30 20:32:24 +01:00
|
|
|
to do it here */
|
2016-11-18 18:39:42 +01:00
|
|
|
if (strchr(wl->wl_next->wl_word, '=') &&
|
2019-12-06 22:04:45 +01:00
|
|
|
(!(wl->wl_next->wl_next) ||
|
|
|
|
|
strstr(wl->wl_next->wl_next->wl_word, "when") ||
|
|
|
|
|
strstr(wl->wl_next->wl_next->wl_word, "after"))) {
|
2010-12-30 20:32:24 +01:00
|
|
|
/* we have vec=val in a single word */
|
2012-09-20 20:30:53 +02:00
|
|
|
wordlist *wln;
|
|
|
|
|
char **charr = TMALLOC(char*, 4);
|
2010-12-30 20:32:24 +01:00
|
|
|
char *tok = copy(wl->wl_next->wl_word);
|
2016-11-18 18:39:42 +01:00
|
|
|
char *tokeq = strchr(tok, '=');
|
2012-09-20 20:30:53 +02:00
|
|
|
char *tokafter = copy(tokeq + 1);
|
2010-12-30 20:32:24 +01:00
|
|
|
*tokeq = '\0';
|
|
|
|
|
charr[0] = tok;
|
|
|
|
|
charr[1] = copy("eq");
|
|
|
|
|
charr[2] = tokafter;
|
|
|
|
|
charr[3] = NULL;
|
2019-11-30 02:12:30 +01:00
|
|
|
wln = wl_build((const char * const *) charr);
|
2010-12-31 10:08:54 +01:00
|
|
|
wl_splice(wl->wl_next, wln);
|
2010-12-30 20:32:24 +01:00
|
|
|
}
|
2019-12-06 22:04:45 +01:00
|
|
|
|
2011-01-01 15:38:47 +01:00
|
|
|
/* continue with parsing the enhanced wordlist */
|
2012-09-20 20:30:53 +02:00
|
|
|
if (wl->wl_next->wl_next && wl->wl_next->wl_next->wl_next) {
|
2010-12-30 20:32:24 +01:00
|
|
|
wl = wl->wl_next;
|
|
|
|
|
d->db_number = debugnumber;
|
|
|
|
|
d->db_type = DB_STOPWHEN;
|
|
|
|
|
s = wl->wl_word;
|
2019-12-06 22:04:45 +01:00
|
|
|
|
|
|
|
|
{
|
|
|
|
|
double val;
|
|
|
|
|
if (ft_numparse(&s, FALSE, &val) >= 0) {
|
|
|
|
|
d->db_value1 = val;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
d->db_nodename1 = copy(wl->wl_word);
|
|
|
|
|
}
|
|
|
|
|
}
|
2010-12-30 20:32:24 +01:00
|
|
|
wl = wl->wl_next;
|
|
|
|
|
|
|
|
|
|
/* Now get the condition */
|
|
|
|
|
if (eq(wl->wl_word, "eq") || eq(wl->wl_word, "="))
|
|
|
|
|
d->db_op = DBC_EQU;
|
2023-08-08 12:56:13 +02:00
|
|
|
else if (eq(wl->wl_word, "ne"))
|
2010-12-30 20:32:24 +01:00
|
|
|
d->db_op = DBC_NEQ;
|
|
|
|
|
else if (eq(wl->wl_word, "gt") || eq(wl->wl_word, ">"))
|
|
|
|
|
d->db_op = DBC_GT;
|
2023-08-08 12:56:13 +02:00
|
|
|
else if (eq(wl->wl_word, "lt"))
|
2010-12-30 20:32:24 +01:00
|
|
|
d->db_op = DBC_LT;
|
2023-08-08 12:56:13 +02:00
|
|
|
else if (eq(wl->wl_word, "<")) {
|
|
|
|
|
/* "<>" is parsed as two words. */
|
|
|
|
|
|
|
|
|
|
if (eq(wl->wl_next->wl_word, ">")) {
|
|
|
|
|
if (!wl->wl_next->wl_next)
|
|
|
|
|
goto bad;
|
|
|
|
|
d->db_op = DBC_NEQ;
|
|
|
|
|
wl = wl->wl_next;
|
|
|
|
|
} else {
|
|
|
|
|
d->db_op = DBC_LT;
|
|
|
|
|
}
|
|
|
|
|
} else if (eq(wl->wl_word, "ge") || eq(wl->wl_word, ">="))
|
2010-12-30 20:32:24 +01:00
|
|
|
d->db_op = DBC_GTE;
|
|
|
|
|
else if (eq(wl->wl_word, "le") || eq(wl->wl_word, "<="))
|
|
|
|
|
d->db_op = DBC_LTE;
|
|
|
|
|
else
|
|
|
|
|
goto bad;
|
|
|
|
|
wl = wl->wl_next;
|
|
|
|
|
|
|
|
|
|
/* Now see about the second one. */
|
|
|
|
|
s = wl->wl_word;
|
2019-12-06 22:04:45 +01:00
|
|
|
|
|
|
|
|
{
|
|
|
|
|
double val;
|
|
|
|
|
if (ft_numparse(&s, FALSE, &val) >= 0) {
|
|
|
|
|
d->db_value2 = val;
|
|
|
|
|
}
|
|
|
|
|
else {
|
2023-08-08 12:56:13 +02:00
|
|
|
d->db_nodename2 = copy(wl->wl_word);
|
2019-12-06 22:04:45 +01:00
|
|
|
}
|
|
|
|
|
}
|
2010-12-30 20:32:24 +01:00
|
|
|
wl = wl->wl_next;
|
2023-08-08 12:56:13 +02:00
|
|
|
} else { // Neither "after" nor "when".
|
2010-12-30 20:32:24 +01:00
|
|
|
goto bad;
|
2012-09-20 20:30:53 +02:00
|
|
|
}
|
2023-08-08 12:56:13 +02:00
|
|
|
} else {
|
|
|
|
|
goto bad;
|
|
|
|
|
}
|
2019-12-06 22:04:45 +01:00
|
|
|
} /* end of loop over wordlist */
|
2012-09-20 20:30:53 +02:00
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
if (thisone) {
|
|
|
|
|
if (dbs) {
|
2010-07-24 20:51:06 +02:00
|
|
|
for (d = dbs; d->db_next; d = d->db_next)
|
|
|
|
|
;
|
2000-04-27 22:03:57 +02:00
|
|
|
d->db_next = thisone;
|
2012-09-20 20:30:53 +02:00
|
|
|
} else {
|
2013-03-05 20:03:25 +01:00
|
|
|
ft_curckt->ci_dbs = dbs = thisone;
|
2012-09-20 20:30:53 +02:00
|
|
|
}
|
2000-04-27 22:03:57 +02:00
|
|
|
(void) sprintf(buf, "%d", debugnumber);
|
|
|
|
|
cp_addkword(CT_DBNUMS, buf);
|
|
|
|
|
debugnumber++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
2012-09-20 20:30:53 +02:00
|
|
|
bad:
|
|
|
|
|
fprintf(cp_err, "Syntax error parsing breakpoint specification.\n");
|
2023-08-08 12:56:13 +02:00
|
|
|
while (thisone) {
|
|
|
|
|
d = thisone->db_also;
|
|
|
|
|
txfree(thisone);
|
|
|
|
|
thisone = d;
|
|
|
|
|
}
|
|
|
|
|
} /* end of function com_stop */
|
2000-04-27 22:03:57 +02:00
|
|
|
|
2012-09-20 20:30:53 +02:00
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
|
2019-12-06 22:04:45 +01:00
|
|
|
/* Trace a node (have wrd_point print it). Usage is "trace node ..."*/
|
2000-04-27 22:03:57 +02:00
|
|
|
void
|
|
|
|
|
com_trce(wordlist *wl)
|
|
|
|
|
{
|
2015-05-02 10:24:49 +02:00
|
|
|
settrace(wl, VF_PRINT, NULL);
|
2000-04-27 22:03:57 +02:00
|
|
|
}
|
|
|
|
|
|
2012-09-20 20:30:53 +02:00
|
|
|
|
2025-03-29 20:08:32 +01:00
|
|
|
/* Incrementally plot values. This is just like trace.
|
|
|
|
|
* Nodes may be specified with an offset, as name+number, as that is useful
|
|
|
|
|
* for separating the graphs of digital nodes. It will be ignored for analogue.
|
|
|
|
|
*/
|
2000-04-27 22:03:57 +02:00
|
|
|
|
|
|
|
|
void
|
|
|
|
|
com_iplot(wordlist *wl)
|
|
|
|
|
{
|
2024-11-30 13:37:36 +01:00
|
|
|
if (check_batch("iplot"))
|
|
|
|
|
return;
|
|
|
|
|
|
2019-06-15 00:39:56 +02:00
|
|
|
/* Check for an active circuit */
|
|
|
|
|
if (ft_curckt == (struct circ *) NULL) {
|
|
|
|
|
fprintf(cp_err, "No circuit loaded. "
|
|
|
|
|
"Incremental plotting is not possible.\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
struct dbcomm *d, *td, *currentdb = NULL;
|
2023-06-20 11:53:21 +02:00
|
|
|
double window = 0.0;
|
2025-03-29 20:08:32 +01:00
|
|
|
#ifdef XSPICE
|
|
|
|
|
int event_auto_incr = 0;
|
|
|
|
|
#endif
|
2023-05-02 14:13:02 +02:00
|
|
|
char *s;
|
2025-03-29 20:08:32 +01:00
|
|
|
int initial_steps = IPOINTMIN;
|
2023-05-02 14:13:02 +02:00
|
|
|
|
2023-06-20 11:53:21 +02:00
|
|
|
/* Look for "-w window-size" at the front, indicating a windowed iplot
|
|
|
|
|
* or "-d steps" to set the initial delay before the window appears.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
while (wl && wl->wl_word[0] == '-') {
|
|
|
|
|
if (wl->wl_word[1] == 'w' && !wl->wl_word[2]) {
|
|
|
|
|
wl = wl->wl_next;
|
|
|
|
|
if (wl) {
|
|
|
|
|
char *cp;
|
|
|
|
|
int error;
|
|
|
|
|
|
|
|
|
|
cp = wl->wl_word;
|
|
|
|
|
window = INPevaluate(&cp, &error, 0);
|
|
|
|
|
if (error || window <= 0) {
|
|
|
|
|
fprintf(cp_err,
|
|
|
|
|
"Incremental plot width must be positive.\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (wl->wl_word[1] == 'd' && !wl->wl_word[2]) {
|
|
|
|
|
wl = wl->wl_next;
|
|
|
|
|
if (wl)
|
|
|
|
|
initial_steps = atoi(wl->wl_word);
|
2025-03-29 20:08:32 +01:00
|
|
|
#ifdef XSPICE
|
|
|
|
|
} else if (wl->wl_word[1] == 'o' && !wl->wl_word[2]) {
|
|
|
|
|
/* Automatically offset traces for event nodes. */
|
|
|
|
|
|
|
|
|
|
event_auto_incr = 1;
|
|
|
|
|
#endif
|
2023-06-20 11:53:21 +02:00
|
|
|
} else {
|
|
|
|
|
break;
|
2023-05-02 14:13:02 +02:00
|
|
|
}
|
|
|
|
|
wl = wl->wl_next;
|
|
|
|
|
}
|
2000-04-27 22:03:57 +02:00
|
|
|
|
|
|
|
|
/* We use a modified ad-hoc algorithm here where db_also denotes
|
2012-09-20 20:30:53 +02:00
|
|
|
vectors on the same command line and db_next denotes
|
|
|
|
|
separate iplot commands. */
|
2023-06-20 11:53:21 +02:00
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
while (wl) {
|
|
|
|
|
s = cp_unquote(wl->wl_word);
|
2016-03-12 12:20:16 +01:00
|
|
|
d = TMALLOC(struct dbcomm, 1);
|
2012-09-20 20:30:53 +02:00
|
|
|
d->db_analysis = NULL;
|
2000-04-27 22:03:57 +02:00
|
|
|
d->db_number = debugnumber++;
|
2025-03-29 20:08:32 +01:00
|
|
|
d->db_iteration = event_auto_incr ? DB_AUTO_OFFSET : DB_NORMAL;
|
|
|
|
|
d->db_op = initial_steps; // Field re-use
|
|
|
|
|
d->db_value1 = window; // Field re-use
|
|
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
if (eq(s, "all")) {
|
|
|
|
|
d->db_type = DB_IPLOTALL;
|
|
|
|
|
} else {
|
|
|
|
|
d->db_type = DB_IPLOT;
|
|
|
|
|
d->db_nodename1 = copy(s);
|
|
|
|
|
}
|
2000-10-26 19:02:12 +02:00
|
|
|
tfree(s);/*DG: avoid memory leak */
|
2025-03-29 20:08:32 +01:00
|
|
|
|
|
|
|
|
/* Chain in expected order. */
|
|
|
|
|
|
|
|
|
|
if (currentdb)
|
|
|
|
|
td->db_also = d;
|
|
|
|
|
else
|
|
|
|
|
currentdb = d;
|
|
|
|
|
td = d;
|
2000-04-27 22:03:57 +02:00
|
|
|
wl = wl->wl_next;
|
|
|
|
|
}
|
2012-09-20 20:30:53 +02:00
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
if (dbs) {
|
|
|
|
|
for (td = dbs; td->db_next; td = td->db_next)
|
|
|
|
|
;
|
|
|
|
|
td->db_next = currentdb;
|
2012-09-20 20:30:53 +02:00
|
|
|
} else {
|
2013-03-05 20:03:25 +01:00
|
|
|
ft_curckt->ci_dbs = dbs = currentdb;
|
2012-09-20 20:30:53 +02:00
|
|
|
}
|
2000-04-27 22:03:57 +02:00
|
|
|
}
|
|
|
|
|
|
2012-09-20 20:30:53 +02:00
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
/* Step a number of iterations. */
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
com_step(wordlist *wl)
|
|
|
|
|
{
|
|
|
|
|
if (wl)
|
|
|
|
|
steps = howmanysteps = atoi(wl->wl_word);
|
|
|
|
|
else
|
|
|
|
|
steps = howmanysteps = 1;
|
2012-09-20 20:30:53 +02:00
|
|
|
|
2010-06-23 19:40:56 +02:00
|
|
|
com_resume(NULL);
|
2000-04-27 22:03:57 +02:00
|
|
|
}
|
|
|
|
|
|
2012-09-20 20:30:53 +02:00
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
/* Print out the currently active breakpoints and traces. If we are printing
|
|
|
|
|
* to a file, assume that the file will be used for a later source and leave
|
|
|
|
|
* off the event numbers (with UNIX, that is). -- I don't like this.
|
|
|
|
|
*/
|
|
|
|
|
|
2013-03-05 20:03:25 +01:00
|
|
|
#undef isatty
|
2000-04-27 22:03:57 +02:00
|
|
|
#define isatty(xxxx) 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
com_sttus(wordlist *wl)
|
|
|
|
|
{
|
|
|
|
|
struct dbcomm *d, *dc;
|
|
|
|
|
|
2010-11-16 21:38:24 +01:00
|
|
|
NG_IGNORE(wl);
|
2010-11-16 20:11:32 +01:00
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
for (d = dbs; d; d = d->db_next) {
|
|
|
|
|
if (d->db_type == DB_TRACENODE) {
|
|
|
|
|
if (isatty(fileno(cp_out)))
|
2012-09-20 20:30:53 +02:00
|
|
|
fprintf(cp_out, "%-4d trace %s", d->db_number, d->db_nodename1);
|
2000-04-27 22:03:57 +02:00
|
|
|
else
|
|
|
|
|
fprintf(cp_out, "trace %s", d->db_nodename1);
|
|
|
|
|
} else if (d->db_type == DB_IPLOT) {
|
2012-09-20 20:30:53 +02:00
|
|
|
if (isatty(fileno(cp_out)))
|
|
|
|
|
fprintf(cp_out, "%-4d iplot %s", d->db_number, d->db_nodename1);
|
|
|
|
|
else
|
2000-04-27 22:03:57 +02:00
|
|
|
fprintf(cp_out, "iplot %s", d->db_nodename1);
|
2012-09-20 20:30:53 +02:00
|
|
|
for (dc = d->db_also; dc; dc = dc->db_also)
|
|
|
|
|
fprintf(cp_out, " %s", dc->db_nodename1);
|
2000-04-27 22:03:57 +02:00
|
|
|
} else if (d->db_type == DB_SAVE) {
|
|
|
|
|
if (isatty(fileno(cp_out)))
|
2012-09-20 20:30:53 +02:00
|
|
|
fprintf(cp_out, "%-4d save %s", d->db_number, d->db_nodename1);
|
2000-04-27 22:03:57 +02:00
|
|
|
else
|
|
|
|
|
fprintf(cp_out, "save %s", d->db_nodename1);
|
|
|
|
|
} else if (d->db_type == DB_TRACEALL) {
|
|
|
|
|
if (isatty(fileno(cp_out)))
|
|
|
|
|
fprintf(cp_out, "%-4d trace all", d->db_number);
|
|
|
|
|
else
|
|
|
|
|
fprintf(cp_out, "trace all");
|
|
|
|
|
} else if (d->db_type == DB_IPLOTALL) {
|
|
|
|
|
if (isatty(fileno(cp_out)))
|
|
|
|
|
fprintf(cp_out, "%-4d iplot all", d->db_number);
|
|
|
|
|
else
|
|
|
|
|
fprintf(cp_out, "iplot all");
|
|
|
|
|
} else if (d->db_type == DB_SAVEALL) {
|
|
|
|
|
if (isatty(fileno(cp_out)))
|
|
|
|
|
fprintf(cp_out, "%-4d save all", d->db_number);
|
|
|
|
|
else
|
|
|
|
|
fprintf(cp_out, "save all");
|
2012-09-20 20:30:53 +02:00
|
|
|
} else if ((d->db_type == DB_STOPAFTER) || (d->db_type == DB_STOPWHEN)) {
|
2000-04-27 22:03:57 +02:00
|
|
|
if (isatty(fileno(cp_out)))
|
|
|
|
|
fprintf(cp_out, "%-4d stop", d->db_number);
|
|
|
|
|
else
|
|
|
|
|
fprintf(cp_out, "stop");
|
|
|
|
|
printcond(d, cp_out);
|
2019-10-26 16:17:21 +02:00
|
|
|
} else if (d->db_type == DB_DEADIPLOT) {
|
2000-04-27 22:03:57 +02:00
|
|
|
if (isatty(fileno(cp_out))) {
|
|
|
|
|
fprintf(cp_out, "%-4d exiting iplot %s", d->db_number,
|
2012-09-20 20:30:53 +02:00
|
|
|
d->db_nodename1);
|
2000-04-27 22:03:57 +02:00
|
|
|
} else {
|
|
|
|
|
fprintf(cp_out, "exiting iplot %s", d->db_nodename1);
|
|
|
|
|
}
|
2012-09-20 20:30:53 +02:00
|
|
|
for (dc = d->db_also; dc; dc = dc->db_also)
|
|
|
|
|
fprintf(cp_out, " %s", dc->db_nodename1);
|
|
|
|
|
} else {
|
|
|
|
|
fprintf(cp_err,
|
|
|
|
|
"com_sttus: Internal Error: bad db %d\n", d->db_type);
|
|
|
|
|
}
|
2000-04-27 22:03:57 +02:00
|
|
|
(void) putc('\n', cp_out);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-20 20:30:53 +02:00
|
|
|
|
2013-08-20 19:46:06 +02:00
|
|
|
/* free the dbcomm structure which has been defined in
|
|
|
|
|
* function settrace() in breakp2.c
|
|
|
|
|
*/
|
|
|
|
|
|
2015-04-16 19:58:58 +02:00
|
|
|
void
|
2016-03-03 19:49:45 +01:00
|
|
|
dbfree1(struct dbcomm *d)
|
2015-04-16 19:58:58 +02:00
|
|
|
{
|
2016-03-03 19:49:45 +01:00
|
|
|
tfree(d->db_nodename1);
|
2025-03-29 20:08:32 +01:00
|
|
|
if (d->db_type != DB_IPLOT && d->db_type != DB_IPLOTALL &&
|
|
|
|
|
d->db_type != DB_DEADIPLOT) {
|
|
|
|
|
tfree(d->db_nodename2);
|
|
|
|
|
}
|
2016-03-03 19:49:45 +01:00
|
|
|
if (d->db_also)
|
|
|
|
|
dbfree(d->db_also);
|
|
|
|
|
tfree(d);
|
2015-04-16 19:58:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
void
|
2016-03-03 19:49:45 +01:00
|
|
|
dbfree(struct dbcomm *d)
|
2000-04-27 22:03:57 +02:00
|
|
|
{
|
2016-03-03 19:49:45 +01:00
|
|
|
while (d) {
|
|
|
|
|
struct dbcomm *next_d = d->db_next;
|
|
|
|
|
dbfree1(d);
|
|
|
|
|
d = next_d;
|
2000-04-27 22:03:57 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-20 20:30:53 +02:00
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
/* Delete breakpoints and traces. Usage is delete [number ...] */
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
com_delete(wordlist *wl)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
char *s, buf[64];
|
|
|
|
|
struct dbcomm *d, *dt;
|
|
|
|
|
|
|
|
|
|
if (wl && eq(wl->wl_word, "all")) {
|
2015-04-16 19:58:58 +02:00
|
|
|
dbfree(dbs);
|
2016-02-28 23:53:15 +01:00
|
|
|
dbs = NULL;
|
|
|
|
|
if (ft_curckt)
|
|
|
|
|
ft_curckt->ci_dbs = NULL;
|
2000-04-27 22:03:57 +02:00
|
|
|
return;
|
|
|
|
|
} else if (!wl) {
|
|
|
|
|
if (!dbs) {
|
|
|
|
|
fprintf(cp_err, "Error: no debugs in effect\n");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-09-20 20:30:53 +02:00
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
while (wl) {
|
2012-09-20 20:30:53 +02:00
|
|
|
|
|
|
|
|
if (wl->wl_word) {
|
2000-04-27 22:03:57 +02:00
|
|
|
#ifdef HAVE_CTYPE_H
|
2012-09-20 20:30:53 +02:00
|
|
|
for (s = wl->wl_word, i = 0; *s; s++)
|
2016-03-08 21:20:11 +01:00
|
|
|
if (!isdigit_c(*s)) {
|
2012-09-20 20:30:53 +02:00
|
|
|
fprintf(cp_err, "Error: %s isn't a number.\n",
|
|
|
|
|
wl->wl_word);
|
|
|
|
|
goto bad;
|
|
|
|
|
} else {
|
|
|
|
|
i = i * 10 + (*s - '0');
|
|
|
|
|
}
|
2000-04-27 22:03:57 +02:00
|
|
|
#else
|
2012-09-20 20:30:53 +02:00
|
|
|
i = atoi(wl->wl_next->wl_word); /* etoi ??? */
|
2000-04-27 22:03:57 +02:00
|
|
|
#endif
|
2012-09-20 20:30:53 +02:00
|
|
|
} else {
|
|
|
|
|
i = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
for (d = dbs, dt = NULL; d; d = d->db_next) {
|
|
|
|
|
if (d->db_number == i) {
|
|
|
|
|
if (dt)
|
|
|
|
|
dt->db_next = d->db_next;
|
|
|
|
|
else
|
2013-03-05 20:03:25 +01:00
|
|
|
ft_curckt->ci_dbs = dbs = d->db_next;
|
2015-04-16 19:58:58 +02:00
|
|
|
dbfree1(d);
|
2000-04-27 22:03:57 +02:00
|
|
|
(void) sprintf(buf, "%d", i);
|
|
|
|
|
cp_remkword(CT_DBNUMS, buf);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
dt = d;
|
|
|
|
|
}
|
2012-09-20 20:30:53 +02:00
|
|
|
|
|
|
|
|
bad:
|
|
|
|
|
wl = wl->wl_next;
|
2000-04-27 22:03:57 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-20 20:30:53 +02:00
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
/* Writedata calls this routine to see if it should keep going. If it
|
|
|
|
|
* returns TRUE, then the run should resume.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
ft_bpcheck(struct plot *runplot, int iteration)
|
|
|
|
|
{
|
|
|
|
|
struct dbcomm *d, *dt;
|
|
|
|
|
|
|
|
|
|
if ((howmanysteps > 0) && (--howmanysteps == 0)) {
|
|
|
|
|
if (steps > 1)
|
|
|
|
|
fprintf(cp_err, "Stopped after %d steps.\n", steps);
|
|
|
|
|
return (FALSE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check the debugs set. */
|
|
|
|
|
for (d = dbs; d; d = d->db_next) {
|
|
|
|
|
for (dt = d; dt; dt = dt->db_also) {
|
|
|
|
|
switch (dt->db_type) {
|
2012-09-20 20:30:53 +02:00
|
|
|
case DB_TRACENODE:
|
|
|
|
|
case DB_TRACEALL:
|
|
|
|
|
case DB_IPLOT:
|
|
|
|
|
case DB_DEADIPLOT:
|
|
|
|
|
case DB_IPLOTALL:
|
|
|
|
|
case DB_SAVE:
|
|
|
|
|
case DB_SAVEALL:
|
|
|
|
|
goto more;
|
|
|
|
|
case DB_STOPAFTER:
|
|
|
|
|
if (iteration == dt->db_iteration)
|
|
|
|
|
break;
|
|
|
|
|
else
|
2000-04-27 22:03:57 +02:00
|
|
|
goto more;
|
2012-09-20 20:30:53 +02:00
|
|
|
case DB_STOPWHEN:
|
|
|
|
|
/* See if the condition is TRUE. */
|
|
|
|
|
if (satisfied(dt, runplot))
|
|
|
|
|
break;
|
|
|
|
|
else
|
|
|
|
|
goto more;
|
|
|
|
|
default:
|
|
|
|
|
fprintf(cp_err, "ft_bpcheck: Internal Error: bad db %d\n",
|
|
|
|
|
dt->db_type);
|
2000-04-27 22:03:57 +02:00
|
|
|
}
|
|
|
|
|
}
|
2012-09-20 20:30:53 +02:00
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
if (dt == NULL) {
|
|
|
|
|
/* It made it */
|
2012-09-20 20:30:53 +02:00
|
|
|
fprintf(cp_err, "%-2d: condition met: stop ", d->db_number);
|
2000-04-27 22:03:57 +02:00
|
|
|
printcond(d, cp_err);
|
|
|
|
|
(void) putc('\n', cp_err);
|
|
|
|
|
return (FALSE);
|
|
|
|
|
}
|
2012-09-20 20:30:53 +02:00
|
|
|
|
|
|
|
|
more: /* Just continue */
|
|
|
|
|
;
|
2000-04-27 22:03:57 +02:00
|
|
|
}
|
2012-09-20 20:30:53 +02:00
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
return (TRUE);
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-20 20:30:53 +02:00
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
/* This is called to determine whether a STOPWHEN is TRUE. */
|
|
|
|
|
|
|
|
|
|
static bool
|
|
|
|
|
satisfied(struct dbcomm *d, struct plot *plot)
|
|
|
|
|
{
|
|
|
|
|
struct dvec *v1 = NULL, *v2 = NULL;
|
|
|
|
|
double d1, d2;
|
2021-09-01 12:26:49 +02:00
|
|
|
static double laststoptime = 0.;
|
2000-04-27 22:03:57 +02:00
|
|
|
|
|
|
|
|
if (d->db_nodename1) {
|
|
|
|
|
if ((v1 = vec_fromplot(d->db_nodename1, plot)) == NULL) {
|
2012-09-20 20:30:53 +02:00
|
|
|
fprintf(cp_err, "Error: %s: no such node\n", d->db_nodename1);
|
2000-04-27 22:03:57 +02:00
|
|
|
return (FALSE);
|
|
|
|
|
}
|
2021-09-01 10:57:27 +02:00
|
|
|
if (v1->v_length == 0)
|
|
|
|
|
return (FALSE);
|
|
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
if (isreal(v1))
|
|
|
|
|
d1 = v1->v_realdata[v1->v_length - 1];
|
|
|
|
|
else
|
2012-02-07 20:53:12 +01:00
|
|
|
d1 = realpart((v1->v_compdata[v1->v_length - 1]));
|
2021-09-01 10:57:27 +02:00
|
|
|
|
2012-09-20 20:30:53 +02:00
|
|
|
} else {
|
2000-04-27 22:03:57 +02:00
|
|
|
d1 = d->db_value1;
|
2012-09-20 20:30:53 +02:00
|
|
|
}
|
2000-04-27 22:03:57 +02:00
|
|
|
|
|
|
|
|
if (d->db_nodename2) {
|
|
|
|
|
if ((v2 = vec_fromplot(d->db_nodename2, plot)) == NULL) {
|
2012-09-20 20:30:53 +02:00
|
|
|
fprintf(cp_err, "Error: %s: no such node\n", d->db_nodename2);
|
2000-04-27 22:03:57 +02:00
|
|
|
return (FALSE);
|
|
|
|
|
}
|
|
|
|
|
if (isreal(v2))
|
|
|
|
|
d2 = v2->v_realdata[v2->v_length - 1];
|
|
|
|
|
else
|
2012-02-07 20:53:12 +01:00
|
|
|
d2 = realpart((v2->v_compdata[v2->v_length - 1]));
|
2021-09-01 12:26:49 +02:00
|
|
|
/* option interp: no new time step since last stop */
|
|
|
|
|
} else if (interpolated && AlmostEqualUlps(d1, laststoptime, 3)){
|
|
|
|
|
d2 = 0.;
|
2012-09-20 20:30:53 +02:00
|
|
|
} else {
|
2000-04-27 22:03:57 +02:00
|
|
|
d2 = d->db_value2;
|
2012-09-20 20:30:53 +02:00
|
|
|
}
|
2000-04-27 22:03:57 +02:00
|
|
|
|
|
|
|
|
switch (d->db_op) {
|
2012-09-20 20:30:53 +02:00
|
|
|
case DBC_EQU:
|
2021-09-01 12:26:49 +02:00
|
|
|
{
|
|
|
|
|
bool hit = AlmostEqualUlps(d1, d2, 3) ? TRUE : FALSE;
|
|
|
|
|
/* option interp: save the last stop time */
|
|
|
|
|
if (interpolated && hit)
|
|
|
|
|
laststoptime = d1;
|
|
|
|
|
return hit;
|
|
|
|
|
}
|
2012-09-20 20:30:53 +02:00
|
|
|
// return ((d1 == d2) ? TRUE : FALSE);
|
|
|
|
|
case DBC_NEQ:
|
|
|
|
|
return ((d1 != d2) ? TRUE : FALSE);
|
|
|
|
|
case DBC_GTE:
|
|
|
|
|
return ((d1 >= d2) ? TRUE : FALSE);
|
|
|
|
|
case DBC_LTE:
|
|
|
|
|
return ((d1 <= d2) ? TRUE : FALSE);
|
|
|
|
|
case DBC_GT:
|
|
|
|
|
return ((d1 > d2) ? TRUE : FALSE);
|
|
|
|
|
case DBC_LT:
|
|
|
|
|
return ((d1 < d2) ? TRUE : FALSE);
|
|
|
|
|
default:
|
|
|
|
|
fprintf(cp_err,
|
|
|
|
|
"satisfied: Internal Error: bad cond %d\n", d->db_op);
|
|
|
|
|
return (FALSE);
|
2000-04-27 22:03:57 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-20 20:30:53 +02:00
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
/* Writedata calls this before it starts a run, to set the proper flags
|
|
|
|
|
* on the dvecs. If a change is made during a break, then the routine
|
|
|
|
|
* wrd_chtrace is used from these routines. We have to be clever with save:
|
|
|
|
|
* if there was no save given, then save everything. Unfortunately you
|
|
|
|
|
* can't stop in the middle, do a save, and have the rest then discarded.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
ft_trquery(void)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-20 20:30:53 +02:00
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
static void
|
|
|
|
|
printcond(struct dbcomm *d, FILE *fp)
|
|
|
|
|
{
|
|
|
|
|
struct dbcomm *dt;
|
|
|
|
|
|
|
|
|
|
for (dt = d; dt; dt = dt->db_also) {
|
2012-09-20 20:30:53 +02:00
|
|
|
if (dt->db_type == DB_STOPAFTER) {
|
2000-04-27 22:03:57 +02:00
|
|
|
fprintf(fp, " after %d", dt->db_iteration);
|
2012-09-20 20:30:53 +02:00
|
|
|
} else {
|
2000-04-27 22:03:57 +02:00
|
|
|
if (dt->db_nodename1)
|
2012-09-20 20:30:53 +02:00
|
|
|
fprintf(fp, " when %s", dt->db_nodename1);
|
2000-04-27 22:03:57 +02:00
|
|
|
else
|
|
|
|
|
fprintf(fp, " when %g", dt->db_value1);
|
2012-09-20 20:30:53 +02:00
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
switch (dt->db_op) {
|
2012-09-20 20:30:53 +02:00
|
|
|
case DBC_EQU:
|
|
|
|
|
fputs(" =", fp);
|
|
|
|
|
break;
|
|
|
|
|
case DBC_NEQ:
|
|
|
|
|
fputs(" <>", fp);
|
|
|
|
|
break;
|
|
|
|
|
case DBC_GT:
|
|
|
|
|
fputs(" >", fp);
|
|
|
|
|
break;
|
|
|
|
|
case DBC_LT:
|
|
|
|
|
fputs(" <", fp);
|
|
|
|
|
break;
|
|
|
|
|
case DBC_GTE:
|
|
|
|
|
fputs(" >=", fp);
|
|
|
|
|
break;
|
|
|
|
|
case DBC_LTE:
|
|
|
|
|
fputs(" <=", fp);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
fprintf(cp_err,
|
|
|
|
|
"printcond: Internal Error: bad cond %d", dt->db_op);
|
2000-04-27 22:03:57 +02:00
|
|
|
}
|
2012-09-20 20:30:53 +02:00
|
|
|
|
2000-04-27 22:03:57 +02:00
|
|
|
if (dt->db_nodename2)
|
|
|
|
|
fprintf(fp, " %s", dt->db_nodename2);
|
|
|
|
|
else
|
|
|
|
|
fprintf(fp, " %g", dt->db_value2);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|