1/*
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 *
6 * Copyright 1997 - July 2008 CWI, August 2008 - 2019 MonetDB B.V.
7 */
8
9/*
10 * (author) M.L. Kersten
11 * For documentation see website.
12 */
13#include "monetdb_config.h"
14#include "mal.h"
15#include "mal_debugger.h"
16#include "mal_interpreter.h" /* for getArgReference() */
17#include "mal_linker.h" /* for getAddress() */
18#include "mal_listing.h"
19#include "mal_function.h"
20#include "mal_parser.h"
21#include "mal_namespace.h"
22#include "mal_private.h"
23
24typedef struct {
25 MalBlkPtr brkBlock[MAXBREAKS];
26 int brkPc[MAXBREAKS];
27 int brkVar[MAXBREAKS];
28 str brkMod[MAXBREAKS];
29 str brkFcn[MAXBREAKS];
30 char brkCmd[MAXBREAKS];
31 str brkRequest[MAXBREAKS];
32 int brkTop;
33} mdbStateRecord, *mdbState;
34
35typedef struct MDBSTATE{
36 MalBlkPtr mb;
37 MalStkPtr stk;
38 InstrPtr p;
39 int pc;
40} MdbState;
41
42#define skipBlanc(c, X) while (*(X) && isspace((unsigned char) *X)) { X++; }
43#define skipNonBlanc(c, X) while (*(X) && !isspace((unsigned char) *X)) { X++; }
44#define skipWord(c, X) while (*(X) && (isalnum((unsigned char) *X))) { X++; } \
45 skipBlanc(c, X);
46
47/* Utilities
48 * Dumping a stack on a file is primarilly used for debugging.
49 * Printing the stack requires access to both the symbol table and
50 * the stackframes in most cases.
51 * Beware that a stack frame need not be initialized with null values.
52 * It has been zeroed upon creation.
53 *
54 * The routine can also be used to inspect the symbol table of
55 * arbitrary functions.
56 */
57static void
58printStackHdr(stream *f, MalBlkPtr mb, ValPtr v, int index)
59{
60 VarPtr n = getVar(mb, index);
61
62 if (v == 0 && isVarConstant(mb, index))
63 v = &getVarConstant(mb, index);
64 mnstr_printf(f, "#[%2d] %5s", index, n->id);
65 mnstr_printf(f, " (%d,%d,%d) = ", getBeginScope(mb,index), getLastUpdate(mb,index),getEndScope(mb, index));
66 if (v)
67 ATOMprint(v->vtype, VALptr(v), f);
68}
69
70static void
71printBATproperties(stream *f, BAT *b)
72{
73 mnstr_printf(f, " count=" BUNFMT " lrefs=%d ",
74 BATcount(b), BBP_lrefs(b->batCacheid));
75 if (BBP_refs(b->batCacheid) - 1)
76 mnstr_printf(f, " refs=%d ", BBP_refs(b->batCacheid));
77 if (b->batSharecnt)
78 mnstr_printf(f, " views=%d", b->batSharecnt);
79 if (b->theap.parentid)
80 mnstr_printf(f, "view on %s ", BBPname(b->theap.parentid));
81}
82
83static void
84printBATelm(stream *f, bat i, BUN cnt, BUN first)
85{
86 BAT *b, *bs[2]={0};
87 str tpe;
88
89 b = BATdescriptor(i);
90 if (b) {
91 tpe = getTypeName(newBatType(b->ttype));
92 mnstr_printf(f, ":%s ", tpe);
93 GDKfree(tpe);
94 printBATproperties(f, b);
95 /* perform property checking */
96 BATassertProps(b);
97 mnstr_printf(f, "\n");
98 if (cnt && BATcount(b) > 0) {
99 if (cnt < BATcount(b)) {
100 mnstr_printf(f, "Sample " BUNFMT " out of " BUNFMT "\n", cnt, BATcount(b));
101 }
102 /* cut out a portion of the BAT for display */
103 bs[1] = BATslice(b, first, first + cnt);
104 /* get the void values */
105 if (bs[1] == NULL)
106 mnstr_printf(f, "Failed to take chunk\n");
107 else {
108 bs[0] = BATdense(bs[1]->hseqbase, 0, BATcount(bs[1]));
109 if( bs[0] == NULL){
110 mnstr_printf(f, "Failed to take chunk index\n");
111 } else {
112 BATprintcolumns(f, 2, bs);
113 BBPunfix(bs[0]->batCacheid);
114 BBPunfix(bs[1]->batCacheid);
115 }
116 }
117 }
118
119 BBPunfix(b->batCacheid);
120 } else
121 mnstr_printf(f, "\n");
122}
123
124static void
125printStackElm(stream *f, MalBlkPtr mb, ValPtr v, int index, BUN cnt, BUN first)
126{
127 str nme, nmeOnStk;
128 VarPtr n = getVar(mb, index);
129
130 printStackHdr(f, mb, v, index);
131
132 if (v && v->vtype == TYPE_bat) {
133 bat i = v->val.bval;
134 BAT *b = BBPquickdesc(i, true);
135
136 if (b) {
137 nme = getTypeName(newBatType(b->ttype));
138 mnstr_printf(f, " :%s rows="BUNFMT, nme, BATcount(b));
139 } else {
140 nme = getTypeName(n->type);
141 mnstr_printf(f, " :%s", nme);
142 }
143 } else {
144 nme = getTypeName(n->type);
145 mnstr_printf(f, " :%s", nme);
146 }
147 nmeOnStk = v ? getTypeName(v->vtype) : GDKstrdup(nme);
148 /* check for type errors */
149 if (strcmp(nmeOnStk, nme) && strncmp(nmeOnStk, "BAT", 3))
150 mnstr_printf(f, "!%s ", nmeOnStk);
151 mnstr_printf(f, " %s", (isVarConstant(mb, index) ? " constant" : ""));
152 mnstr_printf(f, " %s", (isVarUsed(mb,index) ? "": " not used" ));
153 mnstr_printf(f, " %s", (isVarTypedef(mb, index) ? " type variable" : ""));
154 GDKfree(nme);
155 mnstr_printf(f, "\n");
156 GDKfree(nmeOnStk);
157
158 if (cnt && v && (isaBatType(n->type) || v->vtype == TYPE_bat) && !is_bat_nil(v->val.bval)) {
159 printBATelm(f,v->val.bval,cnt,first);
160 }
161}
162
163void
164printStack(stream *f, MalBlkPtr mb, MalStkPtr s)
165{
166 int i = 0;
167
168 if (s) {
169 mnstr_printf(f, "#Stack '%s' size=%d top=%d\n",
170 getInstrPtr(mb, 0)->fcnname, s->stksize, s->stktop);
171 for (; i < mb->vtop; i++)
172 printStackElm(f, mb, s->stk + i, i, 0, 0);
173 } else
174 for (; i < mb->vtop; i++)
175 printStackElm(f, mb, 0, i, 0, 0);
176}
177
178/* utility to display an instruction being called and its stack position */
179static void
180printCall(Client cntxt, MalBlkPtr mb, MalStkPtr stk, int pc)
181{
182 str msg;
183 msg = instruction2str(mb, stk, getInstrPtr(mb, pc), LIST_MAL_CALL);
184 mnstr_printf(cntxt->fdout, "#%s at %s.%s[%d]\n", (msg?msg:"failed instruction2str()") ,
185 getModuleId(getInstrPtr(mb, 0)),
186 getFunctionId(getInstrPtr(mb, 0)), pc);
187 GDKfree(msg);
188}
189
190static void
191mdbBacktrace(Client cntxt, MalStkPtr stk, int pci)
192{
193 for (; stk != NULL; stk = stk->up) {
194 printCall(cntxt, stk->blk, stk, pci);
195 if (stk->up)
196 pci = stk->up->pcup;
197 }
198}
199
200void
201mdbDump(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr pci)
202{
203 int i = getPC(mb, pci);
204 mnstr_printf(cntxt->fdout, "!MDB dump of instruction %d\n", i);
205 if( i < 0)
206 return;
207 printFunction(cntxt->fdout, mb, stk, LIST_MAL_ALL);
208 mdbBacktrace(cntxt, stk, i);
209 printStack(cntxt->fdout, mb, stk);
210}
211
212#ifndef NDEBUG
213static void
214printBatDetails(stream *f, bat bid)
215{
216 BAT *b[2];
217 bat ret,ret2;
218 MALfcn fcn;
219
220 /* at this level we don't know bat kernel primitives */
221 mnstr_printf(f, "#Show info for %d\n", bid);
222 fcn = getAddress("BKCinfo");
223 if (fcn) {
224 (*fcn)(&ret,&ret2, &bid);
225 b[0] = BATdescriptor(ret);
226 if (b[0] == NULL)
227 return;
228 b[1] = BATdescriptor(ret2);
229 if (b[1] == NULL) {
230 BBPunfix(b[0]->batCacheid);
231 return;
232 }
233 BATprintcolumns(f, 2, b);
234 BBPunfix(b[0]->batCacheid);
235 BBPunfix(b[1]->batCacheid);
236 }
237}
238
239static void
240printBatInfo(stream *f, VarPtr n, ValPtr v)
241{
242 if (isaBatType(n->type) && v->val.ival)
243 printBatDetails(f, v->val.ival);
244}
245
246static void
247mdbHelp(stream *f)
248{
249 mnstr_printf(f, "next -- Advance to next statement\n");
250 mnstr_printf(f, "continue -- Continue program being debugged\n");
251 mnstr_printf(f, "catch -- Catch the next exception \n");
252 mnstr_printf(f, "break [<var>] -- set breakpoint on current instruction or <var>\n");
253 mnstr_printf(f, "delete [<var>] -- remove break/trace point <var>\n");
254 mnstr_printf(f, "debug <int> -- set kernel debugging mask\n");
255 mnstr_printf(f, "step -- advance to next MAL instruction\n");
256 mnstr_printf(f, "module -- display a module signatures\n");
257 mnstr_printf(f, "atom -- show atom list\n");
258 mnstr_printf(f, "finish -- finish current call\n");
259 mnstr_printf(f, "exit -- terminate executionr\n");
260 mnstr_printf(f, "quit -- turn off debugging\n");
261 mnstr_printf(f, "list <obj> -- list current program block\n");
262 mnstr_printf(f, "list # [+#],-# -- list current program block slice\n");
263 mnstr_printf(f, "List <obj> [#] -- list with type information[slice]\n");
264 mnstr_printf(f, "list [#] <obj> -- list program block after optimizer <#>\n");
265 mnstr_printf(f, "List # [+#],-# -- list current program block slice\n");
266 mnstr_printf(f, "var <obj> -- print symbol table for module\n");
267 mnstr_printf(f, "optimizer <obj> -- display optimizer steps\n");
268 mnstr_printf(f, "print <var> -- display value of a variable\n");
269 mnstr_printf(f, "print <var> <cnt>[<first>] -- display BAT chunk\n");
270 mnstr_printf(f, "info <var> -- display bat variable properties\n");
271 mnstr_printf(f, "run -- restart current procedure\n");
272 mnstr_printf(f, "where -- print stack trace\n");
273 mnstr_printf(f, "down -- go down the stack\n");
274 mnstr_printf(f, "up -- go up the stack\n");
275 mnstr_printf(f, "trace <var> -- trace assignment to variables\n");
276 mnstr_printf(f, "trap <mod>.<fcn> -- catch MAL function call in debugger\n");
277 mnstr_printf(f, "help -- this message\n");
278}
279
280/*
281 * The debugger flags overview
282 */
283static mdbStateRecord *mdbTable;
284
285bool
286mdbInit(void)
287{
288 /*
289 * Each client has its own breakpoint administration, kept in a
290 * global table. Although a little space consumptive, it is the
291 * easiest to maintain and much less expensive as reserving debugger
292 * space in each instruction.
293 */
294 mdbTable = GDKzalloc(sizeof(mdbStateRecord) * MAL_MAXCLIENTS);
295 if (mdbTable == NULL) {
296 fprintf(stderr,"#mdbInit:" MAL_MALLOC_FAIL);
297 return false;
298 }
299 return true;
300}
301
302void
303mdbExit(void)
304{
305 if (mdbTable) {
306 GDKfree(mdbTable);
307 mdbTable = 0;
308 }
309}
310
311static char
312isBreakpoint(Client cntxt, MalBlkPtr mb, InstrPtr p, int pc)
313{
314 int i, j;
315
316 for (i = 0; i < mdbTable[cntxt->idx].brkTop; i++) {
317 if (mdbTable[cntxt->idx].brkBlock[i] != mb)
318 continue;
319 if (mdbTable[cntxt->idx].brkPc[i] == pc)
320 return mdbTable[cntxt->idx].brkCmd[i];
321
322 if (mdbTable[cntxt->idx].brkMod[i] && getModuleId(p) &&
323 mdbTable[cntxt->idx].brkFcn[i] && getFunctionId(p) &&
324 strcmp(mdbTable[cntxt->idx].brkMod[i], getModuleId(p)) == 0 &&
325 strcmp(mdbTable[cntxt->idx].brkFcn[i], getFunctionId(p)) == 0)
326 return mdbTable[cntxt->idx].brkCmd[i];
327
328 if (mdbTable[cntxt->idx].brkVar[i] >= 0)
329 for (j = 0; j < p->retc; j++)
330 if (mdbTable[cntxt->idx].brkVar[i] == getArg(p, j))
331 return mdbTable[cntxt->idx].brkCmd[i];
332 }
333 return 0;
334}
335
336/*
337 * Break points can be set on assignment to a specific variable,
338 * specific operation, or a instruction line
339 */
340void
341mdbSetBreakRequest(Client cntxt, MalBlkPtr mb, str request, char cmd)
342{
343 int i;
344 str modnme, fcnnme;
345 mdbState mdb = mdbTable + cntxt->idx;
346 Symbol sym;
347
348 /* set breakpoint on specific line */
349 if (*request == '#') {
350 i = atoi(request + 1);
351 if (i < 0 || i >= mb->stop)
352 mnstr_printf(cntxt->fdout, "breakpoint on #%d (<%d) not set\n",
353 i, mb->stop);
354 else {
355 mdb->brkBlock[mdb->brkTop] = mb;
356 mdb->brkPc[mdb->brkTop] = i;
357 mdb->brkVar[mdb->brkTop] = -1;
358 mdb->brkMod[mdb->brkTop] = 0;
359 mdb->brkFcn[mdb->brkTop] = 0;
360 mdb->brkRequest[mdb->brkTop] = GDKstrdup(request);
361 mdb->brkCmd[mdb->brkTop] = cmd;
362 if (mdb->brkTop + 1 < MAXBREAKS)
363 mdb->brkTop++;
364 }
365 return;
366 }
367
368 /* check for a [module.]function request */
369 fcnnme = strchr(request, '.');
370 if (fcnnme) {
371 modnme = request;
372 *fcnnme = 0;
373 fcnnme++;
374 sym = findSymbol(cntxt->usermodule, modnme, fcnnme);
375 mdb->brkBlock[mdb->brkTop] = sym ? sym->def : mb;
376 mdb->brkPc[mdb->brkTop] = -1;
377 mdb->brkVar[mdb->brkTop] = -1;
378 mdb->brkMod[mdb->brkTop] = putName(modnme);
379 mdb->brkFcn[mdb->brkTop] = putName(fcnnme);
380 fcnnme--;
381 *fcnnme = '.';
382 mdb->brkRequest[mdb->brkTop] = GDKstrdup(request);
383 mdb->brkCmd[mdb->brkTop] = cmd;
384 if (mdb->brkTop + 1 < MAXBREAKS)
385 mdb->brkTop++;
386 return;
387 }
388 /* the final step is to break on a variable */
389 i = findVariable(mb, request);
390 /* ignore a possible dummy TMPMARKER character */
391 if ( i < 0)
392 i = findVariable(mb, request+1);
393 if (i < 0)
394 mnstr_printf(cntxt->fdout, "breakpoint on %s not set\n", request);
395 else {
396 mdb->brkBlock[mdb->brkTop] = mb;
397 mdb->brkPc[mdb->brkTop] = -1;
398 mdb->brkVar[mdb->brkTop] = i;
399 mdb->brkMod[mdb->brkTop] = 0;
400 mdb->brkFcn[mdb->brkTop] = 0;
401 mdb->brkRequest[mdb->brkTop] = GDKstrdup(request);
402 mdb->brkCmd[mdb->brkTop] = cmd;
403 if (mdb->brkTop + 1 < MAXBREAKS)
404 mdb->brkTop++;
405 }
406}
407
408/* A breakpoint should be set once for each combination */
409static void
410mdbSetBreakpoint(Client cntxt, MalBlkPtr mb, int pc, char cmd)
411{
412 mdbState mdb = mdbTable + cntxt->idx;
413 char buf[20];
414
415 snprintf(buf, 20, "#%d", pc);
416 mdb->brkBlock[mdb->brkTop] = mb;
417 mdb->brkPc[mdb->brkTop] = pc;
418 mdb->brkVar[mdb->brkTop] = -1;
419 mdb->brkMod[mdb->brkTop] = 0;
420 mdb->brkFcn[mdb->brkTop] = 0;
421 mdb->brkRequest[mdb->brkTop] = GDKstrdup(buf);
422 mdb->brkCmd[mdb->brkTop] = cmd;
423 if (mdb->brkTop + 1 < MAXBREAKS)
424 mdb->brkTop++;
425}
426
427static void
428mdbShowBreakpoints(Client cntxt)
429{
430 int i;
431 mdbState mdb = mdbTable + cntxt->idx;
432
433 for (i = 0; i < mdb->brkTop; i++)
434 mnstr_printf(cntxt->fdout, "breakpoint on '%s'\n", mdb->brkRequest[i]);
435}
436
437static void
438mdbClrBreakpoint(Client cntxt, int pc)
439{
440 int i, j = 0;
441 mdbState mdb = mdbTable + cntxt->idx;
442
443 for (i = 0; i < mdb->brkTop; i++) {
444 mdb->brkBlock[j] = mdb->brkBlock[i];
445 mdb->brkPc[j] = mdb->brkPc[i];
446 mdb->brkVar[j] = mdb->brkVar[i];
447 mdb->brkMod[j] = mdb->brkMod[i];
448 mdb->brkFcn[j] = mdb->brkFcn[i];
449 mdb->brkRequest[j] = mdb->brkRequest[i];
450 mdb->brkCmd[j] = mdb->brkCmd[i];
451 if (mdb->brkPc[i] != pc)
452 j++;
453 else {
454 GDKfree(mdb->brkRequest[i]);
455 mdb->brkRequest[i] = 0;
456 }
457 }
458 mdb->brkTop = j;
459}
460
461static void
462mdbClrBreakRequest(Client cntxt, str request)
463{
464 int i, j = 0;
465 mdbState mdb = mdbTable + cntxt->idx;
466
467 for (i = 0; i < mdb->brkTop; i++) {
468 mdb->brkBlock[j] = mdb->brkBlock[i];
469 mdb->brkPc[j] = mdb->brkPc[i];
470 mdb->brkVar[j] = mdb->brkVar[i];
471 mdb->brkMod[j] = mdb->brkMod[i];
472 mdb->brkFcn[j] = mdb->brkFcn[i];
473 mdb->brkRequest[j] = mdb->brkRequest[i];
474 mdb->brkCmd[j] = mdb->brkCmd[i];
475 if (strcmp(mdb->brkRequest[i], request))
476 j++;
477 else {
478 GDKfree(mdb->brkRequest[i]);
479 mdb->brkRequest[i] = 0;
480 }
481 }
482 mdb->brkTop = j;
483}
484
485/* utility to display instruction and dispose of structure */
486static void
487printTraceCall(stream *out, MalBlkPtr mb, MalStkPtr stk, int pc, int flags)
488{
489 str msg;
490 InstrPtr p;
491
492 p = getInstrPtr(mb, pc);
493 msg = instruction2str(mb, stk, p, flags);
494 mnstr_printf(out, "#%s%s\n", (mb->errors != MAL_SUCCEED ? "!" : ""), msg?msg:"failed instruction2str()");
495 GDKfree(msg);
496}
497
498/* MAL debugger parser
499 * The debugger structure is inherited from GDB.
500 * The routine mdbCommand is called with p=0 after finishing a mal- function call
501 * and before continuing at the next level of invocation.
502 * The commands are self-explanatory.
503 *
504 * The prompt string sent to the user indicates the debugger mode.
505 *
506 * The history of the optimizers is maintained, which can be localized
507 * for inspection.
508 */
509#define MDBstatus(X) if (cntxt->fdout) \
510 mnstr_printf(cntxt->fdout, "#MonetDB Debugger %s\n", (X ? "on" : "off"));
511
512static MalBlkPtr
513mdbLocateMalBlk(Client cntxt, MalBlkPtr mb, str b)
514{
515 MalBlkPtr m = mb;
516 char *h = 0;
517 int idx = -1;
518
519 skipBlanc(cntxt, b);
520 /* start with function in context */
521 if (*b == '[') {
522 if( !isdigit((unsigned char) *(b+1))){
523 m = getMalBlkOptimized(mb, b+1);
524 if( m ==0 )
525 mnstr_printf(cntxt->fdout,"Integer or optimizer named expected");
526 else
527 return m;
528 } else
529 idx = atoi(b + 1);
530 if( idx < 0)
531 return NULL;
532 return getMalBlkHistory(mb, idx);
533 } else if (isdigit((unsigned char) *b)) {
534 idx = atoi(b);
535 if( idx < 0)
536 return NULL;
537 return getMalBlkHistory(mb, idx);
538 } else if (*b != 0) {
539 char *fcnname = strchr(b, '.');
540 Symbol fsym;
541 if (fcnname == NULL)
542 return NULL;
543 *fcnname = 0;
544 if ((h = strchr(fcnname + 1, '['))) {
545 *h = 0;
546 if( !isdigit((unsigned char) *(h+1))){
547 m = getMalBlkOptimized(mb, h+1);
548 if( m ==0 )
549 mnstr_printf(cntxt->fdout,"Integer or optimizer named expected");
550 else
551 return m;
552 } else
553 idx = atoi(h + 1);
554 if( idx < 0)
555 return NULL;
556 }
557 fsym = findSymbolInModule(findModule(cntxt->usermodule, putName(b)), fcnname + 1);
558 *fcnname = '.';
559 if (h)
560 *h = '[';
561 if (fsym == 0) {
562 return NULL;
563 }
564 m = fsym->def;
565 return getMalBlkHistory(m, h ? idx : -1);
566 }
567 return getMalBlkHistory(mb, -1);
568}
569
570static void
571printBatProperties(stream *f, VarPtr n, ValPtr v, str props)
572{
573 if (isaBatType(n->type) && v->val.ival) {
574 bat bid;
575 bat ret,ret2;
576 MALfcn fcn;
577 BUN p;
578
579 /* at this level we don't know bat kernel primitives */
580 fcn = getAddress("BKCinfo");
581 if (fcn) {
582 BAT *b[2];
583 str res;
584
585 bid = v->val.ival;
586 mnstr_printf(f, "BAT %d %s= ", bid, props);
587 res = (*fcn)(&ret, &ret2, &bid);
588 if (res != MAL_SUCCEED) {
589 GDKfree(res);
590 mnstr_printf(f, "mal.info failed\n");
591 return;
592 }
593 b[0] = BATdescriptor(ret);
594 b[1] = BATdescriptor(ret2);
595 if (b[0] == NULL || b[1] == NULL) {
596 mnstr_printf(f, "Could not access descriptor\n");
597 if (b[0])
598 BBPunfix(b[0]->batCacheid);
599 if (b[1])
600 BBPunfix(b[1]->batCacheid);
601 return;
602 }
603 p = BUNfnd(b[0], props);
604 if (p != BUN_NONE) {
605 BATiter bi = bat_iterator(b[1]);
606 mnstr_printf(f, " %s\n", (str) BUNtvar(bi, p));
607 } else {
608 mnstr_printf(f, " not found\n");
609 }
610 BBPunfix(b[0]->batCacheid);
611 BBPunfix(b[1]->batCacheid);
612 }
613 }
614}
615
616
617static void
618mdbCommand(Client cntxt, MalBlkPtr mb, MalStkPtr stkbase, InstrPtr p, int pc)
619{
620 int m = 1;
621 char *b= 0, *c, lastcmd = 0;
622 stream *out = cntxt->fdout;
623 char *oldprompt = cntxt->prompt;
624 size_t oldpromptlength = cntxt->promptlength;
625 MalStkPtr stk = stkbase;
626 int first = pc - ( pc == 1);
627 int stepsize = 1000;
628 char oldcmd[1024] = { 0 };
629 str msg = MAL_SUCCEED;
630
631 do {
632 if (p != NULL) {
633 /* help mclients with fake prompt */
634 if (lastcmd != 'l' && lastcmd != 'L') {
635 mnstr_printf(out, "mdb>");
636 printTraceCall(out, mb, stk, pc, LIST_MAL_CALL);
637 }
638
639 }
640
641 if (cntxt->phase[MAL_SCENARIO_READER]) {
642retryRead:
643 msg = (char *) (*cntxt->phase[MAL_SCENARIO_READER])(cntxt);
644 if (msg != MAL_SUCCEED || cntxt->mode == FINISHCLIENT){
645 freeException(msg);
646 break;
647 }
648 /* SQL patch, it should only react to Smessages, Xclose requests to be ignored */
649 if (strncmp(cntxt->fdin->buf, "Xclose", 6) == 0) {
650 cntxt->fdin->pos = cntxt->fdin->len;
651 goto retryRead;
652 }
653 }
654 b = CURRENT(cntxt);
655
656 /* terminate the line with zero */
657 c = strchr(b, '\n');
658 if (c) {
659 *c = 0;
660 strcpy_len(oldcmd, b, sizeof(oldcmd));
661 cntxt->fdin->pos += (c - b) + 1;
662 } else
663 cntxt->fdin->pos = cntxt->fdin->len;
664
665 skipBlanc(cntxt, b);
666 if (*b)
667 lastcmd = *b;
668 else
669 strcpy(b = cntxt->fdin->buf, oldcmd);
670 b = oldcmd;
671 switch (*b) {
672 case 0:
673 m = 0;
674 break;
675 case 'c':
676 if (strncmp("check",b,5) == 0){
677 Symbol fs;
678 int i;
679 str fcnnme;
680
681 skipWord(cntxt,b);
682 skipBlanc(cntxt, b);
683 if( (fcnnme = strchr(b,'.')) != NULL ){
684 str modnme = b;
685
686 *fcnnme++ = 0;
687
688 fs = findSymbol(cntxt->usermodule, putName(modnme),putName(fcnnme));
689 if (fs == 0) {
690 mnstr_printf(out, "#'%s.%s' not found\n", modnme,fcnnme);
691 continue;
692 }
693 for(; fs; fs = fs->peer){
694 for(i=0; i< fs->def->stop; i++)
695 fs->def->stmt[i]->typechk = TYPE_UNKNOWN;
696 chkProgram(cntxt->usermodule, fs->def);
697 }
698 } else
699 mnstr_printf(out, "#<modnme>.<fcnnme> expected\n");
700 break;
701 }
702 if (strncmp("catch", b, 3) == 0) {
703 /* catch the next exception */
704 stk->cmd = 'C';
705 break;
706 }
707 stk->cmd = 'c';
708 skipWord(cntxt, b);
709 m = 0;
710 break;
711 case 'e':
712 {
713 /* terminate the execution for ordinary functions only */
714 if (strncmp("exit", b, 4) == 0) {
715 case 'x':
716 if (!(getInstrPtr(mb, 0)->token == FACcall)) {
717 stk->cmd = 'x';
718 cntxt->prompt = oldprompt;
719 cntxt->promptlength = oldpromptlength;
720 }
721 }
722 return;
723 }
724 case 'q':
725 {
726 MalStkPtr su;
727
728 /* return from this debugger */
729 for (su = stk; su; su = su->up)
730 su->cmd = 0;
731 cntxt->itrace = 0;
732 mnstr_printf(out, "mdb>#EOD\n");
733 /* MDBstatus(0); */
734 cntxt->prompt = oldprompt;
735 cntxt->promptlength = oldpromptlength;
736 return;
737 }
738 case 'f': /* finish */
739 case 'n': /* next */
740 case 's': /* step */
741 if (strncmp("scenarios", b, 9) == 0) {
742 showAllScenarios(out);
743 continue;
744 } else if (strncmp("scenario", b, 3) == 0) {
745 showScenarioByName(out, cntxt->scenario);
746 continue;
747 }
748 stk->cmd = *b;
749 m = 0;
750 break;
751 case 'M': /* dump all module names */
752 dumpModules(out);
753 break;
754 case 'm': /* display a module */
755 {
756 str modname, fcnname;
757 Module fsym;
758 Symbol fs;
759 int i;
760
761 skipWord(cntxt, b);
762 skipBlanc(cntxt, b);
763 if (*b) {
764 modname = b;
765 fcnname = strchr(b, '.');
766 if (fcnname) {
767 *fcnname = 0;
768 fcnname++;
769 }
770 fsym = findModule(cntxt->usermodule, putName(modname));
771
772 if (fsym == cntxt->usermodule && strcmp(modname, "user")) {
773 mnstr_printf(out, "#module '%s' not found\n", modname);
774 continue;
775 }
776 for (i = 0; i < MAXSCOPE; i++) {
777 fs = fsym->space[i];
778 while (fs != NULL) {
779 printSignature(out, fs, 0);
780 fs = fs->peer;
781 }
782 }
783 continue;
784 } else {
785 Module* list;
786 int length;
787 int i;
788 mnstr_printf(out,"#%s ",cntxt->usermodule->name);
789 getModuleList(&list, &length);
790 for(i = 0; i < length; i++) {
791 mnstr_printf(out, "%s ", list[i]->name);
792 }
793 freeModuleList(list);
794 mnstr_printf(out,"\n");
795 }
796 }
797 break;
798 case 'T': /* debug type resolver for a function call */
799 if (strncmp("Trace", b, 5) == 0) {
800 char *w;
801 skipWord(cntxt, b);
802 skipBlanc(cntxt, b);
803 if ((w = strchr(b, '\n')))
804 *w = 0;
805 }
806 break;
807 case 't': /* trace a variable toggle */
808 if (strncmp("trap", b, 4) == 0) {
809 char *w, *mod, *fcn;
810 skipWord(cntxt, b);
811 skipBlanc(cntxt, b);
812 mod = b;
813 skipWord(cntxt, b);
814 *b = 0;
815 fcn = b + 1;
816 if ((w = strchr(b + 1, '\n')))
817 *w = 0;
818 mnstr_printf(out, "#trap %s.%s\n", mod, fcn);
819 }
820 if (strncmp("trace", b, 5) == 0) {
821 char *w;
822 skipWord(cntxt, b);
823 skipBlanc(cntxt, b);
824 if ((w = strchr(b, '\n')))
825 *w = 0;
826 mdbSetBreakRequest(cntxt, mb, b, 't');
827 }
828 break;
829 case 'v': /* show the symbol table and bindings */
830 case 'V': {
831 str modname, fcnname;
832 Module fsym;
833 Symbol fs;
834 int i;
835
836 skipWord(cntxt, b);
837 if (*b != 0) {
838 modname = b;
839 fcnname = strchr(b, '.');
840 if (fcnname == NULL) {
841 fsym = findModule(cntxt->usermodule, putName(modname));
842 if (fsym == 0) {
843 mnstr_printf(out, "#%s module not found\n", modname);
844 continue;
845 }
846 for (i = 0; i < MAXSCOPE; i++) {
847 fs = fsym->space[i];
848 while (fs != NULL) {
849 printStack(out, fs->def, 0);
850 fs = fs->peer;
851 }
852 }
853 continue;
854 }
855 *fcnname = 0;
856 fcnname++;
857 fsym = findModule(cntxt->usermodule, putName(modname));
858 if (fsym == 0) {
859 mnstr_printf(out, "#%s module not found\n", modname);
860 continue;
861 }
862 /* display the overloaded symbol definition */
863 for (i = 0; i < MAXSCOPE; i++) {
864 fs = fsym->space[i];
865 while (fs != NULL) {
866 if (strcmp(fs->name, fcnname) == 0)
867 printStack(out, fs->def, 0);
868 fs = fs->peer;
869 }
870 }
871 } else
872 printStack(out, mb, stk);
873 break;
874 }
875 case 'b':
876 if (strncmp(b, "bbp", 3) == 0) {
877 int i, limit, inuse = 0;
878
879 skipWord(cntxt, b);
880 i = BBPindex(b);
881 if (i)
882 limit = i + 1;
883 else {
884 limit = getBBPsize();
885 i = 1;
886 }
887 /* the 'dense' qualification only shows entries with a hard ref */
888 /* watchout, you don't want to wait for locks by others */
889 mnstr_printf(out, "BBP contains %d entries\n", limit);
890 for (; i < limit; i++)
891 if ((BBP_lrefs(i) || BBP_refs(i)) && BBP_cache(i)) {
892 mnstr_printf(out, "#[%d] %-15s", i, BBP_logical(i));
893 if (BBP_cache(i))
894 printBATproperties(out, BBP_cache(i));
895 if ((*b == 'd' && BBP_refs(i) == 0) || BBP_cache(i) == 0) {
896 mnstr_printf(out, "\n");
897 continue;
898 }
899 inuse++;
900 if (BATdirty(BBP_cache(i)))
901 mnstr_printf(out, " dirty");
902 if (*BBP_logical(i) == '.')
903 mnstr_printf(out, " zombie ");
904 if (BBPstatus(i) & BBPLOADED)
905 mnstr_printf(out, " loaded ");
906 if (BBPstatus(i) & BBPSWAPPED)
907 mnstr_printf(out, " swapped ");
908 if (BBPstatus(i) & BBPTMP)
909 mnstr_printf(out, " tmp ");
910 if (BBPstatus(i) & BBPDELETED)
911 mnstr_printf(out, " deleted ");
912 if (BBPstatus(i) & BBPEXISTING)
913 mnstr_printf(out, " existing ");
914 if (BBPstatus(i) & BBPNEW)
915 mnstr_printf(out, " new ");
916 if (BBPstatus(i) & BBPPERSISTENT)
917 mnstr_printf(out, " persistent ");
918 mnstr_printf(out, "\n");
919 }
920
921 mnstr_printf(out, "#Entries displayed %d\n", inuse);
922 continue;
923 }
924 if (strncmp(b, "breakpoints", 11) == 0) {
925 mdbShowBreakpoints(cntxt);
926 continue;
927 }
928 if (strncmp(b, "break", 5) == 0)
929 b += 4;
930 if (isspace((unsigned char) b[1])) {
931 skipWord(cntxt, b);
932 if (*b && !isspace((unsigned char) *b) && !isdigit((unsigned char) *b))
933 /* set breakpoints by name */
934 mdbSetBreakRequest(cntxt, mb, b, 's');
935 else if (*b && isdigit((unsigned char) *b))
936 /* set breakpoint at instruction */
937 mdbSetBreakpoint(cntxt, mb, atoi(b), 's');
938 else
939 /* set breakpoint at current instruction */
940 mdbSetBreakpoint(cntxt, mb, pc, 's');
941 continue;
942 }
943 continue;
944 case 'd':
945 if (strncmp(b, "debug", 5) == 0) {
946 skipWord(cntxt, b);
947 GDKdebug = atol(b);
948 mnstr_printf(out, "#Set debug mask to %d\n", GDKdebug);
949 break;
950 }
951 if (strncmp(b, "down", 4) == 0) {
952 MalStkPtr ref = stk;
953 /* find the previous one from the base */
954 stk = stkbase;
955 while (stk != ref && stk->up && stk->up != ref)
956 stk = stk->up;
957 mnstr_printf(out, "#%sgo down the stack\n", "#mdb ");
958 mb = stk->blk;
959 break;
960 }
961 skipWord(cntxt, b);
962 /* get rid of break point */
963 if (*b && !isspace((unsigned char) *b) && !isdigit((unsigned char) *b))
964 mdbClrBreakRequest(cntxt, b);
965 else if (isdigit((unsigned char) *b))
966 mdbClrBreakpoint(cntxt, atoi(b));
967 else {
968 mdbClrBreakpoint(cntxt, pc);
969 }
970 continue;
971 case 'I':
972 case 'i':
973 {
974 int i;
975 char *t;
976
977 /* the user wants information about variables */
978 if (*b == 'I') {
979 skipWord(cntxt, b);
980 for (i = 0; i < mb->vtop; i++)
981 printBatProperties(out, getVar(mb, i), stk->stk + i, b);
982 continue;
983 }
984 skipWord(cntxt, b);
985 t = b;
986 skipNonBlanc(cntxt, t);
987 *t = 0;
988 /* search the symbol */
989 i = findVariable(mb, b);
990 if (i < 0) {
991 /* could be the name of a BAT */
992 i = BBPindex(b);
993 if (i != 0)
994 printBatDetails(out, i);
995 else
996 mnstr_printf(out, "#%s Symbol not found\n", "#mdb ");
997 } else {
998 printBatInfo(out, getVar(mb, i), stk->stk + i);
999 }
1000 continue;
1001 }
1002 case 'P':
1003 case 'p':
1004 {
1005 BUN size = 0, first = 0;
1006 int i;
1007 char *t;
1008 char upper = *b;
1009
1010 skipWord(cntxt, b);
1011 t = b;
1012 skipNonBlanc(cntxt, t);
1013 *t = 0;
1014 /* you can identify a start and length */
1015 t++;
1016 skipBlanc(cntxt, t);
1017 if (isdigit((unsigned char) *t)) {
1018 size = (BUN) atol(t);
1019 skipWord(cntxt, t);
1020 if (isdigit((unsigned char) *t))
1021 first = (BUN) atol(t);
1022 }
1023 /* search the symbol */
1024 i = findVariable(mb, b);
1025 if (i < 0) {
1026 // deal with temporary
1027 if( *b == 'X' ) b++;
1028 i = findVariable(mb, b);
1029 }
1030 if (i < 0) {
1031 mnstr_printf(out, "#%s Symbol not found\n", b);
1032 continue;
1033 }
1034 if (isaBatType(getVarType(mb, i)) && upper == 'p') {
1035 printStackHdr(out, mb, stk->stk + i, i);
1036 printBATelm(out, stk->stk[i].val.bval, size, first);
1037 } else
1038 printStackElm(out, mb, stk->stk + i, i, size, first);
1039 continue;
1040 }
1041 case 'u':
1042 if (stk->up == NULL)
1043 break;
1044 mnstr_printf(out, "#%s go up the stack\n", "#mdb ");
1045 stk = stk->up;
1046 mb = stk->blk;
1047 printCall(cntxt, mb, stk, pc);
1048 continue;
1049 case 'w':
1050 {
1051 mdbBacktrace(cntxt, stk, pc);
1052 continue;
1053 }
1054/*
1055 * While debugging it should be possible to inspect the symbol
1056 * table using the 'module.function' name. The default is to list all
1057 * signatures satisfying the pattern.
1058 */
1059 case 'L':
1060 case 'l': /* list the current MAL block or module */
1061 {
1062 Symbol fs;
1063 int lstng;
1064
1065 lstng = LIST_MAL_NAME;
1066 if(*b == 'L')
1067 lstng = LIST_MAL_NAME | LIST_MAL_VALUE | LIST_MAL_TYPE | LIST_MAL_PROPS;
1068 skipWord(cntxt, b);
1069 skipBlanc(cntxt, b);
1070 if (*b != 0) {
1071 /* debug the current block */
1072 MalBlkPtr m = mdbLocateMalBlk(cntxt, mb, b);
1073
1074 if ( m == 0)
1075 m = mb;
1076 if ( m ){
1077 str nme = getFunctionId(mb->stmt[0]);
1078 str s = strstr(b, nme);
1079 if( s ){
1080 b = s + strlen(nme);
1081 skipBlanc(cntxt,b);
1082 }
1083 }
1084 if (isdigit((unsigned char) *b) || *b == '-' || *b == '+')
1085 goto partial;
1086
1087 /* inspect another function */
1088 if( strchr(b,'.') ){
1089 str modnme = b;
1090 str fcnnme;
1091 fcnnme = strchr(b,'.');
1092 *fcnnme++ = 0;
1093 b = fcnnme;
1094 skipNonBlanc(cntxt, b);
1095 if ( b)
1096 *b++ = 0;
1097
1098 fs = findSymbol(cntxt->usermodule, putName(modnme),putName(fcnnme));
1099 if (fs == 0) {
1100 mnstr_printf(out, "#'%s' not found\n", modnme);
1101 continue;
1102 }
1103 for(; fs; fs = fs->peer)
1104 if( strcmp(fcnnme, fs->name)==0) {
1105 if( lstng == LIST_MAL_NAME)
1106 printFunction(out, fs->def, 0, lstng);
1107 else
1108 debugFunction(out, fs->def, 0, lstng, 0,mb->stop);
1109 }
1110 continue;
1111 }
1112 if (m){
1113 if( lstng == LIST_MAL_NAME)
1114 printFunction(out, m, 0, lstng);
1115 else
1116 debugFunction(out, m, 0, lstng, 0,m->stop);
1117 }
1118 } else {
1119/*
1120 * Listing the program starts at the pc last given.
1121 * Repeated use of the list command moves you up and down the program
1122 */
1123partial:
1124 if (isdigit((unsigned char) *b)) {
1125 first = (int) atoi(b);
1126 skipWord(cntxt, b);
1127 skipBlanc(cntxt, b);
1128 }
1129 if (*b == '-') {
1130 stepsize = (int) atoi(b + 1);
1131 first -= stepsize++;
1132 } else if (*b == '+')
1133 stepsize = (int) atoi(b + 1);
1134 else if (atoi(b))
1135 stepsize = (int) atoi(b);
1136 *b = 0;
1137 if (stepsize < 0)
1138 first -= stepsize;
1139 if( first > mb->stop ) {
1140 mnstr_printf(out, "#line %d out of range (<=%d)\n", first, mb->stop);
1141 first = pc;
1142 } else {
1143 debugFunction(out, mb, 0, lstng, first, stepsize);
1144 first = first + stepsize > mb->stop ? first : first + stepsize;
1145 }
1146 }
1147 continue;
1148 }
1149 case 'h':
1150 mdbHelp(out);
1151 continue;
1152 case 'o':
1153 case 'O': /* optimizer and scheduler steps */
1154 {
1155 MalBlkPtr mdot = mb;
1156 skipWord(cntxt, b);
1157 skipBlanc(cntxt, b);
1158 if (*b) {
1159 mdot = mdbLocateMalBlk(cntxt, mb, b);
1160 if (mdot != NULL)
1161 showMalBlkHistory(out, mdot);
1162 else
1163 mnstr_printf(out, "#'%s' not found\n", b);
1164 } else
1165 showMalBlkHistory(out, mb);
1166 break;
1167 }
1168 case 'r': /* reset program counter */
1169 mnstr_printf(out, "#%s restart with current stack\n", "#mdb ");
1170 stk->cmd = 'r';
1171 break;
1172 default:
1173 mnstr_printf(out, "#%s debugger command expected\n", "#mdb ");
1174 mdbHelp(out);
1175 }
1176 } while (m);
1177 cntxt->prompt = oldprompt;
1178 cntxt->promptlength = oldpromptlength;
1179}
1180
1181static str
1182mdbTrap(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p)
1183{
1184 int pc = getPC(mb,p);
1185
1186 mnstr_printf(cntxt->fdout, "#trapped %s.%s[%d]\n",
1187 getModuleId(mb->stmt[0]), getFunctionId(mb->stmt[0]), pc);
1188 printInstruction(cntxt->fdout, mb, stk, p, LIST_MAL_DEBUG);
1189 cntxt->itrace = 'W';
1190 return MAL_SUCCEED;
1191}
1192
1193void
1194mdbStep(Client cntxt, MalBlkPtr mb, MalStkPtr stk, int pc)
1195{
1196 InstrPtr p;
1197 char ch;
1198 stream *out = cntxt->fdout;
1199
1200 /* mdbSanityCheck(cntxt, mb, stk, pc); expensive */
1201 /* process should sleep */
1202 if (cntxt->itrace == 'S') {
1203 MdbState state;
1204 state.mb = mb;
1205 state.stk = stk;
1206 state.p = getInstrPtr(mb, pc);
1207 state.pc = pc;
1208 mnstr_printf(cntxt->fdout, "#Process %d put to sleep\n", (int) (cntxt - mal_clients));
1209 cntxt->itrace = 'W';
1210 mdbTrap(cntxt, mb, stk, state.p);
1211 while (cntxt->itrace == 'W')
1212 MT_sleep_ms(300);
1213 mnstr_printf(cntxt->fdout, "#Process %d woke up\n", (int) (cntxt - mal_clients));
1214 return;
1215 }
1216 if (stk->cmd == 0)
1217 stk->cmd = 'n';
1218 p = getInstrPtr(mb, pc);
1219 switch (stk->cmd) {
1220 case 'c':
1221 ch = isBreakpoint(cntxt, mb, p, pc);
1222 if (ch == 't') {
1223 /* help mclients with fake prompt */
1224 mnstr_printf(out, "mdb>");
1225 printTraceCall(out, mb, stk, pc, LIST_MAL_CALL);
1226 } else if (ch)
1227 mdbCommand(cntxt, mb, stk, p, pc);
1228 break;
1229 case 's':
1230 case 'n':
1231 mdbCommand(cntxt, mb, stk, p, pc);
1232 break;
1233 case 't':
1234 printTraceCall(out, mb, stk, pc, LIST_MAL_CALL);
1235 break;
1236 }
1237 if (mb->errors != MAL_SUCCEED) {
1238 MalStkPtr su;
1239
1240 /* return from this debugger */
1241 for (su = stk; su; su = su->up)
1242 su->cmd = 0;
1243 mnstr_printf(out, "mdb>#EOD\n");
1244 stk->cmd = 'x'; /* will force a graceful termination */
1245 }
1246}
1247
1248/*
1249 * It would come in handy if at any time you could activate
1250 * the debugger on a specific function. This calls for the
1251 * creation of a minimal execution environment first.
1252 */
1253str
1254runMALDebugger(Client cntxt, MalBlkPtr mb)
1255{
1256 str oldprompt= cntxt->prompt;
1257 int oldtrace = cntxt->itrace;
1258 int oldhist = cntxt->curprg->def->keephistory;
1259 str msg;
1260
1261 cntxt->itrace = 'n';
1262 cntxt->curprg->def->keephistory = TRUE;
1263
1264 msg = runMAL(cntxt, mb, 0, 0);
1265
1266 cntxt->curprg->def->keephistory = oldhist;
1267 cntxt->prompt =oldprompt;
1268 cntxt->itrace = oldtrace;
1269 mnstr_printf(cntxt->fdout, "mdb>#EOD\n");
1270 return msg;
1271}
1272
1273#else
1274str runMALDebugger(Client cntxt, MalBlkPtr mb)
1275{
1276 (void)cntxt; (void)mb;
1277 return NULL;
1278}
1279void mdbSetBreakRequest(Client cntxt, MalBlkPtr mb, str request, char cmd) {
1280 (void)cntxt; (void)mb; (void)request; (void)cmd;
1281}
1282#endif
1283