• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tarantool / luajit / 8545943226

03 Apr 2024 09:46PM UTC coverage: 92.672% (+4.6%) from 88.067%
8545943226

push

github

mkokryashkin
test: add tests for debugging extensions

This patch adds tests for LuaJIT debugging
extensions for lldb and gdb.

5662 of 6018 branches covered (94.08%)

Branch coverage included in aggregate %.

21615 of 23416 relevant lines covered (92.31%)

2822621.74 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.45
/src/lj_debug.c
1
/*
2
** Debugging and introspection.
3
** Copyright (C) 2005-2017 Mike Pall. See Copyright Notice in luajit.h
4
*/
5

6
#define lj_debug_c
7
#define LUA_CORE
8

9
#include "lj_obj.h"
10
#include "lj_err.h"
11
#include "lj_debug.h"
12
#include "lj_buf.h"
13
#include "lj_tab.h"
14
#include "lj_state.h"
15
#include "lj_frame.h"
16
#include "lj_bc.h"
17
#include "lj_strfmt.h"
18
#if LJ_HASJIT
19
#include "lj_jit.h"
20
#endif
21

22
/* -- Frames -------------------------------------------------------------- */
23

24
/* Get frame corresponding to a level. */
25
cTValue *lj_debug_frame(lua_State *L, int level, int *size)
71,454✔
26
{
27
  cTValue *frame, *nextframe, *bot = tvref(L->stack)+LJ_FR2;
71,454✔
28
  /* Traverse frames backwards. */
29
  for (nextframe = frame = L->base-1; frame > bot; ) {
928,942✔
30
    if (frame_gc(frame) == obj2gco(L))
928,905✔
31
      level++;  /* Skip dummy frames. See lj_err_optype_call(). */
×
32
    if (level-- == 0) {
928,905✔
33
      *size = (int)(nextframe - frame);
71,417✔
34
      return frame;  /* Level found. */
71,417✔
35
    }
36
    nextframe = frame;
857,488✔
37
    if (frame_islua(frame)) {
857,488✔
38
      frame = frame_prevl(frame);
857,133✔
39
    } else {
40
      if (frame_isvarg(frame))
355✔
41
        level++;  /* Skip vararg pseudo-frame. */
59✔
42
      frame = frame_prevd(frame);
355✔
43
    }
44
  }
45
  *size = level;
37✔
46
  return NULL;  /* Level not found. */
37✔
47
}
48

49
/* Invalid bytecode position. */
50
#define NO_BCPOS        (~(BCPos)0)
51

52
/* Return bytecode position for function/frame or NO_BCPOS. */
53
static BCPos debug_framepc(lua_State *L, GCfunc *fn, cTValue *nextframe)
54
{
55
  const BCIns *ins;
56
  GCproto *pt;
57
  BCPos pos;
58
  lj_assertL(fn->c.gct == ~LJ_TFUNC || fn->c.gct == ~LJ_TTHREAD,
59
             "function or frame expected");
60
  if (!isluafunc(fn)) {  /* Cannot derive a PC for non-Lua functions. */
61
    return NO_BCPOS;
62
  } else if (nextframe == NULL) {  /* Lua function on top. */
63
    void *cf = cframe_raw(L->cframe);
64
    if (cf == NULL || (char *)cframe_pc(cf) == (char *)cframe_L(cf))
65
      return NO_BCPOS;
66
    ins = cframe_pc(cf);  /* Only happens during error/hook handling. */
67
  } else {
68
    if (frame_islua(nextframe)) {
69
      ins = frame_pc(nextframe);
70
    } else if (frame_iscont(nextframe)) {
71
      ins = frame_contpc(nextframe);
72
    } else {
73
      /* Lua function below errfunc/gc/hook: find cframe to get the PC. */
74
      void *cf = cframe_raw(L->cframe);
75
      TValue *f = L->base-1;
76
      for (;;) {
77
        if (cf == NULL)
78
          return NO_BCPOS;
79
        while (cframe_nres(cf) < 0) {
80
          if (f >= restorestack(L, -cframe_nres(cf)))
81
            break;
82
          cf = cframe_raw(cframe_prev(cf));
83
          if (cf == NULL)
84
            return NO_BCPOS;
85
        }
86
        if (f < nextframe)
87
          break;
88
        if (frame_islua(f)) {
89
          f = frame_prevl(f);
90
        } else {
91
          if (frame_isc(f) || (frame_iscont(f) && frame_iscont_fficb(f)))
92
            cf = cframe_raw(cframe_prev(cf));
93
          f = frame_prevd(f);
94
        }
95
      }
96
      ins = cframe_pc(cf);
97
      if (!ins) return NO_BCPOS;
98
    }
99
  }
100
  pt = funcproto(fn);
101
  pos = proto_bcpos(pt, ins) - 1;
102
#if LJ_HASJIT
103
  if (pos > pt->sizebc) {  /* Undo the effects of lj_trace_exit for JLOOP. */
104
    if (bc_isret(bc_op(ins[-1]))) {
105
      GCtrace *T = (GCtrace *)((char *)(ins-1) - offsetof(GCtrace, startins));
106
      pos = proto_bcpos(pt, mref(T->startpc, const BCIns));
107
    } else {
108
      pos = NO_BCPOS;  /* Punt in case of stack overflow for stitched trace. */
109
    }
110
  }
111
#endif
112
  return pos;
113
}
114

