ngspice/src/frontend/breakp.c

569 lines
16 KiB
C
Raw Normal View History

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.
*/
#include "ngspice/ngspice.h"
#include "ngspice/cpdefs.h"
#include "ngspice/ftedefs.h"
#include "ngspice/dvec.h"
#include "ngspice/ftedebug.h"
2000-04-27 22:03:57 +02:00
#include "breakp.h"
#include "breakp2.h"
2010-10-16 19:09:46 +02:00
#include "runcoms2.h"
2000-04-27 22:03:57 +02:00
* src/Makefile.am src/main.c src/sconvert.c src/analysis/cktdisto.c src/analysis/cktnoise.c src/analysis/noisean.c: Updates for the new header files. * src/maths/cmaths/cmath1.c src/maths/cmaths/cmath2.c src/maths/cmaths/cmath3.c src/maths/cmaths/cmath4.c: Updates for the new header files. * src/frontend/.cvsignore src/frontend/Makefile.am: Updates for the new files. * src/frontend/agraf.c src/frontend/aspice.c src/frontend/breakp.c src/frontend/breakp2.c src/frontend/circuits.c src/frontend/cpitf.c src/frontend/debugcom.c src/frontend/define.c src/frontend/diff.c src/frontend/dimens.c src/frontend/display.c src/frontend/doplot.c src/frontend/dotcards.c src/frontend/evaluate.c src/frontend/fourier.c src/frontend/graf.c src/frontend/grid.c src/frontend/inp.c src/frontend/inpcom.c src/frontend/interp.c src/frontend/linear.c src/frontend/misccoms.c src/frontend/misccoms.h src/frontend/miscvars.c src/frontend/mw_coms.c src/frontend/newcoms.c src/frontend/nutinp.c src/frontend/options.c src/frontend/outitf.c src/frontend/parse.c src/frontend/plotcurv.c src/frontend/points.c src/frontend/postcoms.c src/frontend/rawfile.c src/frontend/runcoms.c src/frontend/runcoms2.c src/frontend/shyu.c src/frontend/spec.c src/frontend/spiceif.c src/frontend/typesdef.c src/frontend/vectors.c src/frontend/where.c src/frontend/postcoms.c: Updates for the new header files. Some commands have moved into the new files below. * src/frontend/README src/frontend/com_compose.c src/frontend/com_compose.h src/frontend/com_display.c src/frontend/com_display.h src/frontend/com_let.c src/frontend/com_let.h src/frontend/com_setscale.c src/frontend/com_setscale.h src/frontend/commands.c src/frontend/commands.h src/frontend/completion.h src/frontend/streams.h src/frontend/testcommands.c: Separation into different com_* commands. This is a start. The rest of the subdirectory needs doing. * src/include/complex.h src/include/cpdefs.h src/include/cpextern.h src/include/cpstd.h src/include/fteconst.h src/include/ftedata.h src/include/ftedev.h src/include/fteext.h src/include/ftegraph.h src/include/fteparse.h src/include/dvec.h src/include/grid.h src/include/plot.h src/include/pnode.h src/include/sim.h src/include/variable.h src/include/wordlist.h src/include/bool.h: Separation of header files into smaller pieces. This limits recompilation to only the affected source files. The original header files have a warning message embedded to flag obsoleted use. * src/frontend/compose.c src/frontend/compose.h src/frontend/nutctab.c src/frontend/nutctab.h src/frontend/plot5.c src/frontend/plot5.h src/frontend/spcmdtab.c src/frontend/x11.c src/frontend/x11.h src/frontend/xgraph.c src/frontend/xgraph.h: Moved these files into src/frontend/plotting subdirectory. * src/frontend/plotting/.cvsignore src/frontend/plotting/Makefile.am src/frontend/plotting/plot5.c src/frontend/plotting/plot5.h src/frontend/plotting/plotting.c src/frontend/plotting/plotting.h src/frontend/plotting/pvec.c src/frontend/plotting/pvec.h src/frontend/plotting/x11.c src/frontend/plotting/x11.h src/frontend/plotting/xgraph.c src/frontend/plotting/xgraph.h: The new libplotting library with automake and CVS infrastructure.
2000-05-06 16:12:51 +02:00
#include "completion.h"
2000-04-27 22:03:57 +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;
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)
{
/* Check for an active circuit */
if (ft_curckt == (struct circ *) NULL) {
fprintf(cp_err, "No circuit loaded. Stopping is not possible.\n");
return;
}
2000-04-27 22:03:57 +02:00
struct dbcomm *thisone = NULL;
struct dbcomm *d = NULL;
char *s, buf[64];
int i;
double *val;
while (wl) {
if (thisone == NULL) {
thisone = d = TMALLOC(struct dbcomm, 1);
} else {
d->db_also = TMALLOC(struct dbcomm, 1);
2000-04-27 22:03:57 +02:00
d = d->db_also;
}
/* Figure out what the first condition is. */
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;
if (!wl->wl_next->wl_word) {
i = 0;
} else {
2000-04-27 22:03:57 +02:00
#ifdef HAVE_CTYPE_H
for (s = wl->wl_next->wl_word, i = 0; *s; s++)
if (!isdigit_c(*s))
goto bad;
else
i = i * 10 + (*s - '0');
2000-04-27 22:03:57 +02:00
#else
i = atoi(wl->wl_next->wl_word); /* etoi ??? */
2000-04-27 22:03:57 +02:00
#endif
}
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) {
/* 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, '=') &&
(!(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 */
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, '=');
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;
wln = wl_build(charr);
2010-12-31 10:08:54 +01:00
wl_splice(wl->wl_next, wln);
2010-12-30 20:32:24 +01:00
}
/* continue with parsing the enhanced wordlist */
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;
val = ft_numparse(&s, FALSE);
if (val)
d->db_value1 = *val;
else
d->db_nodename1 = copy(wl->wl_word);
wl = wl->wl_next;
/* Now get the condition */
if (eq(wl->wl_word, "eq") || eq(wl->wl_word, "="))
d->db_op = DBC_EQU;
else if (eq(wl->wl_word, "ne") || eq(wl->wl_word, "<>"))
d->db_op = DBC_NEQ;
else if (eq(wl->wl_word, "gt") || eq(wl->wl_word, ">"))
d->db_op = DBC_GT;
else if (eq(wl->wl_word, "lt") || eq(wl->wl_word, "<"))
d->db_op = DBC_LT;
else if (eq(wl->wl_word, "ge") || eq(wl->wl_word, ">="))
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;
val = ft_numparse(&s, FALSE);
if (val)
d->db_value2 = *val;
else
d->db_nodename2 = copy(wl->wl_word);
wl = wl->wl_next;
} else {
2010-12-30 20:32:24 +01:00
goto bad;
}
2010-12-30 20:32:24 +01:00
}
2000-04-27 22:03:57 +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;
} else {
ft_curckt->ci_dbs = dbs = thisone;
}
2000-04-27 22:03:57 +02:00
(void) sprintf(buf, "%d", debugnumber);
cp_addkword(CT_DBNUMS, buf);
debugnumber++;
}
return;
bad:
fprintf(cp_err, "Syntax error parsing breakpoint specification.\n");
2000-04-27 22:03:57 +02:00
}
2000-04-27 22:03:57 +02:00
/* Trace a node (have wrd_point print it). Usage is "trace node ..."*/
void
com_trce(wordlist *wl)
{
settrace(wl, VF_PRINT, NULL);
2000-04-27 22:03:57 +02:00
}
2000-04-27 22:03:57 +02:00
/* Incrementally plot a value. This is just like trace. */
void
com_iplot(wordlist *wl)
{
/* 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
/* settrace(wl, VF_PLOT); */
struct dbcomm *d, *td, *currentdb = NULL;
char *s;
/* We use a modified ad-hoc algorithm here where db_also denotes
vectors on the same command line and db_next denotes
separate iplot commands. */
2000-04-27 22:03:57 +02:00
while (wl) {
s = cp_unquote(wl->wl_word);
d = TMALLOC(struct dbcomm, 1);
d->db_analysis = NULL;
2000-04-27 22:03:57 +02:00
d->db_number = debugnumber++;
if (eq(s, "all")) {
d->db_type = DB_IPLOTALL;
} else {
d->db_type = DB_IPLOT;
d->db_nodename1 = copy(s);
}
tfree(s);/*DG: avoid memory leak */
2000-04-27 22:03:57 +02:00
d->db_also = currentdb;
currentdb = d;
wl = wl->wl_next;
}
2000-04-27 22:03:57 +02:00
if (dbs) {
for (td = dbs; td->db_next; td = td->db_next)
;
td->db_next = currentdb;
} else {
ft_curckt->ci_dbs = dbs = currentdb;
}
2000-04-27 22:03:57 +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;
2010-06-23 19:40:56 +02:00
com_resume(NULL);
2000-04-27 22:03:57 +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.
*/
#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)))
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) {
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);
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)))
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");
} 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,
d->db_nodename1);
2000-04-27 22:03:57 +02:00
} else {
fprintf(cp_out, "exiting iplot %s", d->db_nodename1);
}
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);
}
}
/* free the dbcomm structure which has been defined in
* function settrace() in breakp2.c
*/
void
dbfree1(struct dbcomm *d)
{
tfree(d->db_nodename1);
tfree(d->db_nodename2);
if (d->db_also)
dbfree(d->db_also);
tfree(d);
}
2000-04-27 22:03:57 +02:00
void
dbfree(struct dbcomm *d)
2000-04-27 22:03:57 +02:00
{
while (d) {
struct dbcomm *next_d = d->db_next;
dbfree1(d);
d = next_d;
2000-04-27 22:03:57 +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")) {
dbfree(dbs);
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;
}
}
2000-04-27 22:03:57 +02:00
while (wl) {
if (wl->wl_word) {
2000-04-27 22:03:57 +02:00
#ifdef HAVE_CTYPE_H
for (s = wl->wl_word, i = 0; *s; s++)
if (!isdigit_c(*s)) {
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
i = atoi(wl->wl_next->wl_word); /* etoi ??? */
2000-04-27 22:03:57 +02:00
#endif
} 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
ft_curckt->ci_dbs = dbs = d->db_next;
dbfree1(d);
2000-04-27 22:03:57 +02:00
(void) sprintf(buf, "%d", i);
cp_remkword(CT_DBNUMS, buf);
break;
}
dt = d;
}
bad:
wl = wl->wl_next;
2000-04-27 22:03:57 +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) {
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;
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
}
}
2000-04-27 22:03:57 +02:00
if (dt == NULL) {
/* It made it */
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);
}
more: /* Just continue */
;
2000-04-27 22:03:57 +02:00
}
2000-04-27 22:03:57 +02:00
return (TRUE);
}
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;
if (d->db_nodename1) {
if ((v1 = vec_fromplot(d->db_nodename1, plot)) == NULL) {
fprintf(cp_err, "Error: %s: no such node\n", d->db_nodename1);
2000-04-27 22:03:57 +02:00
return (FALSE);
}
if (isreal(v1))
d1 = v1->v_realdata[v1->v_length - 1];
else
d1 = realpart((v1->v_compdata[v1->v_length - 1]));
} else {
2000-04-27 22:03:57 +02:00
d1 = d->db_value1;
}
2000-04-27 22:03:57 +02:00
if (d->db_nodename2) {
if ((v2 = vec_fromplot(d->db_nodename2, plot)) == NULL) {
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
d2 = realpart((v2->v_compdata[v2->v_length - 1]));
} else {
2000-04-27 22:03:57 +02:00
d2 = d->db_value2;
}
2000-04-27 22:03:57 +02:00
switch (d->db_op) {
case DBC_EQU:
return (AlmostEqualUlps(d1, d2, 3) ? TRUE : FALSE);
// 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
}
}
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)
{
}
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) {
if (dt->db_type == DB_STOPAFTER) {
2000-04-27 22:03:57 +02:00
fprintf(fp, " after %d", dt->db_iteration);
} else {
2000-04-27 22:03:57 +02:00
if (dt->db_nodename1)
fprintf(fp, " when %s", dt->db_nodename1);
2000-04-27 22:03:57 +02:00
else
fprintf(fp, " when %g", dt->db_value1);
2000-04-27 22:03:57 +02:00
switch (dt->db_op) {
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
}
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);
}
}
}