Line data Source code
1 : /*
2 : * SPDX-License-Identifier: MPL-2.0
3 : *
4 : * This Source Code Form is subject to the terms of the Mozilla Public
5 : * License, v. 2.0. If a copy of the MPL was not distributed with this
6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 : *
8 : * Copyright 2024 MonetDB Foundation;
9 : * Copyright August 2008 - 2023 MonetDB B.V.;
10 : * Copyright 1997 - July 2008 CWI.
11 : */
12 :
13 : /*
14 : * author Martin Kersten
15 : * MAL debugger interface
16 : * This module provides access to the functionality offered
17 : * by the MonetDB debugger and interpreter status.
18 : * It is primarilly used in interactive sessions to activate
19 : * the debugger at a given point. Furthermore, the instructions
20 : * provide the necessary handle to generate information
21 : * for post-mortum analysis.
22 : *
23 : * To enable ease of debugging and performance monitoring, the MAL interpreter
24 : * comes with a hardwired gdb-like text-based debugger.
25 : * A limited set of instructions can be included in the programs themselves,
26 : * but beware that debugging has a global effect. Any concurrent user
27 : * will be affected by breakpoints being set.
28 : *
29 : * The prime scheme to inspect the MAL interpreter status is to use
30 : * the MAL debugger directly. However, in case of automatic exception handling
31 : * it helps to be able to obtain BAT versions of the critical information,
32 : * such as stack frame table, stack trace,
33 : * and the instruction(s) where an exception occurred.
34 : * The inspection typically occurs in the exception handling part of the
35 : * MAL block.
36 : *
37 : * Beware, a large class of internal errors can not easily captured this way.
38 : * For example, bus-errors and segmentation faults lead to premature
39 : * termination of the process. Similar, creation of the post-mortum
40 : * information may fail due to an inconsistent state or insufficient resources.
41 : */
42 :
43 : #include "monetdb_config.h"
44 : #include "gdk.h"
45 : #include "mutils.h"
46 : #include <time.h>
47 : #include <sys/types.h>
48 : #ifdef HAVE_DIRENT_H
49 : #include <dirent.h>
50 : #endif
51 : #include "mal_resolve.h"
52 : #include "mal_linker.h"
53 : #include "mal_client.h"
54 : #include "mal_exception.h"
55 : #include "mal_interpreter.h"
56 : #include "mal_namespace.h"
57 : #include "mal_authorize.h"
58 : #include "mal_function.h"
59 :
60 : #define MDBstatus(X) \
61 : if( stk->cmd && X==0 ) \
62 : mnstr_printf(cntxt->fdout,"#Monet Debugger off\n"); \
63 : else if(stk->cmd==0 && X) \
64 : mnstr_printf(cntxt->fdout,"#Monet Debugger on\n");
65 :
66 : static str
67 0 : MDBgetVMsize(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p)
68 : {
69 0 : lng *ret = getArgReference_lng(stk, p, 0);
70 :
71 0 : (void) cntxt;
72 0 : (void) mb; /* still unused */
73 0 : *ret = (lng) GDK_vm_maxsize / 1024 / 1024;
74 0 : return MAL_SUCCEED;
75 : }
76 :
77 : /* Set the max VM in MBs */
78 : static str
79 0 : MDBsetVMsize(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p)
80 : {
81 0 : lng *ret = getArgReference_lng(stk, p, 0);
82 :
83 0 : (void) cntxt;
84 0 : (void) mb; /* still unused */
85 0 : *ret = (lng) GDK_vm_maxsize;
86 0 : if (*getArgReference_lng(stk, p, 1) > 1024)
87 0 : GDK_vm_maxsize = (size_t) (*getArgReference_lng(stk, p, 1) * 1024 * 1024);
88 0 : return MAL_SUCCEED;
89 : }
90 :
91 : static str
92 8 : MDBgetDebug(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p)
93 : {
94 8 : int *ret = getArgReference_int(stk, p, 0);
95 :
96 8 : (void) cntxt;
97 8 : (void) mb;
98 8 : (void) stk;
99 8 : (void) p;
100 8 : *ret = (int) ATOMIC_GET(&GDKdebug);
101 8 : return MAL_SUCCEED;
102 : }
103 :
104 : static str
105 20 : MDBsetDebug(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p)
106 : {
107 20 : int *ret = getArgReference_int(stk, p, 0);
108 20 : int *flg = getArgReference_int(stk, p, 1);
109 :
110 20 : (void) cntxt;
111 20 : (void) mb;
112 20 : (void) stk;
113 20 : (void) p;
114 20 : *ret = (int) GDKgetdebug();
115 20 : GDKsetdebug((unsigned) *flg);
116 20 : return MAL_SUCCEED;
117 : }
118 :
119 : #define addFlag(NME, FLG, DSET) \
120 : state = (DSET & FLG) > 0;\
121 : if (BUNappend(flg, (void*) NME, false) != GDK_SUCCEED) goto bailout;\
122 : if (BUNappend(val, &state, false) != GDK_SUCCEED) goto bailout;
123 :
124 : static str
125 0 : MDBgetDebugFlags(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p)
126 : {
127 0 : bat *f = getArgReference_bat(stk, p, 0);
128 0 : bat *v = getArgReference_bat(stk, p, 1);
129 0 : BAT *flg, *val;
130 0 : bit state = 0;
131 :
132 0 : (void) cntxt;
133 0 : (void) mb;
134 :
135 0 : flg = COLnew(0, TYPE_str, 256, TRANSIENT);
136 0 : val = COLnew(0, TYPE_bit, 256, TRANSIENT);
137 :
138 0 : if (flg == NULL || val == NULL) {
139 0 : BBPreclaim(flg);
140 0 : BBPreclaim(val);
141 0 : throw(MAL, "mdb.getDebugFlags", SQLSTATE(HY013) MAL_MALLOC_FAIL);
142 : }
143 0 : ATOMIC_BASE_TYPE dbg = ATOMIC_GET(&GDKdebug);
144 0 : addFlag("threads", GRPthreads, dbg);
145 0 : addFlag("memory", GRPmemory, dbg);
146 0 : addFlag("properties", GRPproperties, dbg);
147 0 : addFlag("io", GRPio, dbg);
148 0 : addFlag("heaps", GRPheaps, dbg);
149 0 : addFlag("transactions", GRPtransactions, dbg);
150 0 : addFlag("modules", GRPmodules, dbg);
151 0 : addFlag("algorithms", GRPalgorithms, dbg);
152 0 : addFlag("performance", GRPperformance, dbg);
153 0 : addFlag("forcemito", GRPforcemito, dbg);
154 :
155 0 : *f = flg->batCacheid;
156 0 : BBPkeepref(flg);
157 0 : *v = val->batCacheid;
158 0 : BBPkeepref(val);
159 0 : return MAL_SUCCEED;
160 :
161 0 : bailout:
162 0 : BBPunfix(flg->batCacheid);
163 0 : BBPunfix(val->batCacheid);
164 0 : throw(MAL, "mdb.getDebugFlags", SQLSTATE(HY013) "Failed to append");
165 : }
166 :
167 : /* Toggle the debug flags on/off */
168 : static str
169 0 : MDBsetDebugStr_(int *ret, str *flg)
170 : {
171 0 : ATOMIC_BASE_TYPE debug = ATOMIC_GET(&GDKdebug);
172 0 : if (strcmp("threads", *flg) == 0)
173 0 : debug ^=GRPthreads;
174 0 : else if (strcmp("memory", *flg) == 0)
175 0 : debug ^=GRPmemory;
176 0 : else if (strcmp("properties", *flg) == 0)
177 0 : debug ^=GRPproperties;
178 0 : else if (strcmp("io", *flg) == 0)
179 0 : debug ^=GRPio;
180 0 : else if (strcmp("heaps", *flg) == 0)
181 0 : debug ^=GRPheaps;
182 0 : else if (strcmp("transactions", *flg) == 0)
183 0 : debug ^=GRPtransactions;
184 0 : else if (strcmp("modules", *flg) == 0)
185 0 : debug ^=GRPmodules;
186 0 : else if (strcmp("algorithms", *flg) == 0)
187 0 : debug ^=GRPalgorithms;
188 0 : else if (strcmp("performance", *flg) == 0)
189 0 : debug ^=GRPperformance;
190 0 : else if (strcmp("forcemito", *flg) == 0)
191 0 : debug ^=GRPforcemito;
192 : else
193 0 : throw(MAL, "mdb.setDebugStr", ILLEGAL_ARGUMENT);
194 0 : *ret = (int) GDKgetdebug();
195 0 : GDKsetdebug((unsigned) debug);
196 :
197 0 : return MAL_SUCCEED;
198 : }
199 :
200 : static str
201 0 : MDBsetDebugStr(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p)
202 : {
203 0 : str *flg = (str *) getArgReference(stk, p, 1);
204 0 : int *ret = (int *) getArgReference(stk, p, 0);
205 :
206 0 : (void) cntxt;
207 0 : (void) mb;
208 0 : return MDBsetDebugStr_(ret, flg);
209 : }
210 :
211 : /*
212 : * Variables and stack information
213 : * The variable information can be turned into a BAT for inspection as well.
214 : */
215 :
216 : static int
217 : getStkDepth(MalStkPtr s)
218 : {
219 : int i = 0;
220 :
221 12 : while (s != 0) {
222 8 : i++;
223 8 : s = s->up;
224 : }
225 4 : return i;
226 : }
227 :
228 : static str
229 1 : MDBStkDepth(Client cntxt, MalBlkPtr mb, MalStkPtr s, InstrPtr p)
230 : {
231 1 : int *ret = getArgReference_int(s, p, 0);
232 :
233 : (void) cntxt;
234 : (void) mb; /* fool compiler */
235 1 : *ret = getStkDepth(s);
236 1 : return MAL_SUCCEED;
237 : }
238 :
239 : static str
240 2 : MDBgetFrame(BAT *b, BAT *bn, MalBlkPtr mb, MalStkPtr s, int depth,
241 : const char *name)
242 : {
243 2 : ValPtr v;
244 2 : int i;
245 2 : char *buf = 0;
246 :
247 3 : while (depth > 0 && s) {
248 1 : depth--;
249 1 : s = s->up;
250 : }
251 2 : if (s != 0)
252 22 : for (i = 0; i < s->stktop; i++, v++) {
253 20 : v = &s->stk[i];
254 40 : if ((buf = ATOMformat(v->vtype, VALptr(v))) == NULL ||
255 40 : BUNappend(b, getVarName(mb, i), false) != GDK_SUCCEED ||
256 20 : BUNappend(bn, buf, false) != GDK_SUCCEED) {
257 0 : BBPunfix(b->batCacheid);
258 0 : BBPunfix(bn->batCacheid);
259 0 : GDKfree(buf);
260 0 : throw(MAL, name, SQLSTATE(HY013) MAL_MALLOC_FAIL);
261 : }
262 20 : GDKfree(buf);
263 20 : buf = NULL;
264 : }
265 : return MAL_SUCCEED;
266 : }
267 :
268 : static str
269 0 : MDBgetStackFrame(Client cntxt, MalBlkPtr m, MalStkPtr s, InstrPtr p)
270 : {
271 0 : bat *ret = getArgReference_bat(s, p, 0);
272 0 : bat *ret2 = getArgReference_bat(s, p, 1);
273 0 : BAT *b = COLnew(0, TYPE_str, 256, TRANSIENT);
274 0 : BAT *bn = COLnew(0, TYPE_str, 256, TRANSIENT);
275 0 : str err;
276 :
277 0 : (void) cntxt;
278 0 : if (b == 0 || bn == 0) {
279 0 : BBPreclaim(b);
280 0 : BBPreclaim(bn);
281 0 : throw(MAL, "mdb.getStackFrame", SQLSTATE(HY013) MAL_MALLOC_FAIL);
282 : }
283 0 : if ((err = MDBgetFrame(b, bn, m, s, 0, "mdb.getStackFrame")) !=MAL_SUCCEED) {
284 0 : BBPreclaim(b);
285 0 : BBPreclaim(bn);
286 0 : return err;
287 : }
288 0 : *ret = b->batCacheid;
289 0 : BBPkeepref(b);
290 0 : *ret2 = bn->batCacheid;
291 0 : BBPkeepref(bn);
292 0 : return MAL_SUCCEED;
293 : }
294 :
295 : static str
296 3 : MDBgetStackFrameN(Client cntxt, MalBlkPtr m, MalStkPtr s, InstrPtr p)
297 : {
298 3 : int n;
299 3 : bat *ret = getArgReference_bat(s, p, 0);
300 3 : bat *ret2 = getArgReference_bat(s, p, 1);
301 3 : BAT *b;
302 3 : BAT *bn;
303 3 : str err;
304 :
305 3 : (void) cntxt;
306 3 : n = *getArgReference_int(s, p, 2);
307 6 : if (n < 0 || n >= getStkDepth(s))
308 1 : throw(MAL, "mdb.getStackFrame", ILLEGAL_ARGUMENT " Illegal depth.");
309 :
310 2 : b = COLnew(0, TYPE_str, 256, TRANSIENT);
311 2 : bn = COLnew(0, TYPE_str, 256, TRANSIENT);
312 2 : if (b == 0 || bn == 0) {
313 0 : BBPreclaim(b);
314 0 : BBPreclaim(bn);
315 0 : throw(MAL, "mdb.getStackFrame", SQLSTATE(HY013) MAL_MALLOC_FAIL);
316 : }
317 :
318 2 : if ((err = MDBgetFrame(b, bn, m, s, n, "mdb.getStackFrameN")) !=MAL_SUCCEED) {
319 0 : BBPreclaim(b);
320 0 : BBPreclaim(bn);
321 0 : return err;
322 : }
323 2 : *ret = b->batCacheid;
324 2 : BBPkeepref(b);
325 2 : *ret2 = bn->batCacheid;
326 2 : BBPkeepref(bn);
327 2 : return MAL_SUCCEED;
328 : }
329 :
330 : static str
331 1 : MDBStkTrace(Client cntxt, MalBlkPtr m, MalStkPtr s, InstrPtr p)
332 : {
333 1 : BAT *b, *bn;
334 1 : str msg;
335 1 : char *buf;
336 1 : bat *ret = getArgReference_bat(s, p, 0);
337 1 : bat *ret2 = getArgReference_bat(s, p, 1);
338 1 : int k = 0;
339 1 : size_t len, l;
340 1 : int pcup;
341 :
342 1 : b = COLnew(0, TYPE_int, 256, TRANSIENT);
343 1 : if (b == NULL)
344 0 : throw(MAL, "mdb.getStackTrace", SQLSTATE(HY013) MAL_MALLOC_FAIL);
345 1 : bn = COLnew(0, TYPE_str, 256, TRANSIENT);
346 1 : if (bn == NULL) {
347 0 : BBPreclaim(b);
348 0 : throw(MAL, "mdb.getStackTrace", SQLSTATE(HY013) MAL_MALLOC_FAIL);
349 : }
350 1 : (void) cntxt;
351 1 : if ((msg = instruction2str(s->blk, s, p, LIST_MAL_DEBUG)) == NULL) {
352 0 : BBPreclaim(b);
353 0 : BBPreclaim(bn);
354 0 : throw(MAL, "mdb.getStackTrace", SQLSTATE(HY013) MAL_MALLOC_FAIL);
355 : }
356 1 : len = strlen(msg);
357 1 : buf = (char *) GDKmalloc(len + 1024);
358 1 : if (buf == NULL) {
359 0 : GDKfree(msg);
360 0 : BBPreclaim(b);
361 0 : BBPreclaim(bn);
362 0 : throw(MAL, "mdb.setTrace", SQLSTATE(HY013) MAL_MALLOC_FAIL);
363 : }
364 1 : snprintf(buf, len + 1024, "%s at %s.%s[%d]", msg,
365 : getModuleId(getInstrPtr(m, 0)),
366 1 : getFunctionId(getInstrPtr(m, 0)), getPC(m, p));
367 2 : if (BUNappend(b, &k, false) != GDK_SUCCEED ||
368 1 : BUNappend(bn, buf, false) != GDK_SUCCEED) {
369 0 : GDKfree(msg);
370 0 : GDKfree(buf);
371 0 : BBPreclaim(b);
372 0 : BBPreclaim(bn);
373 0 : throw(MAL, "mdb.setTrace", SQLSTATE(HY013) MAL_MALLOC_FAIL);
374 : }
375 1 : GDKfree(msg);
376 :
377 2 : for (pcup = s->pcup, s = s->up, k++; s != NULL;
378 1 : pcup = s->pcup, s = s->up, k++) {
379 1 : if ((msg = instruction2str(s->blk, s, getInstrPtr(s->blk, pcup),
380 : LIST_MAL_DEBUG)) == NULL) {
381 0 : BBPunfix(b->batCacheid);
382 0 : BBPunfix(bn->batCacheid);
383 0 : throw(MAL, "mdb.setTrace", SQLSTATE(HY013) MAL_MALLOC_FAIL);
384 : }
385 1 : l = strlen(msg);
386 1 : if (l > len) {
387 0 : GDKfree(buf);
388 0 : len = l;
389 0 : buf = (char *) GDKmalloc(len + 1024);
390 0 : if (buf == NULL) {
391 0 : GDKfree(msg);
392 0 : BBPunfix(b->batCacheid);
393 0 : BBPunfix(bn->batCacheid);
394 0 : throw(MAL, "mdb.setTrace", SQLSTATE(HY013) MAL_MALLOC_FAIL);
395 : }
396 : }
397 1 : snprintf(buf, len + 1024, "%s at %s.%s[%d]", msg,
398 : getModuleId(getInstrPtr(s->blk, 0)),
399 1 : getFunctionId(getInstrPtr(s->blk, 0)), pcup);
400 2 : if (BUNappend(b, &k, false) != GDK_SUCCEED ||
401 1 : BUNappend(bn, buf, false) != GDK_SUCCEED) {
402 0 : GDKfree(buf);
403 0 : GDKfree(msg);
404 0 : BBPunfix(b->batCacheid);
405 0 : BBPunfix(bn->batCacheid);
406 0 : throw(MAL, "mdb.setTrace", SQLSTATE(HY013) MAL_MALLOC_FAIL);
407 : }
408 1 : GDKfree(msg);
409 : }
410 1 : GDKfree(buf);
411 1 : *ret = b->batCacheid;
412 1 : BBPkeepref(b);
413 1 : *ret2 = bn->batCacheid;
414 1 : BBPkeepref(bn);
415 1 : return MAL_SUCCEED;
416 : }
417 :
418 : /*
419 : * Display routines
420 : */
421 : static str
422 1 : MDBlist(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p)
423 : {
424 1 : (void) p;
425 1 : (void) stk;
426 1 : printFunction(cntxt->fdout, mb, 0, LIST_MAL_NAME);
427 1 : return MAL_SUCCEED;
428 : }
429 :
430 : static str
431 0 : MDBlistMapi(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p)
432 : {
433 0 : (void) p;
434 0 : (void) stk;
435 0 : printFunction(cntxt->fdout, mb, 0, LIST_MAL_ALL);
436 0 : return MAL_SUCCEED;
437 : }
438 :
439 : static str
440 2 : MDBlist3(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p)
441 : {
442 2 : str modnme = *getArgReference_str(stk, p, 1);
443 2 : str fcnnme = *getArgReference_str(stk, p, 2);
444 2 : Symbol s = NULL;
445 :
446 2 : s = findSymbol(cntxt->usermodule, putName(modnme), putName(fcnnme));
447 2 : if (s == NULL)
448 0 : throw(MAL, "mdb.list", "Could not find %s.%s", modnme, fcnnme);
449 2 : printFunction(cntxt->fdout, s->def, 0, LIST_MAL_NAME);
450 2 : (void) mb; /* fool compiler */
451 2 : return MAL_SUCCEED;
452 : }
453 :
454 : static str
455 0 : MDBlistDetail(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p)
456 : {
457 0 : (void) p;
458 0 : (void) stk;
459 0 : printFunction(cntxt->fdout, mb, 0, LIST_MAL_DEBUG);
460 0 : return MAL_SUCCEED;
461 : }
462 :
463 : static str
464 1 : MDBlist3Detail(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p)
465 : {
466 1 : str modnme = *getArgReference_str(stk, p, 1);
467 1 : str fcnnme = *getArgReference_str(stk, p, 2);
468 1 : Symbol s = NULL;
469 :
470 1 : s = findSymbol(cntxt->usermodule, putName(modnme), putName(fcnnme));
471 1 : if (s == NULL)
472 0 : throw(MAL, "mdb.list", "Could not find %s.%s", modnme, fcnnme);
473 1 : printFunction(cntxt->fdout, s->def, 0, LIST_MAL_DEBUG);
474 1 : (void) mb; /* fool compiler */
475 1 : return NULL;
476 : }
477 :
478 : /* Utilities
479 : * Dumping a stack on a file is primarilly used for debugging.
480 : * Printing the stack requires access to both the symbol table and
481 : * the stackframes in most cases.
482 : * Beware that a stack frame need not be initialized with null values.
483 : * It has been zeroed upon creation.
484 : *
485 : * The routine can also be used to inspect the symbol table of
486 : * arbitrary functions.
487 : */
488 : static void
489 0 : printStackHdr(stream *f, MalBlkPtr mb, ValPtr v, int index)
490 : {
491 0 : if (v == 0 && isVarConstant(mb, index))
492 0 : v = &getVarConstant(mb, index);
493 0 : mnstr_printf(f, "#[%2d] %5s", index, getVarName(mb, index));
494 0 : mnstr_printf(f, " (%d,%d,%d) = ", getBeginScope(mb, index),
495 0 : getLastUpdate(mb, index), getEndScope(mb, index));
496 0 : if (v)
497 0 : ATOMprint(v->vtype, VALptr(v), f);
498 0 : }
499 :
500 : static void
501 0 : printBATproperties(stream *f, BAT *b)
502 : {
503 0 : mnstr_printf(f, " count=" BUNFMT " lrefs=%d ",
504 0 : BATcount(b), BBP_lrefs(b->batCacheid));
505 0 : if (BBP_refs(b->batCacheid) - 1)
506 0 : mnstr_printf(f, " refs=%d ", BBP_refs(b->batCacheid));
507 0 : if (b->theap->refs)
508 0 : mnstr_printf(f, " views=%llu",
509 0 : (unsigned long long) ATOMIC_GET(&b->theap->refs));
510 0 : if (b->tvheap->refs)
511 0 : mnstr_printf(f, " shared vheaps=%llu",
512 0 : (unsigned long long) ATOMIC_GET(&b->tvheap->refs));
513 0 : if (b->theap->parentid != b->batCacheid)
514 0 : mnstr_printf(f, "view on %s ", BBP_logical(b->theap->parentid));
515 0 : }
516 :
517 : static void
518 0 : printBATelm(stream *f, bat i, BUN cnt, BUN first)
519 : {
520 0 : BAT *b, *bs = NULL;
521 0 : str tpe;
522 :
523 0 : b = BATdescriptor(i);
524 0 : if (b) {
525 0 : tpe = getTypeName(newBatType(b->ttype));
526 0 : mnstr_printf(f, ":%s ", tpe);
527 0 : GDKfree(tpe);
528 0 : printBATproperties(f, b);
529 : /* perform property checking */
530 0 : BATassertProps(b);
531 0 : mnstr_printf(f, "\n");
532 0 : if (cnt && BATcount(b) > 0) {
533 0 : if (cnt < BATcount(b)) {
534 0 : mnstr_printf(f, "Sample " BUNFMT " out of " BUNFMT "\n", cnt,
535 : BATcount(b));
536 : }
537 : /* cut out a portion of the BAT for display */
538 0 : bs = BATslice(b, first, first + cnt);
539 : /* get the void values */
540 0 : if (bs == NULL)
541 0 : mnstr_printf(f, "Failed to take chunk\n");
542 : else {
543 0 : if (BATprint(f, bs) != GDK_SUCCEED)
544 0 : mnstr_printf(f, "Failed to print chunk\n");
545 0 : BBPunfix(bs->batCacheid);
546 : }
547 : }
548 :
549 0 : BBPunfix(b->batCacheid);
550 : } else
551 0 : mnstr_printf(f, "\n");
552 0 : }
553 :
554 : static void
555 0 : printStackElm(stream *f, MalBlkPtr mb, ValPtr v, int index, BUN cnt, BUN first)
556 : {
557 0 : str nme, nmeOnStk;
558 0 : VarPtr n = getVar(mb, index);
559 :
560 0 : printStackHdr(f, mb, v, index);
561 :
562 0 : if (v && v->vtype == TYPE_bat) {
563 0 : bat i = v->val.bval;
564 0 : BAT *b = BBPquickdesc(i);
565 :
566 0 : if (b) {
567 0 : nme = getTypeName(newBatType(b->ttype));
568 0 : mnstr_printf(f, " :%s rows=" BUNFMT, nme, BATcount(b));
569 : } else {
570 0 : nme = getTypeName(n->type);
571 0 : mnstr_printf(f, " :%s", nme);
572 : }
573 : } else {
574 0 : nme = getTypeName(n->type);
575 0 : mnstr_printf(f, " :%s", nme);
576 : }
577 0 : nmeOnStk = v ? getTypeName(v->vtype) : GDKstrdup(nme);
578 : /* check for type errors */
579 0 : if (nmeOnStk && strcmp(nmeOnStk, nme) && strncmp(nmeOnStk, "BAT", 3))
580 0 : mnstr_printf(f, "!%s ", nmeOnStk);
581 0 : mnstr_printf(f, " %s", (isVarConstant(mb, index) ? " constant" : ""));
582 0 : mnstr_printf(f, " %s", (isVarTypedef(mb, index) ? " type variable" : ""));
583 0 : GDKfree(nme);
584 0 : mnstr_printf(f, "\n");
585 0 : GDKfree(nmeOnStk);
586 :
587 0 : if (cnt && v && (isaBatType(n->type) || v->vtype == TYPE_bat)
588 0 : && !is_bat_nil(v->val.bval)) {
589 0 : printBATelm(f, v->val.bval, cnt, first);
590 : }
591 0 : }
592 :
593 : static void
594 0 : printStack(stream *f, MalBlkPtr mb, MalStkPtr s)
595 : {
596 0 : int i = 0;
597 :
598 0 : setVariableScope(mb);
599 0 : if (s) {
600 0 : mnstr_printf(f, "#Stack '%s' size=%d top=%d\n",
601 0 : getInstrPtr(mb, 0)->fcnname, s->stksize, s->stktop);
602 0 : for (; i < mb->vtop; i++)
603 0 : printStackElm(f, mb, s->stk + i, i, 0, 0);
604 : } else
605 0 : for (; i < mb->vtop; i++)
606 0 : printStackElm(f, mb, 0, i, 0, 0);
607 0 : }
608 :
609 : static str
610 0 : MDBvar(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p)
611 : {
612 0 : (void) p;
613 0 : (void) stk;
614 0 : (void) cntxt;
615 0 : (void) mb;
616 0 : printStack(cntxt->fdout, mb, stk);
617 0 : return MAL_SUCCEED;
618 : }
619 :
620 : static str
621 0 : MDBvar3(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p)
622 : {
623 0 : str modnme = *getArgReference_str(stk, p, 1);
624 0 : str fcnnme = *getArgReference_str(stk, p, 2);
625 0 : Symbol s = NULL;
626 :
627 0 : s = findSymbol(cntxt->usermodule, putName(modnme), putName(fcnnme));
628 0 : if (s == NULL)
629 0 : throw(MAL, "mdb.var", "Could not find %s.%s", modnme, fcnnme);
630 0 : (void) cntxt;
631 0 : (void) mb;
632 0 : printStack(cntxt->fdout, s->def, (s->def == mb ? stk : 0));
633 0 : (void) mb;
634 0 : return NULL;
635 : }
636 :
637 : /*
638 : * It is illustrative to dump the code when you
639 : * have encountered an error.
640 : */
641 : static str
642 1 : MDBgetDefinition(Client cntxt, MalBlkPtr m, MalStkPtr stk, InstrPtr p)
643 : {
644 1 : int i;
645 1 : bat *ret = getArgReference_bat(stk, p, 0);
646 1 : str ps;
647 1 : BAT *b = COLnew(0, TYPE_str, 256, TRANSIENT);
648 :
649 1 : (void) cntxt;
650 1 : if (b == 0)
651 0 : throw(MAL, "mdb.getDefinition", SQLSTATE(HY013) MAL_MALLOC_FAIL);
652 :
653 7 : for (i = 0; i < m->stop; i++) {
654 6 : if ((ps = instruction2str(m, 0, getInstrPtr(m, i), 1)) == NULL) {
655 0 : BBPreclaim(b);
656 0 : throw(MAL, "mdb.getDefinition", SQLSTATE(HY013) MAL_MALLOC_FAIL);
657 : }
658 6 : if (BUNappend(b, ps, false) != GDK_SUCCEED) {
659 0 : GDKfree(ps);
660 0 : BBPreclaim(b);
661 0 : throw(MAL, "mdb.getDefinition", SQLSTATE(HY013) MAL_MALLOC_FAIL);
662 : }
663 6 : GDKfree(ps);
664 : }
665 1 : *ret = b->batCacheid;
666 1 : BBPkeepref(b);
667 :
668 1 : return MAL_SUCCEED;
669 : }
670 :
671 : static str
672 0 : MDBgetExceptionVariable(str *ret, str *msg)
673 : {
674 0 : str tail;
675 :
676 0 : tail = strchr(*msg, ':');
677 0 : if (tail == 0)
678 0 : throw(MAL, "mdb.getExceptionVariable",
679 : OPERATION_FAILED " ':'<name> missing");
680 :
681 0 : *tail = 0;
682 0 : *ret = GDKstrdup(*msg);
683 0 : if (*ret == NULL)
684 0 : throw(MAL, "mdb.getExceptionVariable", SQLSTATE(HY013) MAL_MALLOC_FAIL);
685 0 : *tail = ':';
686 0 : return MAL_SUCCEED;
687 : }
688 :
689 : static str
690 0 : MDBgetExceptionContext(str *ret, str *msg)
691 : {
692 0 : str tail, tail2;
693 :
694 0 : tail = strchr(*msg, ':');
695 0 : if (tail == 0)
696 0 : throw(MAL, "mdb.getExceptionContext",
697 : OPERATION_FAILED " ':'<name> missing");
698 0 : tail2 = strchr(tail + 1, ':');
699 0 : if (tail2 == 0)
700 0 : throw(MAL, "mdb.getExceptionContext",
701 : OPERATION_FAILED " <name> missing");
702 :
703 0 : *tail2 = 0;
704 0 : *ret = GDKstrdup(tail + 1);
705 0 : if (*ret == NULL)
706 0 : throw(MAL, "mdb.getExceptionContext", SQLSTATE(HY013) MAL_MALLOC_FAIL);
707 0 : *tail2 = ':';
708 0 : return MAL_SUCCEED;
709 : }
710 :
711 : static str
712 0 : MDBgetExceptionReason(str *ret, str *msg)
713 : {
714 0 : str tail;
715 :
716 0 : tail = strchr(*msg, ':');
717 0 : if (tail == 0)
718 0 : throw(MAL, "mdb.getExceptionReason", OPERATION_FAILED " '::' missing");
719 0 : tail = strchr(tail + 1, ':');
720 0 : if (tail == 0)
721 0 : throw(MAL, "mdb.getExceptionReason", OPERATION_FAILED " ':' missing");
722 :
723 0 : *ret = GDKstrdup(tail + 1);
724 0 : if (*ret == NULL)
725 0 : throw(MAL, "mdb.getExceptionReason", SQLSTATE(HY013) MAL_MALLOC_FAIL);
726 : return MAL_SUCCEED;
727 : }
728 :
729 : static str
730 6 : MDBdummy(void *ret)
731 : {
732 6 : (void) ret;
733 6 : throw(MAL, "mdb.dummy", OPERATION_FAILED);
734 : }
735 :
736 :
737 : static str
738 0 : CMDmodules(bat *bid)
739 : {
740 0 : BAT *b = getModules();
741 :
742 0 : if (b == NULL)
743 0 : throw(MAL, "mdb.modules", SQLSTATE(HY013) MAL_MALLOC_FAIL);
744 0 : *bid = b->batCacheid;
745 0 : BBPkeepref(b);
746 0 : return MAL_SUCCEED;
747 : }
748 :
749 : #include "mel.h"
750 : mel_func mdb_init_funcs[] = {
751 : command("mdb", "modules", CMDmodules, false, "List available modules", args(1,1, batarg("",str))),
752 : pattern("mdb", "getVMsize", MDBgetVMsize, false, "Retrieve the max VM size", args(1,1, arg("",lng))),
753 : pattern("mdb", "setVMsize", MDBsetVMsize, false, "Manipulate the VM max size in MBs", args(1,2, arg("",lng),arg("l",lng))),
754 : pattern("mdb", "getDebugFlags", MDBgetDebugFlags, false, "Get the kernel debugging flags bit-set", args(2,2, batarg("flg",str),batarg("val",bit))),
755 : pattern("mdb", "getDebug", MDBgetDebug, false, "Get the kernel debugging bit-set.\nSee the MonetDB configuration file for details", args(1,1, arg("",int))),
756 : pattern("mdb", "setDebug", MDBsetDebugStr, false, "Set the kernel debugging bit-set and return its previous value.\nThe recognized options are: threads, memory, properties,\nio, transactions, modules, algorithms, estimates.", args(1,2, arg("",int),arg("flg",str))),
757 : pattern("mdb", "setDebug", MDBsetDebug, false, "Set the kernel debugging bit-set and return its previous value.", args(1,2, arg("",int),arg("flg",int))),
758 : command("mdb", "getException", MDBgetExceptionVariable, false, "Extract the variable name from the exception message", args(1,2, arg("",str),arg("s",str))),
759 : command("mdb", "getReason", MDBgetExceptionReason, false, "Extract the reason from the exception message", args(1,2, arg("",str),arg("s",str))),
760 : command("mdb", "getContext", MDBgetExceptionContext, false, "Extract the context string from the exception message", args(1,2, arg("",str),arg("s",str))),
761 : pattern("mdb", "list", MDBlist, false, "Dump the current routine on standard out.", args(1,1, arg("",void))),
762 : pattern("mdb", "listMapi", MDBlistMapi, false, "Dump the current routine on standard out with Mapi prefix.", args(1,1, arg("",void))),
763 : pattern("mdb", "list", MDBlist3, false, "Dump the routine M.F on standard out.", args(1,3, arg("",void),arg("M",str),arg("F",str))),
764 : pattern("mdb", "List", MDBlistDetail, false, "Dump the current routine on standard out.", args(1,1, arg("",void))),
765 : pattern("mdb", "List", MDBlist3Detail, false, "Dump the routine M.F on standard out.", args(1,3, arg("",void),arg("M",str),arg("F",str))),
766 : pattern("mdb", "var", MDBvar, false, "Dump the symboltable of current routine on standard out.", args(1,1, arg("",void))),
767 : pattern("mdb", "var", MDBvar3, false, "Dump the symboltable of routine M.F on standard out.", args(1,3, arg("",void),arg("M",str),arg("F",str))),
768 : pattern("mdb", "getStackDepth", MDBStkDepth, false, "Return the depth of the calling stack.", args(1,1, arg("",int))),
769 : pattern("mdb", "getStackFrame", MDBgetStackFrameN, false, "", args(2,3, batarg("",str),batarg("",str),arg("i",int))),
770 : pattern("mdb", "getStackFrame", MDBgetStackFrame, false, "Collect variable binding of current (n-th) stack frame.", args(2,2, batarg("",str),batarg("",str))),
771 : pattern("mdb", "getStackTrace", MDBStkTrace, false, "", args(2,2, batarg("",int),batarg("",str))),
772 : pattern("mdb", "getDefinition", MDBgetDefinition, false, "Returns a string representation of the current function \nwith typing information attached", args(1,1, batarg("",str))),
773 : command("mdb", "#dummy", MDBdummy, false, "Dummy function for testing", args(1,1, arg("",void))),
774 : { .imp=NULL }
775 : };
776 : #include "mal_import.h"
777 : #ifdef _MSC_VER
778 : #undef read
779 : #pragma section(".CRT$XCU",read)
780 : #endif
781 329 : LIB_STARTUP_FUNC(init_mdb_mal)
782 329 : { mal_module("mdb", NULL, mdb_init_funcs); }
|