115
/* -- Line numbers -------------------------------------------------------- */
116

117
/* Get line number for a bytecode position. */
118
BCLine LJ_FASTCALL lj_debug_line(GCproto *pt, BCPos pc)
32,321,941✔
119
{
120
  const void *lineinfo = proto_lineinfo(pt);
32,321,941✔
121
  if (pc <= pt->sizebc && lineinfo) {
32,321,941✔
122
    BCLine first = pt->firstline;
32,319,715✔
123
    if (pc == pt->sizebc) return first + pt->numline;
32,319,715✔
124
    if (pc-- == 0) return first;
32,319,713✔
125
    if (pt->numline < 256)
32,319,660✔
126
      return first + (BCLine)((const uint8_t *)lineinfo)[pc];
30,122,151✔
127
    else if (pt->numline < 65536)
2,197,509✔
128
      return first + (BCLine)((const uint16_t *)lineinfo)[pc];
1,287,298✔
129
    else
130
      return first + (BCLine)((const uint32_t *)lineinfo)[pc];
910,211✔
131
  }
132
  return 0;
133
}
134

135
/* Get line number for function/frame. */
136
BCLine lj_debug_frameline(lua_State *L, GCfunc *fn, cTValue *nextframe)
2,274✔
137
{
138
  BCPos pc = debug_framepc(L, fn, nextframe);
2,274✔
139
  if (pc != NO_BCPOS) {
2,274✔
140
    GCproto *pt = funcproto(fn);
2,233✔
141
    lj_assertL(pc <= pt->sizebc, "PC out of range");
2,233✔
142
    return lj_debug_line(pt, pc);
2,233✔
143
  }
144
  return -1;
145
}
146

147
/* -- Variable names ------------------------------------------------------ */
148

149
/* Get name of a local variable from slot number and PC. */
150
static const char *debug_varname(const GCproto *pt, BCPos pc, BCReg slot)
151
{
152
  const char *p = (const char *)proto_varinfo(pt);
153
  if (p) {
154
    BCPos lastpc = 0;
155
    for (;;) {
156
      const char *name = p;
157
      uint32_t vn = *(const uint8_t *)p;
158
      BCPos startpc, endpc;
159
      if (vn < VARNAME__MAX) {
160
        if (vn == VARNAME_END) break;  /* End of varinfo. */
161
      } else {
162
        do { p++; } while (*(const uint8_t *)p);  /* Skip over variable name. */
163
      }
164
      p++;
165
      lastpc = startpc = lastpc + lj_buf_ruleb128(&p);
166
      if (startpc > pc) break;
167
      endpc = startpc + lj_buf_ruleb128(&p);
168
      if (pc < endpc && slot-- == 0) {
169
        if (vn < VARNAME__MAX) {
170
#define VARNAMESTR(name, str)        str "\0"
171
          name = VARNAMEDEF(VARNAMESTR);
172
#undef VARNAMESTR
173
          if (--vn) while (*name++ || --vn) ;
174
        }
175
        return name;
176
      }
177
    }
178
  }
179
  return NULL;
180
}
181

182
/* Get name of local variable from 1-based slot number and function/frame. */
183
static TValue *debug_localname(lua_State *L, const lua_Debug *ar,
184
                               const char **name, BCReg slot1)
