diff options
Diffstat (limited to '')
-rw-r--r-- | src/lj_debug.c | 170 |
1 files changed, 133 insertions, 37 deletions
diff --git a/src/lj_debug.c b/src/lj_debug.c index 09896462..92c216f9 100644 --- a/src/lj_debug.c +++ b/src/lj_debug.c | |||
@@ -9,11 +9,12 @@ | |||
9 | #include "lj_obj.h" | 9 | #include "lj_obj.h" |
10 | #include "lj_err.h" | 10 | #include "lj_err.h" |
11 | #include "lj_debug.h" | 11 | #include "lj_debug.h" |
12 | #include "lj_str.h" | 12 | #include "lj_buf.h" |
13 | #include "lj_tab.h" | 13 | #include "lj_tab.h" |
14 | #include "lj_state.h" | 14 | #include "lj_state.h" |
15 | #include "lj_frame.h" | 15 | #include "lj_frame.h" |
16 | #include "lj_bc.h" | 16 | #include "lj_bc.h" |
17 | #include "lj_strfmt.h" | ||
17 | #if LJ_HASJIT | 18 | #if LJ_HASJIT |
18 | #include "lj_jit.h" | 19 | #include "lj_jit.h" |
19 | #endif | 20 | #endif |
@@ -27,7 +28,7 @@ cTValue *lj_debug_frame(lua_State *L, int level, int *size) | |||
27 | /* Traverse frames backwards. */ | 28 | /* Traverse frames backwards. */ |
28 | for (nextframe = frame = L->base-1; frame > bot; ) { | 29 | for (nextframe = frame = L->base-1; frame > bot; ) { |
29 | if (frame_gc(frame) == obj2gco(L)) | 30 | if (frame_gc(frame) == obj2gco(L)) |
30 | level++; /* Skip dummy frames. See lj_meta_call(). */ | 31 | level++; /* Skip dummy frames. See lj_err_optype_call(). */ |
31 | if (level-- == 0) { | 32 | if (level-- == 0) { |
32 | *size = (int)(nextframe - frame); | 33 | *size = (int)(nextframe - frame); |
33 | return frame; /* Level found. */ | 34 | return frame; /* Level found. */ |
@@ -140,38 +141,25 @@ static BCLine debug_frameline(lua_State *L, GCfunc *fn, cTValue *nextframe) | |||
140 | 141 | ||
141 | /* -- Variable names ------------------------------------------------------ */ | 142 | /* -- Variable names ------------------------------------------------------ */ |
142 | 143 | ||
143 | /* Read ULEB128 value. */ | ||
144 | static uint32_t debug_read_uleb128(const uint8_t **pp) | ||
145 | { | ||
146 | const uint8_t *p = *pp; | ||
147 | uint32_t v = *p++; | ||
148 | if (LJ_UNLIKELY(v >= 0x80)) { | ||
149 | int sh = 0; | ||
150 | v &= 0x7f; | ||
151 | do { v |= ((*p & 0x7f) << (sh += 7)); } while (*p++ >= 0x80); | ||
152 | } | ||
153 | *pp = p; | ||
154 | return v; | ||
155 | } | ||
156 | |||
157 | /* Get name of a local variable from slot number and PC. */ | 144 | /* Get name of a local variable from slot number and PC. */ |
158 | static const char *debug_varname(const GCproto *pt, BCPos pc, BCReg slot) | 145 | static const char *debug_varname(const GCproto *pt, BCPos pc, BCReg slot) |
159 | { | 146 | { |
160 | const uint8_t *p = proto_varinfo(pt); | 147 | const char *p = (const char *)proto_varinfo(pt); |
161 | if (p) { | 148 | if (p) { |
162 | BCPos lastpc = 0; | 149 | BCPos lastpc = 0; |
163 | for (;;) { | 150 | for (;;) { |
164 | const char *name = (const char *)p; | 151 | const char *name = p; |
165 | uint32_t vn = *p++; | 152 | uint32_t vn = *(const uint8_t *)p; |
166 | BCPos startpc, endpc; | 153 | BCPos startpc, endpc; |
167 | if (vn < VARNAME__MAX) { | 154 | if (vn < VARNAME__MAX) { |
168 | if (vn == VARNAME_END) break; /* End of varinfo. */ | 155 | if (vn == VARNAME_END) break; /* End of varinfo. */ |
169 | } else { | 156 | } else { |
170 | while (*p++) ; /* Skip over variable name string. */ | 157 | do { p++; } while (*(const uint8_t *)p); /* Skip over variable name. */ |
171 | } | 158 | } |
172 | lastpc = startpc = lastpc + debug_read_uleb128(&p); | 159 | p++; |
160 | lastpc = startpc = lastpc + lj_buf_ruleb128(&p); | ||
173 | if (startpc > pc) break; | 161 | if (startpc > pc) break; |
174 | endpc = startpc + debug_read_uleb128(&p); | 162 | endpc = startpc + lj_buf_ruleb128(&p); |
175 | if (pc < endpc && slot-- == 0) { | 163 | if (pc < endpc && slot-- == 0) { |
176 | if (vn < VARNAME__MAX) { | 164 | if (vn < VARNAME__MAX) { |
177 | #define VARNAMESTR(name, str) str "\0" | 165 | #define VARNAMESTR(name, str) str "\0" |
@@ -297,9 +285,9 @@ restart: | |||
297 | } | 285 | } |
298 | 286 | ||
299 | /* Deduce function name from caller of a frame. */ | 287 | /* Deduce function name from caller of a frame. */ |
300 | const char *lj_debug_funcname(lua_State *L, TValue *frame, const char **name) | 288 | const char *lj_debug_funcname(lua_State *L, cTValue *frame, const char **name) |
301 | { | 289 | { |
302 | TValue *pframe; | 290 | cTValue *pframe; |
303 | GCfunc *fn; | 291 | GCfunc *fn; |
304 | BCPos pc; | 292 | BCPos pc; |
305 | if (frame <= tvref(L->stack)) | 293 | if (frame <= tvref(L->stack)) |
@@ -328,7 +316,7 @@ const char *lj_debug_funcname(lua_State *L, TValue *frame, const char **name) | |||
328 | /* -- Source code locations ----------------------------------------------- */ | 316 | /* -- Source code locations ----------------------------------------------- */ |
329 | 317 | ||
330 | /* Generate shortened source name. */ | 318 | /* Generate shortened source name. */ |
331 | void lj_debug_shortname(char *out, GCstr *str) | 319 | void lj_debug_shortname(char *out, GCstr *str, BCLine line) |
332 | { | 320 | { |
333 | const char *src = strdata(str); | 321 | const char *src = strdata(str); |
334 | if (*src == '=') { | 322 | if (*src == '=') { |
@@ -342,11 +330,11 @@ void lj_debug_shortname(char *out, GCstr *str) | |||
342 | *out++ = '.'; *out++ = '.'; *out++ = '.'; | 330 | *out++ = '.'; *out++ = '.'; *out++ = '.'; |
343 | } | 331 | } |
344 | strcpy(out, src); | 332 | strcpy(out, src); |
345 | } else { /* Output [string "string"]. */ | 333 | } else { /* Output [string "string"] or [builtin:name]. */ |
346 | size_t len; /* Length, up to first control char. */ | 334 | size_t len; /* Length, up to first control char. */ |
347 | for (len = 0; len < LUA_IDSIZE-12; len++) | 335 | for (len = 0; len < LUA_IDSIZE-12; len++) |
348 | if (((const unsigned char *)src)[len] < ' ') break; | 336 | if (((const unsigned char *)src)[len] < ' ') break; |
349 | strcpy(out, "[string \""); out += 9; | 337 | strcpy(out, line == ~(BCLine)0 ? "[builtin:" : "[string \""); out += 9; |
350 | if (src[len] != '\0') { /* Must truncate? */ | 338 | if (src[len] != '\0') { /* Must truncate? */ |
351 | if (len > LUA_IDSIZE-15) len = LUA_IDSIZE-15; | 339 | if (len > LUA_IDSIZE-15) len = LUA_IDSIZE-15; |
352 | strncpy(out, src, len); out += len; | 340 | strncpy(out, src, len); out += len; |
@@ -354,7 +342,7 @@ void lj_debug_shortname(char *out, GCstr *str) | |||
354 | } else { | 342 | } else { |
355 | strcpy(out, src); out += len; | 343 | strcpy(out, src); out += len; |
356 | } | 344 | } |
357 | strcpy(out, "\"]"); | 345 | strcpy(out, line == ~(BCLine)0 ? "]" : "\"]"); |
358 | } | 346 | } |
359 | } | 347 | } |
360 | 348 | ||
@@ -367,14 +355,15 @@ void lj_debug_addloc(lua_State *L, const char *msg, | |||
367 | if (isluafunc(fn)) { | 355 | if (isluafunc(fn)) { |
368 | BCLine line = debug_frameline(L, fn, nextframe); | 356 | BCLine line = debug_frameline(L, fn, nextframe); |
369 | if (line >= 0) { | 357 | if (line >= 0) { |
358 | GCproto *pt = funcproto(fn); | ||
370 | char buf[LUA_IDSIZE]; | 359 | char buf[LUA_IDSIZE]; |
371 | lj_debug_shortname(buf, proto_chunkname(funcproto(fn))); | 360 | lj_debug_shortname(buf, proto_chunkname(pt), pt->firstline); |
372 | lj_str_pushf(L, "%s:%d: %s", buf, line, msg); | 361 | lj_strfmt_pushf(L, "%s:%d: %s", buf, line, msg); |
373 | return; | 362 | return; |
374 | } | 363 | } |
375 | } | 364 | } |
376 | } | 365 | } |
377 | lj_str_pushf(L, "%s", msg); | 366 | lj_strfmt_pushf(L, "%s", msg); |
378 | } | 367 | } |
379 | 368 | ||
380 | /* Push location string for a bytecode position to Lua stack. */ | 369 | /* Push location string for a bytecode position to Lua stack. */ |
@@ -384,20 +373,22 @@ void lj_debug_pushloc(lua_State *L, GCproto *pt, BCPos pc) | |||
384 | const char *s = strdata(name); | 373 | const char *s = strdata(name); |
385 | MSize i, len = name->len; | 374 | MSize i, len = name->len; |
386 | BCLine line = lj_debug_line(pt, pc); | 375 | BCLine line = lj_debug_line(pt, pc); |
387 | if (*s == '@') { | 376 | if (pt->firstline == ~(BCLine)0) { |
377 | lj_strfmt_pushf(L, "builtin:%s", s); | ||
378 | } else if (*s == '@') { | ||
388 | s++; len--; | 379 | s++; len--; |
389 | for (i = len; i > 0; i--) | 380 | for (i = len; i > 0; i--) |
390 | if (s[i] == '/' || s[i] == '\\') { | 381 | if (s[i] == '/' || s[i] == '\\') { |
391 | s += i+1; | 382 | s += i+1; |
392 | break; | 383 | break; |
393 | } | 384 | } |
394 | lj_str_pushf(L, "%s:%d", s, line); | 385 | lj_strfmt_pushf(L, "%s:%d", s, line); |
395 | } else if (len > 40) { | 386 | } else if (len > 40) { |
396 | lj_str_pushf(L, "%p:%d", pt, line); | 387 | lj_strfmt_pushf(L, "%p:%d", pt, line); |
397 | } else if (*s == '=') { | 388 | } else if (*s == '=') { |
398 | lj_str_pushf(L, "%s:%d", s+1, line); | 389 | lj_strfmt_pushf(L, "%s:%d", s+1, line); |
399 | } else { | 390 | } else { |
400 | lj_str_pushf(L, "\"%s\":%d", s, line); | 391 | lj_strfmt_pushf(L, "\"%s\":%d", s, line); |
401 | } | 392 | } |
402 | } | 393 | } |
403 | 394 | ||
@@ -460,7 +451,7 @@ int lj_debug_getinfo(lua_State *L, const char *what, lj_Debug *ar, int ext) | |||
460 | BCLine firstline = pt->firstline; | 451 | BCLine firstline = pt->firstline; |
461 | GCstr *name = proto_chunkname(pt); | 452 | GCstr *name = proto_chunkname(pt); |
462 | ar->source = strdata(name); | 453 | ar->source = strdata(name); |
463 | lj_debug_shortname(ar->short_src, name); | 454 | lj_debug_shortname(ar->short_src, name, pt->firstline); |
464 | ar->linedefined = (int)firstline; | 455 | ar->linedefined = (int)firstline; |
465 | ar->lastlinedefined = (int)(firstline + pt->numline); | 456 | ar->lastlinedefined = (int)(firstline + pt->numline); |
466 | ar->what = firstline ? "Lua" : "main"; | 457 | ar->what = firstline ? "Lua" : "main"; |
@@ -550,6 +541,111 @@ LUA_API int lua_getstack(lua_State *L, int level, lua_Debug *ar) | |||
550 | } | 541 | } |
551 | } | 542 | } |
552 | 543 | ||
544 | #if LJ_HASPROFILE | ||
545 | /* Put the chunkname into a buffer. */ | ||
546 | static int debug_putchunkname(SBuf *sb, GCproto *pt, int pathstrip) | ||
547 | { | ||
548 | GCstr *name = proto_chunkname(pt); | ||
549 | const char *p = strdata(name); | ||
550 | if (pt->firstline == ~(BCLine)0) { | ||
551 | lj_buf_putmem(sb, "[builtin:", 9); | ||
552 | lj_buf_putstr(sb, name); | ||
553 | lj_buf_putb(sb, ']'); | ||
554 | return 0; | ||
555 | } | ||
556 | if (*p == '=' || *p == '@') { | ||
557 | MSize len = name->len-1; | ||
558 | p++; | ||
559 | if (pathstrip) { | ||
560 | int i; | ||
561 | for (i = len-1; i >= 0; i--) | ||
562 | if (p[i] == '/' || p[i] == '\\') { | ||
563 | len -= i+1; | ||
564 | p = p+i+1; | ||
565 | break; | ||
566 | } | ||
567 | } | ||
568 | lj_buf_putmem(sb, p, len); | ||
569 | } else { | ||
570 | lj_buf_putmem(sb, "[string]", 9); | ||
571 | } | ||
572 | return 1; | ||
573 | } | ||
574 | |||
575 | /* Put a compact stack dump into a buffer. */ | ||
576 | void lj_debug_dumpstack(lua_State *L, SBuf *sb, const char *fmt, int depth) | ||
577 | { | ||
578 | int level = 0, dir = 1, pathstrip = 1; | ||
579 | MSize lastlen = 0; | ||
580 | if (depth < 0) { level = ~depth; depth = dir = -1; } /* Reverse frames. */ | ||
581 | while (level != depth) { /* Loop through all frame. */ | ||
582 | int size; | ||
583 | cTValue *frame = lj_debug_frame(L, level, &size); | ||
584 | if (frame) { | ||
585 | cTValue *nextframe = size ? frame+size : NULL; | ||
586 | GCfunc *fn = frame_func(frame); | ||
587 | const uint8_t *p = (const uint8_t *)fmt; | ||
588 | int c; | ||
589 | while ((c = *p++)) { | ||
590 | switch (c) { | ||
591 | case 'p': /* Preserve full path. */ | ||
592 | pathstrip = 0; | ||
593 | break; | ||
594 | case 'F': case 'f': { /* Dump function name. */ | ||
595 | const char *name; | ||
596 | const char *what = lj_debug_funcname(L, frame, &name); | ||
597 | if (what) { | ||
598 | if (c == 'F' && isluafunc(fn)) { /* Dump module:name for 'F'. */ | ||
599 | GCproto *pt = funcproto(fn); | ||
600 | if (pt->firstline != ~(BCLine)0) { /* Not a bytecode builtin. */ | ||
601 | debug_putchunkname(sb, pt, pathstrip); | ||
602 | lj_buf_putb(sb, ':'); | ||
603 | } | ||
604 | } | ||
605 | lj_buf_putmem(sb, name, (MSize)strlen(name)); | ||
606 | break; | ||
607 | } /* else: can't derive a name, dump module:line. */ | ||
608 | } | ||
609 | /* fallthrough */ | ||
610 | case 'l': /* Dump module:line. */ | ||
611 | if (isluafunc(fn)) { | ||
612 | GCproto *pt = funcproto(fn); | ||
613 | if (debug_putchunkname(sb, pt, pathstrip)) { | ||
614 | /* Regular Lua function. */ | ||
615 | BCLine line = c == 'l' ? debug_frameline(L, fn, nextframe) : | ||
616 | pt->firstline; | ||
617 | lj_buf_putb(sb, ':'); | ||
618 | lj_strfmt_putint(sb, line >= 0 ? line : pt->firstline); | ||
619 | } | ||
620 | } else if (isffunc(fn)) { /* Dump numbered builtins. */ | ||
621 | lj_buf_putmem(sb, "[builtin#", 9); | ||
622 | lj_strfmt_putint(sb, fn->c.ffid); | ||
623 | lj_buf_putb(sb, ']'); | ||
624 | } else { /* Dump C function address. */ | ||
625 | lj_buf_putb(sb, '@'); | ||
626 | lj_strfmt_putptr(sb, fn->c.f); | ||
627 | } | ||
628 | break; | ||
629 | case 'Z': /* Zap trailing separator. */ | ||
630 | lastlen = sbuflen(sb); | ||
631 | break; | ||
632 | default: | ||
633 | lj_buf_putb(sb, c); | ||
634 | break; | ||
635 | } | ||
636 | } | ||
637 | } else if (dir == 1) { | ||
638 | break; | ||
639 | } else { | ||
640 | level -= size; /* Reverse frame order: quickly skip missing level. */ | ||
641 | } | ||
642 | level += dir; | ||
643 | } | ||
644 | if (lastlen) | ||
645 | setsbufP(sb, sbufB(sb) + lastlen); /* Zap trailing separator. */ | ||
646 | } | ||
647 | #endif | ||
648 | |||
553 | /* Number of frames for the leading and trailing part of a traceback. */ | 649 | /* Number of frames for the leading and trailing part of a traceback. */ |
554 | #define TRACEBACK_LEVELS1 12 | 650 | #define TRACEBACK_LEVELS1 12 |
555 | #define TRACEBACK_LEVELS2 10 | 651 | #define TRACEBACK_LEVELS2 10 |