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 19 : MDBsetDebug(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p)
106 : {
107 19 : int *ret = getArgReference_int(stk, p, 0);
108 19 : int *flg = getArgReference_int(stk, p, 1);
109 :
110 19 : (void) cntxt;
111 19 : (void) mb;
112 19 : (void) stk;
113 19 : (void) p;
114 19 : *ret = (int) GDKgetdebug();
115 19 : GDKsetdebug((unsigned) *flg);
116 19 : 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, const char *name)
241 : {
242 2 : ValPtr v;
243 2 : int i;
244 2 : char *buf = 0;
245 :
246 3 : while (depth > 0 && s) {
247 1 : depth--;
248 1 : s = s->up;
249 : }
250 2 : if (s != 0) {
251 : char namebuf[IDLENGTH];
252 22 : for (i = 0; i < s->stktop; i++, v++) {
253 20 : v = &s->stk[i];
254 20 : if ((v->bat && (buf = ATOMformat(TYPE_int, &v->val.ival)) == NULL) ||
255 40 : (!v->bat && (buf = ATOMformat(v->vtype, VALptr(v))) == NULL) ||
256 40 : BUNappend(b, getVarNameIntoBuffer(mb, i, namebuf), false) != GDK_SUCCEED ||
257 20 : BUNappend(bn, buf, false) != GDK_SUCCEED) {
258 0 : BBPunfix(b->batCacheid);
259 0 : BBPunfix(bn->batCacheid);
260 0 : GDKfree(buf);
261 0 : throw(MAL, name, SQLSTATE(HY013) MAL_MALLOC_FAIL);
262 : }
263 20 : GDKfree(buf);
264 20 : buf = NULL;
265 : }
266 : }
267 : return MAL_SUCCEED;
268 : }
269 :
270 : static str
271 0 : MDBgetStackFrame(Client cntxt, MalBlkPtr m, MalStkPtr s, InstrPtr p)
272 : {
273 0 : bat *ret = getArgReference_bat(s, p, 0);
274 0 : bat *ret2 = getArgReference_bat(s, p, 1);
275 0 : BAT *b = COLnew(0, TYPE_str, 256, TRANSIENT);
276 0 : BAT *bn = COLnew(0, TYPE_str, 256, TRANSIENT);
277 0 : str err;
278 :
279 0 : (void) cntxt;
280 0 : if (b == 0 || bn == 0) {
281 0 : BBPreclaim(b);
282 0 : BBPreclaim(bn);
283 0 : throw(MAL, "mdb.getStackFrame", SQLSTATE(HY013) MAL_MALLOC_FAIL);
284 : }
285 0 : if ((err = MDBgetFrame(b, bn, m, s, 0, "mdb.getStackFrame")) !=MAL_SUCCEED) {
286 0 : BBPreclaim(b);
287 0 : BBPreclaim(bn);
288 0 : return err;
289 : }
290 0 : *ret = b->batCacheid;
291 0 : BBPkeepref(b);
292 0 : *ret2 = bn->batCacheid;
293 0 : BBPkeepref(bn);
294 0 : return MAL_SUCCEED;
295 : }
296 :
297 : static str
298 3 : MDBgetStackFrameN(Client cntxt, MalBlkPtr m, MalStkPtr s, InstrPtr p)
299 : {
300 3 : int n;
301 3 : bat *ret = getArgReference_bat(s, p, 0);
302 3 : bat *ret2 = getArgReference_bat(s, p, 1);
303 3 : BAT *b;
304 3 : BAT *bn;
305 3 : str err;
306 :
307 3 : (void) cntxt;
308 3 : n = *getArgReference_int(s, p, 2);
309 6 : if (n < 0 || n >= getStkDepth(s))
310 1 : throw(MAL, "mdb.getStackFrame", ILLEGAL_ARGUMENT " Illegal depth.");
311 :
312 2 : b = COLnew(0, TYPE_str, 256, TRANSIENT);
313 2 : bn = COLnew(0, TYPE_str, 256, TRANSIENT);
314 2 : if (b == 0 || bn == 0) {
315 0 : BBPreclaim(b);
316 0 : BBPreclaim(bn);
317 0 : throw(MAL, "mdb.getStackFrame", SQLSTATE(HY013) MAL_MALLOC_FAIL);
318 : }
319 :
320 2 : if ((err = MDBgetFrame(b, bn, m, s, n, "mdb.getStackFrameN")) !=MAL_SUCCEED) {
321 0 : BBPreclaim(b);
322 0 : BBPreclaim(bn);
323 0 : return err;
324 : }
325 2 : *ret = b->batCacheid;
326 2 : BBPkeepref(b);
327 2 : *ret2 = bn->batCacheid;
328 2 : BBPkeepref(bn);
329 2 : return MAL_SUCCEED;
330 : }
331 :
332 : static str
333 1 : MDBStkTrace(Client cntxt, MalBlkPtr m, MalStkPtr s, InstrPtr p)
334 : {
335 1 : BAT *b, *bn;
336 1 : str msg;
337 1 : char *buf;
338 1 : bat *ret = getArgReference_bat(s, p, 0);
339 1 : bat *ret2 = getArgReference_bat(s, p, 1);
340 1 : int k = 0;
341 1 : size_t len, l;
342 1 : int pcup;
343 :
344 1 : b = COLnew(0, TYPE_int, 256, TRANSIENT);
345 1 : if (b == NULL)
346 0 : throw(MAL, "mdb.getStackTrace", SQLSTATE(HY013) MAL_MALLOC_FAIL);
347 1 : bn = COLnew(0, TYPE_str, 256, TRANSIENT);
348 1 : if (bn == NULL) {
349 0 : BBPreclaim(b);
350 0 : throw(MAL, "mdb.getStackTrace", SQLSTATE(HY013) MAL_MALLOC_FAIL);
351 : }
352 1 : (void) cntxt;
353 1 : if ((msg = instruction2str(s->blk, s, p, LIST_MAL_DEBUG)) == NULL) {
354 0 : BBPreclaim(b);
355 0 : BBPreclaim(bn);
356 0 : throw(MAL, "mdb.getStackTrace", SQLSTATE(HY013) MAL_MALLOC_FAIL);
357 : }
358 1 : len = strlen(msg);
359 1 : buf = (char *) GDKmalloc(len + 1024);
360 1 : if (buf == NULL) {
361 0 : GDKfree(msg);
362 0 : BBPreclaim(b);
363 0 : BBPreclaim(bn);
364 0 : throw(MAL, "mdb.setTrace", SQLSTATE(HY013) MAL_MALLOC_FAIL);
365 : }
366 1 : snprintf(buf, len + 1024, "%s at %s.%s[%d]", msg,
367 : getModuleId(getInstrPtr(m, 0)),
368 1 : getFunctionId(getInstrPtr(m, 0)), getPC(m, p));
369 2 : if (BUNappend(b, &k, false) != GDK_SUCCEED ||
370 1 : BUNappend(bn, buf, false) != GDK_SUCCEED) {
371 0 : GDKfree(msg);
372 0 : GDKfree(buf);
373 0 : BBPreclaim(b);
374 0 : BBPreclaim(bn);
375 0 : throw(MAL, "mdb.setTrace", SQLSTATE(HY013) MAL_MALLOC_FAIL);
376 : }
377 1 : GDKfree(msg);
378 :
379 2 : for (pcup = s->pcup, s = s->up, k++; s != NULL;
380 1 : pcup = s->pcup, s = s->up, k++) {
381 1 : if ((msg = instruction2str(s->blk, s, getInstrPtr(s->blk, pcup),
382 : LIST_MAL_DEBUG)) == NULL) {
383 0 : BBPunfix(b->batCacheid);
384 0 : BBPunfix(bn->batCacheid);
385 0 : throw(MAL, "mdb.setTrace", SQLSTATE(HY013) MAL_MALLOC_FAIL);
386 : }
387 1 : l = strlen(msg);
388 1 : if (l > len) {
389 0 : GDKfree(buf);
390 0 : len = l;
391 0 : buf = (char *) GDKmalloc(len + 1024);
392 0 : if (buf == NULL) {
393 0 : GDKfree(msg);
394 0 : BBPunfix(b->batCacheid);
395 0 : BBPunfix(bn->batCacheid);
396 0 : throw(MAL, "mdb.setTrace", SQLSTATE(HY013) MAL_MALLOC_FAIL);
397 : }
398 : }
399 1 : snprintf(buf, len + 1024, "%s at %s.%s[%d]", msg,
400 : getModuleId(getInstrPtr(s->blk, 0)),
401 1 : getFunctionId(getInstrPtr(s->blk, 0)), pcup);
402 2 : if (BUNappend(b, &k, false) != GDK_SUCCEED ||
403 1 : BUNappend(bn, buf, false) != GDK_SUCCEED) {
404 0 : GDKfree(buf);
405 0 : GDKfree(msg);
406 0 : BBPunfix(b->batCacheid);
407 0 : BBPunfix(bn->batCacheid);
408 0 : throw(MAL, "mdb.setTrace", SQLSTATE(HY013) MAL_MALLOC_FAIL);
409 : }
410 1 : GDKfree(msg);
411 : }
412 1 : GDKfree(buf);
413 1 : *ret = b->batCacheid;
414 1 : BBPkeepref(b);
415 1 : *ret2 = bn->batCacheid;
416 1 : BBPkeepref(bn);
417 1 : return MAL_SUCCEED;
418 : }
419 :
420 : /*
421 : * Display routines
422 : */
423 : static str
424 1 : MDBlist(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p)
425 : {
426 1 : (void) p;
427 1 : (void) stk;
428 1 : printFunction(cntxt->fdout, mb, 0, LIST_MAL_NAME);
429 1 : return MAL_SUCCEED;
430 : }
431 :
432 : static str
433 0 : MDBlistMapi(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p)
434 : {
435 0 : (void) p;
436 0 : (void) stk;
437 0 : printFunction(cntxt->fdout, mb, 0, LIST_MAL_ALL);
438 0 : return MAL_SUCCEED;
439 : }
440 :
441 : static str
442 2 : MDBlist3(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p)
443 : {
444 2 : str modnme = *getArgReference_str(stk, p, 1);
445 2 : str fcnnme = *getArgReference_str(stk, p, 2);
446 2 : Symbol s = NULL;
447 :
448 2 : s = findSymbol(cntxt->usermodule, putName(modnme), putName(fcnnme));
449 2 : if (s == NULL)
450 0 : throw(MAL, "mdb.list", "Could not find %s.%s", modnme, fcnnme);
451 2 : printFunction(cntxt->fdout, s->def, 0, LIST_MAL_NAME);
452 2 : (void) mb; /* fool compiler */
453 2 : return MAL_SUCCEED;
454 : }
455 :
456 : static str
457 0 : MDBlistDetail(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p)
458 : {
459 0 : (void) p;
460 0 : (void) stk;
461 0 : printFunction(cntxt->fdout, mb, 0, LIST_MAL_DEBUG);
462 0 : return MAL_SUCCEED;
463 : }
464 :
465 : static str
466 1 : MDBlist3Detail(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p)
467 : {
468 1 : str modnme = *getArgReference_str(stk, p, 1);
469 1 : str fcnnme = *getArgReference_str(stk, p, 2);
470 1 : Symbol s = NULL;
471 :
472 1 : s = findSymbol(cntxt->usermodule, putName(modnme), putName(fcnnme));
473 1 : if (s == NULL)
474 0 : throw(MAL, "mdb.list", "Could not find %s.%s", modnme, fcnnme);
475 1 : printFunction(cntxt->fdout, s->def, 0, LIST_MAL_DEBUG);
476 1 : (void) mb; /* fool compiler */
477 1 : return NULL;
478 : }
479 :
480 : /* Utilities
481 : * Dumping a stack on a file is primarilly used for debugging.
482 : * Printing the stack requires access to both the symbol table and
483 : * the stackframes in most cases.
484 : * Beware that a stack frame need not be initialized with null values.
485 : * It has been zeroed upon creation.
486 : *
487 : * The routine can also be used to inspect the symbol table of
488 : * arbitrary functions.
489 : */
490 : static void
491 0 : printStackHdr(stream *f, MalBlkPtr mb, const ValRecord *v, int index)
492 : {
493 0 : char name[IDLENGTH] = { 0 };
494 0 : if (v == 0 && isVarConstant(mb, index))
495 0 : v = &getVarConstant(mb, index);
496 0 : mnstr_printf(f, "#[%2d] %5s", index, getVarNameIntoBuffer(mb, index, name));
497 0 : mnstr_printf(f, " (%d,%d,%d) = ", getBeginScope(mb, index),
498 0 : getLastUpdate(mb, index), getEndScope(mb, index));
499 0 : if (v)
500 0 : ATOMprint(v->vtype, VALptr(v), f);
501 0 : }
502 :
503 : static void
504 0 : printBATproperties(stream *f, BAT *b)
505 : {
506 0 : mnstr_printf(f, " count=" BUNFMT " lrefs=%d ",
507 0 : BATcount(b), BBP_lrefs(b->batCacheid));
508 0 : if (BBP_refs(b->batCacheid) - 1)
509 0 : mnstr_printf(f, " refs=%d ", BBP_refs(b->batCacheid));
510 0 : if (b->theap->refs)
511 0 : mnstr_printf(f, " views=%llu",
512 0 : (unsigned long long) ATOMIC_GET(&b->theap->refs));
513 0 : if (b->tvheap->refs)
514 0 : mnstr_printf(f, " shared vheaps=%llu",
515 0 : (unsigned long long) ATOMIC_GET(&b->tvheap->refs));
516 0 : if (b->theap->parentid != b->batCacheid)
517 0 : mnstr_printf(f, "view on %s ", BBP_logical(b->theap->parentid));
518 0 : }
519 :
520 : static void
521 0 : printBATelm(stream *f, bat i, BUN cnt, BUN first)
522 : {
523 0 : BAT *b, *bs = NULL;
524 0 : str tpe;
525 :
526 0 : b = BATdescriptor(i);
527 0 : if (b) {
528 0 : tpe = getTypeName(newBatType(b->ttype));
529 0 : mnstr_printf(f, ":%s ", tpe);
530 0 : GDKfree(tpe);
531 0 : printBATproperties(f, b);
532 : /* perform property checking */
533 0 : BATassertProps(b);
534 0 : mnstr_printf(f, "\n");
535 0 : if (cnt && BATcount(b) > 0) {
536 0 : if (cnt < BATcount(b)) {
537 0 : mnstr_printf(f, "Sample " BUNFMT " out of " BUNFMT "\n", cnt,
538 : BATcount(b));
539 : }
540 : /* cut out a portion of the BAT for display */
541 0 : bs = BATslice(b, first, first + cnt);
542 : /* get the void values */
543 0 : if (bs == NULL)
544 0 : mnstr_printf(f, "Failed to take chunk\n");
545 : else {
546 0 : if (BATprint(f, bs) != GDK_SUCCEED)
547 0 : mnstr_printf(f, "Failed to print chunk\n");
548 0 : BBPunfix(bs->batCacheid);
549 : }
550 : }
551 :
552 0 : BBPunfix(b->batCacheid);
553 : } else
554 0 : mnstr_printf(f, "\n");
555 0 : }
556 :
557 : static void
558 0 : printStackElm(stream *f, MalBlkPtr mb, const ValRecord *v, int index, BUN cnt, BUN first)
559 : {
560 0 : str nme, nmeOnStk;
561 0 : VarPtr n = getVar(mb, index);
562 :
563 0 : printStackHdr(f, mb, v, index);
564 :
565 0 : if (v && v->bat) {
566 0 : bat i = v->val.bval;
567 0 : BAT *b = BBPquickdesc(i);
568 :
569 0 : if (b) {
570 0 : nme = getTypeName(newBatType(b->ttype));
571 0 : mnstr_printf(f, " :%s rows=" BUNFMT, nme, BATcount(b));
572 : } else {
573 0 : nme = getTypeName(n->type);
574 0 : mnstr_printf(f, " :%s", nme);
575 : }
576 : } else {
577 0 : nme = getTypeName(n->type);
578 0 : mnstr_printf(f, " :%s", nme);
579 : }
580 0 : nmeOnStk = v ? getTypeName(v->vtype) : GDKstrdup(nme);
581 : /* check for type errors */
582 0 : if (nmeOnStk && strcmp(nmeOnStk, nme) && strncmp(nmeOnStk, "BAT", 3))
583 0 : mnstr_printf(f, "!%s ", nmeOnStk);
584 0 : mnstr_printf(f, " %s", (isVarConstant(mb, index) ? " constant" : ""));
585 0 : mnstr_printf(f, " %s", (isVarTypedef(mb, index) ? " type variable" : ""));
586 0 : GDKfree(nme);
587 0 : mnstr_printf(f, "\n");
588 0 : GDKfree(nmeOnStk);
589 :
590 0 : if (cnt && v && (isaBatType(n->type) || v->bat) && !is_bat_nil(v->val.bval)) {
591 0 : printBATelm(f, v->val.bval, cnt, first);
592 : }
593 0 : }
594 :
595 : static void
596 0 : printStack(stream *f, MalBlkPtr mb, MalStkPtr s)
597 : {
598 0 : int i = 0;
599 :
600 0 : setVariableScope(mb);
601 0 : if (s) {
602 0 : mnstr_printf(f, "#Stack '%s' size=%d top=%d\n",
603 0 : getInstrPtr(mb, 0)->fcnname, s->stksize, s->stktop);
604 0 : for (; i < mb->vtop; i++)
605 0 : printStackElm(f, mb, s->stk + i, i, 0, 0);
606 : } else
607 0 : for (; i < mb->vtop; i++)
608 0 : printStackElm(f, mb, 0, i, 0, 0);
609 0 : }
610 :
611 : static str
612 0 : MDBvar(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p)
613 : {
614 0 : (void) p;
615 0 : (void) stk;
616 0 : (void) cntxt;
617 0 : (void) mb;
618 0 : printStack(cntxt->fdout, mb, stk);
619 0 : return MAL_SUCCEED;
620 : }
621 :
622 : static str
623 0 : MDBvar3(Client cntxt, MalBlkPtr mb, MalStkPtr stk, InstrPtr p)
624 : {
625 0 : str modnme = *getArgReference_str(stk, p, 1);
626 0 : str fcnnme = *getArgReference_str(stk, p, 2);
627 0 : Symbol s = NULL;
628 :
629 0 : s = findSymbol(cntxt->usermodule, putName(modnme), putName(fcnnme));
630 0 : if (s == NULL)
631 0 : throw(MAL, "mdb.var", "Could not find %s.%s", modnme, fcnnme);
632 0 : (void) cntxt;
633 0 : (void) mb;
634 0 : printStack(cntxt->fdout, s->def, (s->def == mb ? stk : 0));
635 0 : (void) mb;
636 0 : return NULL;
637 : }
638 :
639 : /*
640 : * It is illustrative to dump the code when you
641 : * have encountered an error.
642 : */
643 : static str
644 1 : MDBgetDefinition(Client cntxt, MalBlkPtr m, MalStkPtr stk, InstrPtr p)
645 : {
646 1 : int i;
647 1 : bat *ret = getArgReference_bat(stk, p, 0);
648 1 : str ps;
649 1 : BAT *b = COLnew(0, TYPE_str, 256, TRANSIENT);
650 :
651 1 : (void) cntxt;
652 1 : if (b == 0)
653 0 : throw(MAL, "mdb.getDefinition", SQLSTATE(HY013) MAL_MALLOC_FAIL);
654 :
655 7 : for (i = 0; i < m->stop; i++) {
656 6 : if ((ps = instruction2str(m, 0, getInstrPtr(m, i), 1)) == NULL) {
657 0 : BBPreclaim(b);
658 0 : throw(MAL, "mdb.getDefinition", SQLSTATE(HY013) MAL_MALLOC_FAIL);
659 : }
660 6 : if (BUNappend(b, ps, false) != GDK_SUCCEED) {
661 0 : GDKfree(ps);
662 0 : BBPreclaim(b);
663 0 : throw(MAL, "mdb.getDefinition", SQLSTATE(HY013) MAL_MALLOC_FAIL);
664 : }
665 6 : GDKfree(ps);
666 : }
667 1 : *ret = b->batCacheid;
668 1 : BBPkeepref(b);
669 :
670 1 : return MAL_SUCCEED;
671 : }
672 :
673 : static str
674 0 : MDBgetExceptionVariable(str *ret, str *msg)
675 : {
676 0 : str tail;
677 :
678 0 : tail = strchr(*msg, ':');
679 0 : if (tail == 0)
680 0 : throw(MAL, "mdb.getExceptionVariable",
681 : OPERATION_FAILED " ':'<name> missing");
682 :
683 0 : *tail = 0;
684 0 : *ret = GDKstrdup(*msg);
685 0 : if (*ret == NULL)
686 0 : throw(MAL, "mdb.getExceptionVariable", SQLSTATE(HY013) MAL_MALLOC_FAIL);
687 0 : *tail = ':';
688 0 : return MAL_SUCCEED;
689 : }
690 :
691 : static str
692 0 : MDBgetExceptionContext(str *ret, str *msg)
693 : {
694 0 : str tail, tail2;
695 :
696 0 : tail = strchr(*msg, ':');
697 0 : if (tail == 0)
698 0 : throw(MAL, "mdb.getExceptionContext",
699 : OPERATION_FAILED " ':'<name> missing");
700 0 : tail2 = strchr(tail + 1, ':');
701 0 : if (tail2 == 0)
702 0 : throw(MAL, "mdb.getExceptionContext",
703 : OPERATION_FAILED " <name> missing");
704 :
705 0 : *tail2 = 0;
706 0 : *ret = GDKstrdup(tail + 1);
707 0 : if (*ret == NULL)
708 0 : throw(MAL, "mdb.getExceptionContext", SQLSTATE(HY013) MAL_MALLOC_FAIL);
709 0 : *tail2 = ':';
710 0 : return MAL_SUCCEED;
711 : }
712 :
713 : static str
714 0 : MDBgetExceptionReason(str *ret, str *msg)
715 : {
716 0 : str tail;
717 :
718 0 : tail = strchr(*msg, ':');
719 0 : if (tail == 0)
720 0 : throw(MAL, "mdb.getExceptionReason", OPERATION_FAILED " '::' missing");
721 0 : tail = strchr(tail + 1, ':');
722 0 : if (tail == 0)
723 0 : throw(MAL, "mdb.getExceptionReason", OPERATION_FAILED " ':' missing");
724 :
725 0 : *ret = GDKstrdup(tail + 1);
726 0 : if (*ret == NULL)
727 0 : throw(MAL, "mdb.getExceptionReason", SQLSTATE(HY013) MAL_MALLOC_FAIL);
728 : return MAL_SUCCEED;
729 : }
730 :
731 : static str
732 6 : MDBdummy(void *ret)
733 : {
734 6 : (void) ret;
735 6 : throw(MAL, "mdb.dummy", OPERATION_FAILED);
736 : }
737 :
738 :
739 : static str
740 0 : CMDmodules(bat *bid)
741 : {
742 0 : BAT *b = getModules();
743 :
744 0 : if (b == NULL)
745 0 : throw(MAL, "mdb.modules", SQLSTATE(HY013) MAL_MALLOC_FAIL);
746 0 : *bid = b->batCacheid;
747 0 : BBPkeepref(b);
748 0 : return MAL_SUCCEED;
749 : }
750 :
751 : #include "mel.h"
752 : mel_func mdb_init_funcs[] = {
753 : command("mdb", "modules", CMDmodules, false, "List available modules", args(1,1, batarg("",str))),
754 : pattern("mdb", "getVMsize", MDBgetVMsize, false, "Retrieve the max VM size", args(1,1, arg("",lng))),
755 : pattern("mdb", "setVMsize", MDBsetVMsize, false, "Manipulate the VM max size in MBs", args(1,2, arg("",lng),arg("l",lng))),
756 : pattern("mdb", "getDebugFlags", MDBgetDebugFlags, false, "Get the kernel debugging flags bit-set", args(2,2, batarg("flg",str),batarg("val",bit))),
757 : pattern("mdb", "getDebug", MDBgetDebug, false, "Get the kernel debugging bit-set.\nSee the MonetDB configuration file for details", args(1,1, arg("",int))),
758 : 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))),
759 : pattern("mdb", "setDebug", MDBsetDebug, false, "Set the kernel debugging bit-set and return its previous value.", args(1,2, arg("",int),arg("flg",int))),
760 : command("mdb", "getException", MDBgetExceptionVariable, false, "Extract the variable name from the exception message", args(1,2, arg("",str),arg("s",str))),
761 : command("mdb", "getReason", MDBgetExceptionReason, false, "Extract the reason from the exception message", args(1,2, arg("",str),arg("s",str))),
762 : command("mdb", "getContext", MDBgetExceptionContext, false, "Extract the context string from the exception message", args(1,2, arg("",str),arg("s",str))),
763 : pattern("mdb", "list", MDBlist, false, "Dump the current routine on standard out.", args(1,1, arg("",void))),
764 : pattern("mdb", "listMapi", MDBlistMapi, false, "Dump the current routine on standard out with Mapi prefix.", args(1,1, arg("",void))),
765 : pattern("mdb", "list", MDBlist3, false, "Dump the routine M.F on standard out.", args(1,3, arg("",void),arg("M",str),arg("F",str))),
766 : pattern("mdb", "List", MDBlistDetail, false, "Dump the current routine on standard out.", args(1,1, arg("",void))),
767 : pattern("mdb", "List", MDBlist3Detail, false, "Dump the routine M.F on standard out.", args(1,3, arg("",void),arg("M",str),arg("F",str))),
768 : pattern("mdb", "var", MDBvar, false, "Dump the symboltable of current routine on standard out.", args(1,1, arg("",void))),
769 : 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))),
770 : pattern("mdb", "getStackDepth", MDBStkDepth, false, "Return the depth of the calling stack.", args(1,1, arg("",int))),
771 : pattern("mdb", "getStackFrame", MDBgetStackFrameN, false, "", args(2,3, batarg("",str),batarg("",str),arg("i",int))),
772 : pattern("mdb", "getStackFrame", MDBgetStackFrame, false, "Collect variable binding of current (n-th) stack frame.", args(2,2, batarg("",str),batarg("",str))),
773 : pattern("mdb", "getStackTrace", MDBStkTrace, false, "", args(2,2, batarg("",int),batarg("",str))),
774 : pattern("mdb", "getDefinition", MDBgetDefinition, false, "Returns a string representation of the current function \nwith typing information attached", args(1,1, batarg("",str))),
775 : command("mdb", "#dummy", MDBdummy, false, "Dummy function for testing", args(1,1, arg("",void))),
776 : { .imp=NULL }
777 : };
778 : #include "mal_import.h"
779 : #ifdef _MSC_VER
780 : #undef read
781 : #pragma section(".CRT$XCU",read)
782 : #endif
783 334 : LIB_STARTUP_FUNC(init_mdb_mal)
784 334 : { mal_module("mdb", NULL, mdb_init_funcs); }
|