185
{
186
  uint32_t offset = (uint32_t)ar->i_ci & 0xffff;
187
  uint32_t size = (uint32_t)ar->i_ci >> 16;
188
  TValue *frame = tvref(L->stack) + offset;
189
  TValue *nextframe = size ? frame + size : NULL;
190
  GCfunc *fn = frame_func(frame);
191
  BCPos pc = debug_framepc(L, fn, nextframe);
192
  if (!nextframe) nextframe = L->top+LJ_FR2;
193
  if ((int)slot1 < 0) {  /* Negative slot number is for varargs. */
194
    if (pc != NO_BCPOS) {
195
      GCproto *pt = funcproto(fn);
196
      if ((pt->flags & PROTO_VARARG)) {
197
        slot1 = pt->numparams + (BCReg)(-(int)slot1);
198
        if (frame_isvarg(frame)) {  /* Vararg frame has been set up? (pc!=0) */
199
          nextframe = frame;
200
          frame = frame_prevd(frame);
201
        }
202
        if (frame + slot1+LJ_FR2 < nextframe) {
203
          *name = "(*vararg)";
204
          return frame+slot1;
205
        }
206
      }
207
    }
208
    return NULL;
209
  }
210
  if (pc != NO_BCPOS &&
211
      (*name = debug_varname(funcproto(fn), pc, slot1-1)) != NULL)
212
    ;
213
  else if (slot1 > 0 && frame + slot1+LJ_FR2 < nextframe)
214
    *name = "(*temporary)";
215
  return frame+slot1;
216
}
217

218
/* Get name of upvalue. */
219
const char *lj_debug_uvname(GCproto *pt, uint32_t idx)
167✔
220
{
221
  const uint8_t *p = proto_uvinfo(pt);
167✔
222
  lj_assertX(idx < pt->sizeuv, "bad upvalue index");
167✔
223
  if (!p) return "";
84✔
224
  if (idx) while (*p++ || --idx) ;
846✔
225
  return (const char *)p;
226
}
227

228
/* Get name and value of upvalue. */
229
const char *lj_debug_uvnamev(cTValue *o, uint32_t idx, TValue **tvp, GCobj **op)
21✔
230
{
231
  if (tvisfunc(o)) {
21✔
232
    GCfunc *fn = funcV(o);
21✔
233
    if (isluafunc(fn)) {
21✔
234
      GCproto *pt = funcproto(fn);
19✔
235
      if (idx < pt->sizeuv) {
19✔
236
        GCobj *uvo = gcref(fn->l.uvptr[idx]);
12✔
237
        *tvp = uvval(&uvo->uv);
12✔
238
        *op = uvo;
12✔
239
        return lj_debug_uvname(pt, idx);
24✔
240
      }
241
    } else {
242
      if (idx < fn->c.nupvalues) {
2✔
243
        *tvp = &fn->c.upvalue[idx];
×
244
        *op = obj2gco(fn);
×
245
        return "";
×
246
      }
247
    }
248
  }
249
  return NULL;
250
}
251

252
/* Deduce name of an object from slot number and PC. */
253
const char *lj_debug_slotname(GCproto *pt, const BCIns *ip, BCReg slot,
72,692✔
254
                              const char **name)
255
{
256
  const char *lname;
72,726✔
257
restart:
72,726✔
258
  lname = debug_varname(pt, proto_bcpos(pt, ip), slot);
72,726✔
259
  if (lname != NULL) { *name = lname; return "local"; }
72,726✔
260
  while (--ip > proto_bc(pt)) {
73,174✔
261
    BCIns ins = *ip;
73,171✔
262
    BCOp op = bc_op(ins);
73,171✔
263
    BCReg ra = bc_a(ins);
73,171✔
264
    if (bcmode_a(op) == BCMbase) {
73,171✔
265
      if (slot >= ra && (op != BC_KNIL || slot <= bc_d(ins)))
8✔
266
        return NULL;
267
    } else if (bcmode_a(op) == BCMdst && ra == slot) {
73,163✔
268
      switch (bc_op(ins)) {
72,664✔
269
      case BC_MOV:
270
        if (ra == slot) { slot = bc_d(ins); goto restart; }
34✔
271
        break;
272
      case BC_GGET:
71,459✔
273
        *name = strdata(gco2str(proto_kgc(pt, ~(ptrdiff_t)bc_d(ins))));
71,459✔
274
        return "global";
71,459✔
275
      case BC_TGETS:
39✔
276
        *name = strdata(gco2str(proto_kgc(pt, ~(ptrdiff_t)bc_c(ins))));
39✔
277
        if (ip > proto_bc(pt)) {
39✔
278
          BCIns insp = ip[-1];
39✔
279
          if (bc_op(insp) == BC_MOV && bc_a(insp) == ra+1+LJ_FR2 &&
39✔
280
              bc_d(insp) == bc_b(ins))
5✔
281
            return "method";
282
        }
283
        return "field";
34✔
284
      case BC_UGET:
71✔
285
        *name = lj_debug_uvname(pt, bc_d(ins));
71✔
286
        return "upvalue";
71✔
287
      default:
288
        return NULL;
289
      }
290
    }
499✔
291
  }
292
  return NULL;
293
}
294

