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 | |
24 | typedef 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 | |
35 | typedef 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 | */ |
57 | static void |
58 | printStackHdr(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 | |
70 | static void |
71 | printBATproperties(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 | |
83 | static void |
84 | printBATelm(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 | |
124 | static void |
125 | printStackElm(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 | |
163 | void |
164 | printStack(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 */ |
179 | static void |
180 | printCall(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 | |
190 | static void |
191 | mdbBacktrace(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 | |
200 | void |
201 | mdbDump(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 |
213 | static void |
214 | printBatDetails(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 | |
239 | static void |
240 | printBatInfo(stream *f, VarPtr n, ValPtr v) |
241 | { |
242 | if (isaBatType(n->type) && v->val.ival) |
243 | printBatDetails(f, v->val.ival); |
244 | } |
245 | |
246 | static void |
247 | mdbHelp(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 | */ |
283 | static mdbStateRecord *mdbTable; |
284 | |
285 | bool |
286 | mdbInit(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 | |
302 | void |
303 | mdbExit(void) |
304 | { |
305 | if (mdbTable) { |
306 | GDKfree(mdbTable); |
307 | mdbTable = 0; |
308 | } |
309 | } |
310 | |
311 | static char |
312 | isBreakpoint(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 | */ |
340 | void |
341 | mdbSetBreakRequest(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 */ |
409 | static void |
410 | mdbSetBreakpoint(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 | |
427 | static void |
428 | mdbShowBreakpoints(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 | |
437 | static void |
438 | mdbClrBreakpoint(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 | |
461 | static void |
462 | mdbClrBreakRequest(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 */ |
486 | static void |
487 | printTraceCall(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 | |
512 | static MalBlkPtr |
513 | mdbLocateMalBlk(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 | |
570 | static void |
571 | printBatProperties(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 | |
617 | static void |
618 | mdbCommand(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]) { |
642 | retryRead: |
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 | */ |
1123 | partial: |
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 | |
1181 | static str |
1182 | mdbTrap(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 | |
1193 | void |
1194 | mdbStep(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 | */ |
1253 | str |
1254 | runMALDebugger(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 |
1274 | str runMALDebugger(Client cntxt, MalBlkPtr mb) |
1275 | { |
1276 | (void)cntxt; (void)mb; |
1277 | return NULL; |
1278 | } |
1279 | void mdbSetBreakRequest(Client cntxt, MalBlkPtr mb, str request, char cmd) { |
1280 | (void)cntxt; (void)mb; (void)request; (void)cmd; |
1281 | } |
1282 | #endif |
1283 | |