295
/* Deduce function name from caller of a frame. */
296
const char *lj_debug_funcname(lua_State *L, cTValue *frame, const char **name)
71,337✔
297
{
298
  cTValue *pframe;
71,337✔
299
  GCfunc *fn;
71,337✔
300
  BCPos pc;
71,337✔
301
  if (frame <= tvref(L->stack)+LJ_FR2)
71,337✔
302
    return NULL;
303
  if (frame_isvarg(frame))
71,337✔
304
    frame = frame_prevd(frame);
26✔
305
  pframe = frame_prev(frame);
71,337✔
306
  fn = frame_func(pframe);
71,337✔
307
  pc = debug_framepc(L, fn, frame);
71,337✔
308
  if (pc != NO_BCPOS) {
71,337✔
309
    GCproto *pt = funcproto(fn);
71,240✔
310
    const BCIns *ip = &proto_bc(pt)[check_exp(pc < pt->sizebc, pc)];
71,240✔
311
    MMS mm = bcmode_mm(bc_op(*ip));
71,240✔
312
    if (mm == MM_call) {
71,240✔
313
      BCReg slot = bc_a(*ip);
71,203✔
314
      if (bc_op(*ip) == BC_ITERC) slot -= 3;
71,203✔
315
      return lj_debug_slotname(pt, ip, slot, name);
71,203✔
316
    } else if (mm != MM__MAX) {
37✔
317
      *name = strdata(mmname_str(G(L), mm));
36✔
318
      return "metamethod";
36✔
319
    }
320
  }
321
  return NULL;
322
}
323

324
/* -- Source code locations ----------------------------------------------- */
325

326
/* Generate shortened source name. */
327
void lj_debug_shortname(char *out, GCstr *str, BCLine line)
14,692✔
328
{
329
  const char *src = strdata(str);
14,692✔
330
  if (*src == '=') {
14,692✔
331
    strncpy(out, src+1, LUA_IDSIZE);  /* Remove first char. */
424✔
332
    out[LUA_IDSIZE-1] = '\0';  /* Ensures null termination. */
424✔
333
  } else if (*src == '@') {  /* Output "source", or "...source". */
14,268✔
334
    size_t len = str->len-1;
590✔
335
    src++;  /* Skip the `@' */
590✔
336
    if (len >= LUA_IDSIZE) {
590✔
337
      src += len-(LUA_IDSIZE-4);  /* Get last part of file name. */
586✔
338
      *out++ = '.'; *out++ = '.'; *out++ = '.';
586✔
339
    }
340
    strcpy(out, src);
590✔
341
  } else {  /* Output [string "string"] or [builtin:name]. */
342
    size_t len;  /* Length, up to first control char. */
343
    for (len = 0; len < LUA_IDSIZE-12; len++)
176,689✔
344
      if (((const unsigned char *)src)[len] < ' ') break;
176,672✔
345
    strcpy(out, line == ~(BCLine)0 ? "[builtin:" : "[string \""); out += 9;
13,678✔
346
    if (src[len] != '\0') {  /* Must truncate? */
13,678✔
347
      if (len > LUA_IDSIZE-15) len = LUA_IDSIZE-15;
9,483✔
348
      strncpy(out, src, len); out += len;
9,483✔
349
      strcpy(out, "..."); out += 3;
9,483✔
350
    } else {
351
      strcpy(out, src); out += len;
4,195✔
352
    }
353
    strcpy(out, line == ~(BCLine)0 ? "]" : "\"]");
27,356✔
354
  }
355
}
14,692✔
356

357
/* Add current location of a frame to error message. */
358
void lj_debug_addloc(lua_State *L, const char *msg,
33,553✔
359
                     cTValue *frame, cTValue *nextframe)
360
{
361
  if (frame) {
33,553✔
362
    GCfunc *fn = frame_func(frame);
33,331✔
363
    if (isluafunc(fn)) {
33,331✔
364
      BCLine line = lj_debug_frameline(L, fn, nextframe);
1,809✔
365
      if (line >= 0) {
1,809✔
366
        GCproto *pt = funcproto(fn);
1,807✔
367
        char buf[LUA_IDSIZE];
1,807✔
368
        lj_debug_shortname(buf, proto_chunkname(pt), pt->firstline);
1,807✔
369
        lj_strfmt_pushf(L, "%s:%d: %s", buf, line, msg);
1,807✔
370
        return;
1,807✔
371
      }
372
    }
373
  }
374
  lj_strfmt_pushf(L, "%s", msg);
31,746✔
375
}
376

377
/* Push location string for a bytecode position to Lua stack. */
378
void lj_debug_pushloc(lua_State *L, GCproto *pt, BCPos pc)
38✔
379
{
380
  GCstr *name = proto_chunkname(pt);
38✔
381
  const char *s = strdata(name);
38✔
382
  MSize i, len = name->len;
38✔
383
  BCLine line = lj_debug_line(pt, pc);
38✔
384
  if (pt->firstline == ~(BCLine)0) {
38✔
385
    lj_strfmt_pushf(L, "builtin:%s", s);
1✔
386
  } else if (*s == '@') {
37✔
387
    s++; len--;
37✔
388
    for (i = len; i > 0; i--)
935✔
389
      if (s[i] == '/' || s[i] == '\\') {
933✔
390
        s += i+1;
35✔
391
        break;
35✔
392
      }
393
    lj_strfmt_pushf(L, "%s:%d", s, line);
37✔
394
  } else if (len > 40) {
×
395
    lj_strfmt_pushf(L, "%p:%d", pt, line);
×
396
  } else if (*s == '=') {
×
397
    lj_strfmt_pushf(L, "%s:%d", s+1, line);
×
398
  } else {
399
    lj_strfmt_pushf(L, "\"%s\":%d", s, line);
×
400
  }
401
}
38✔
402

403
/* -- Public debug API ---------------------------------------------------- */
404

405
/* lua_getupvalue() and lua_setupvalue() are in lj_api.c. */
406

407
LUA_API const char *lua_getlocal(lua_State *L, const lua_Debug *ar, int n)
19✔
408
{
409
  const char *name = NULL;
19✔
410
  if (ar) {
19✔
411
    TValue *o = debug_localname(L, ar, &name, (BCReg)n);
19✔
412
    if (name) {
19✔
413
      copyTV(L, L->top, o);
15✔
414
      incr_top(L);
15✔
415
    }
416
  } else if (tvisfunc(L->top-1) && isluafunc(funcV(L->top-1))) {
×
417
    name = debug_varname(funcproto(funcV(L->top-1)), 0, (BCReg)n-1);
×
418
  }
419
  return name;
19✔
420
}
421

422
LUA_API const char *lua_setlocal(lua_State *L, const lua_Debug *ar, int n)
6✔
423
{
424
  const char *name = NULL;
6✔
425
  TValue *o = debug_localname(L, ar, &name, (BCReg)n);
6✔
426
  if (name)
6✔
427
    copyTV(L, o, L->top-1);
5✔
428
  L->top--;
6✔
429
  return name;
6✔
430
}
431

432
int lj_debug_getinfo(lua_State *L, const char *what, lj_Debug *ar, int ext)
71,322✔
433
{
434
  int opt_f = 0, opt_L = 0;
71,322✔
435
  TValue *frame = NULL;
71,322✔
436
  TValue *nextframe = NULL;
71,322✔
437
  GCfunc *fn;
71,322✔
438
  if (*what == '>') {
71,322✔
439
    TValue *func = L->top - 1;
23✔
440
    if (!tvisfunc(func)) return 0;
23✔
441
    fn = funcV(func);
22✔
442
    L->top--;
22✔
443
    what++;
22✔
444
  } else {
445
    uint32_t offset = (uint32_t)ar->i_ci & 0xffff;
71,299✔
446
    uint32_t size = (uint32_t)ar->i_ci >> 16;
71,299✔
447
    lj_assertL(offset != 0, "bad frame offset");
71,299✔
448
    frame = tvref(L->stack) + offset;
71,299✔
449
    if (size) nextframe = frame + size;
71,299✔
450
    lj_assertL(frame <= tvref(L->maxstack) &&
71,299✔
451
               (!nextframe || nextframe <= tvref(L->maxstack)),
452
               "broken frame chain");
453
    fn = frame_func(frame);
71,299✔
454
    lj_assertL(fn->c.gct == ~LJ_TFUNC, "bad frame function");
455
  }
456
  for (; *what; what++) {
143,329✔
457
    if (*what == 'S') {
72,009✔
458
      if (isluafunc(fn)) {
209✔
459
        GCproto *pt = funcproto(fn);
174✔
460
        BCLine firstline = pt->firstline;
174✔
461
        GCstr *name = proto_chunkname(pt);
174✔
462
        ar->source = strdata(name);
174✔
463
        lj_debug_shortname(ar->short_src, name, pt->firstline);
174✔
464
        ar->linedefined = (int)firstline;
174✔
465
        ar->lastlinedefined = (int)(firstline + pt->numline);
174✔
466
        ar->what = (firstline || !pt->numline) ? "Lua" : "main";
204✔
467
      } else {
468
        ar->source = "=[C]";
35✔
469
        ar->short_src[0] = '[';
35✔
470
        ar->short_src[1] = 'C';
35✔
471
        ar->short_src[2] = ']';
35✔
472
        ar->short_src[3] = '\0';
35✔
473
        ar->linedefined = -1;
35✔
474
        ar->lastlinedefined = -1;
35✔
475
        ar->what = "C";
35✔
476
      }
477
    } else if (*what == 'l') {
71,800✔
478
      ar->currentline = frame ? lj_debug_frameline(L, fn, nextframe) : -1;
213✔
479
    } else if (*what == 'u') {
71,587✔
480
      ar->nups = fn->c.nupvalues;
80✔
481
      if (ext) {
80✔
482
        if (isluafunc(fn)) {
80✔
483
          GCproto *pt = funcproto(fn);
79✔
484
          ar->nparams = pt->numparams;
79✔
485
          ar->isvararg = !!(pt->flags & PROTO_VARARG);
79✔
486
        } else {
487
          ar->nparams = 0;
1✔
488
          ar->isvararg = 1;
1✔
489
        }
490
      }
491
    } else if (*what == 'n') {
71,507✔
492
      ar->namewhat = frame ? lj_debug_funcname(L, frame, &ar->name) : NULL;
71,243✔
493
      if (ar->namewhat == NULL) {
71,243✔
494
        ar->namewhat = "";
67✔
495
        ar->name = NULL;
67✔
496
      }
497
    } else if (*what == 'f') {
264✔
498
      opt_f = 1;
499
    } else if (*what == 'L') {
4✔
500
      opt_L = 1;
501
    } else {
502
      return 0;  /* Bad option. */
503
    }
504
  }
505
  if (opt_f) {
71,320✔
506
    setfuncV(L, L->top, fn);
260✔
507
    incr_top(L);
260✔
508
  }
509
  if (opt_L) {
71,320✔
510
    if (isluafunc(fn)) {
3✔
511
      GCtab *t = lj_tab_new(L, 0, 0);
3✔
512
      GCproto *pt = funcproto(fn);
3✔
513
      const void *lineinfo = proto_lineinfo(pt);
3✔
514
      if (lineinfo) {
3✔
515
        BCLine first = pt->firstline;
3✔
516
        int sz = pt->numline < 256 ? 1 : pt->numline < 65536 ? 2 : 4;
3✔
517
        MSize i, szl = pt->sizebc-1;
3✔
518
        for (i = 0; i < szl; i++) {
87✔
519
          BCLine line = first +
168✔
520
            (sz == 1 ? (BCLine)((const uint8_t *)lineinfo)[i] :
84✔
521
             sz == 2 ? (BCLine)((const uint16_t *)lineinfo)[i] :
×
522
             (BCLine)((const uint32_t *)lineinfo)[i]);
×
523
          setboolV(lj_tab_setint(L, t, line), 1);
84✔
524
        }
525
      }
526
      settabV(L, L->top, t);
3✔
527
    } else {
528
      setnilV(L->top);
×
529
    }
530
    incr_top(L);
3✔
531
  }
532
  return 1;  /* Ok. */
533
}
534

535
LUA_API int lua_getinfo(lua_State *L, const char *what, lua_Debug *ar)
133✔
536
{
537
  return lj_debug_getinfo(L, what, (lj_Debug *)ar, 0);
12✔
538
}
539

540
LUA_API int lua_getstack(lua_State *L, int level, lua_Debug *ar)
71,362✔
541
{
542
  int size;
71,362✔
543
  cTValue *frame = lj_debug_frame(L, level, &size);
71,362✔
544
  if (frame) {
71,362✔
545
    ar->i_ci = (size << 16) + (int)(frame - tvref(L->stack));
71,329✔
546
    return 1;
71,329✔
547
  } else {
548
    ar->i_ci = level - size;
33✔
549
    return 0;
33✔
550
  }
551
}
552

553
#if LJ_HASPROFILE
554
/* Put the chunkname into a buffer. */
555
static int debug_putchunkname(SBuf *sb, GCproto *pt, int pathstrip)
556
{
557
  GCstr *name = proto_chunkname(pt);
558
  const char *p = strdata(name);
559
  if (pt->firstline == ~(BCLine)0) {
560
    lj_buf_putmem(sb, "[builtin:", 9);
561
    lj_buf_putstr(sb, name);
562
    lj_buf_putb(sb, ']');
563
    return 0;
564
  }
565
  if (*p == '=' || *p == '@') {
566
    MSize len = name->len-1;
567
    p++;
568
    if (pathstrip) {
569
      int i;
570
      for (i = len-1; i >= 0; i--)
571
        if (p[i] == '/' || p[i] == '\\') {
572
          len -= i+1;
573
          p = p+i+1;
574
          break;
575
        }
576
    }
577
    lj_buf_putmem(sb, p, len);
578
  } else {
579
    lj_buf_putmem(sb, "[string]", 8);
580
  }
581
  return 1;
582
}
583

584
/* Put a compact stack dump into a buffer. */
585
void lj_debug_dumpstack(lua_State *L, SBuf *sb, const char *fmt, int depth)
1✔
586
{
587
  int level = 0, dir = 1, pathstrip = 1;
1✔
588
  MSize lastlen = 0;
1✔
589
  if (depth < 0) { level = ~depth; depth = dir = -1; }  /* Reverse frames. */
1✔
590
  while (level != depth) {  /* Loop through all frame. */
2✔
591
    int size;
1✔
592
    cTValue *frame = lj_debug_frame(L, level, &size);
1✔
593
    if (frame) {
1✔
594
      cTValue *nextframe = size ? frame+size : NULL;
1✔
595
      GCfunc *fn = frame_func(frame);
1✔
596
      const uint8_t *p = (const uint8_t *)fmt;
1✔
597
      int c;
1✔
598
      while ((c = *p++)) {
1✔
599
        switch (c) {
5✔
600
        case 'p':  /* Preserve full path. */
601
          pathstrip = 0;
602
          break;
603
        case 'F': case 'f': {  /* Dump function name. */
1✔
604
          const char *name;
1✔
605
          const char *what = lj_debug_funcname(L, frame, &name);
1✔
606
          if (what) {
1✔
607
            if (c == 'F' && isluafunc(fn)) {  /* Dump module:name for 'F'. */
1✔
608
              GCproto *pt = funcproto(fn);
×
609
              if (pt->firstline != ~(BCLine)0) {  /* Not a bytecode builtin. */
×
610
                debug_putchunkname(sb, pt, pathstrip);
×
611
                lj_buf_putb(sb, ':');
×
612
              }
613
            }
614
            lj_buf_putmem(sb, name, (MSize)strlen(name));
1✔
615
            break;
1✔
616
          }  /* else: can't derive a name, dump module:line. */
617
          }
618
          /* fallthrough */
619
        case 'l':  /* Dump module:line. */
620
          if (isluafunc(fn)) {
×
621
            GCproto *pt = funcproto(fn);
×
622
            if (debug_putchunkname(sb, pt, pathstrip)) {
×
623
              /* Regular Lua function. */
624
              BCLine line = c == 'l' ? lj_debug_frameline(L, fn, nextframe) :
×
625
                                       pt->firstline;
626
              lj_buf_putb(sb, ':');
×
627
              lj_strfmt_putint(sb, line >= 0 ? line : pt->firstline);
×
628
            }
629
          } else if (isffunc(fn)) {  /* Dump numbered builtins. */
×
630
            lj_buf_putmem(sb, "[builtin#", 9);
×
631
            lj_strfmt_putint(sb, fn->c.ffid);
×
632
            lj_buf_putb(sb, ']');
×
633
          } else {  /* Dump C function address. */
634
            lj_buf_putb(sb, '@');
×
635
            lj_strfmt_putptr(sb, fn->c.f);
×
636
          }
637
          break;
638
        case 'Z':  /* Zap trailing separator. */
1✔
639
          lastlen = sbuflen(sb);
1✔
640
          break;
1✔
641
        default:
642
          lj_buf_putb(sb, c);
9✔
643
          break;
644
        }
645
      }
646
    } else if (dir == 1) {
×
647
      break;
648
    } else {
649
      level -= size;  /* Reverse frame order: quickly skip missing level. */
×
650
    }
651
    level += dir;
1✔
652
  }
653
  if (lastlen)
1✔
654
    setsbufP(sb, sbufB(sb) + lastlen);  /* Zap trailing separator. */
1✔
655
}
1✔
656
#endif
657

658
/* Number of frames for the leading and trailing part of a traceback. */
659
#define TRACEBACK_LEVELS1        12
660
#define TRACEBACK_LEVELS2        10
661

662
LUALIB_API void luaL_traceback (lua_State *L, lua_State *L1, const char *msg,
23✔
663
                                int level)
664
{
665
  int top = (int)(L->top - L->base);
23✔
666
  int lim = TRACEBACK_LEVELS1;
23✔
667
  lua_Debug ar;
23✔
668
  if (msg) lua_pushfstring(L, "%s\n", msg);
23✔
669
  lua_pushliteral(L, "stack traceback:");
23✔
670
  while (lua_getstack(L1, level++, &ar)) {
146✔
671
    GCfunc *fn;
123✔
672
    if (level > lim) {
123✔
673
      if (!lua_getstack(L1, level + TRACEBACK_LEVELS2, &ar)) {
2✔
674
        level--;
675
      } else {
676
        lua_pushliteral(L, "\n\t...");
2✔
677
        lua_getstack(L1, -10, &ar);
2✔
678
        level = ar.i_ci - TRACEBACK_LEVELS2;
2✔
679
      }
680
      lim = 2147483647;
2✔
681
      continue;
2✔
682
    }
683
    lua_getinfo(L1, "Snlf", &ar);
121✔
684
    fn = funcV(L1->top-1); L1->top--;
121✔
685
    if (isffunc(fn) && !*ar.namewhat)
121✔
686
      lua_pushfstring(L, "\n\t[builtin#%d]:", fn->c.ffid);
×
687
    else
688
      lua_pushfstring(L, "\n\t%s:", ar.short_src);
121✔
689
    if (ar.currentline > 0)
121✔
690
      lua_pushfstring(L, "%d:", ar.currentline);
86✔
691
    if (*ar.namewhat) {
121✔
692
      lua_pushfstring(L, " in function " LUA_QS, ar.name);
75✔
693
    } else {
694
      if (*ar.what == 'm') {
46✔
695
        lua_pushliteral(L, " in main chunk");
14✔
696
      } else if (*ar.what == 'C') {
32✔
697
        lua_pushfstring(L, " at %p", fn->c.f);
14✔
698
      } else {
699
        lua_pushfstring(L, " in function <%s:%d>",
18✔
700
                        ar.short_src, ar.linedefined);
701
      }
702
    }
703
    if ((int)(L->top - L->base) - top >= 15)
121✔
704
      lua_concat(L, (int)(L->top - L->base) - top);
15✔
705
  }
706
  lua_concat(L, (int)(L->top - L->base) - top);
23✔
707
}
23✔
708

STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc