0e6c493fec
In order for cppcheck to perform a proper analysis it needs to be aware of how the sources are compiled (source files, include paths/files, extra defines, etc). All the needed information is available from the Makefiles and can be leveraged with a generic cppcheck Makefile target. So let's add one. Additional minor changes: * Removing the cppcheck-suppressions.txt file. With cppcheck 2.3 and these changes it appears to no longer be needed. Some inline suppressions were also removed since they appear not to be needed. We can add them back if it turns out they're needed for older versions of cppcheck. * Added the ax_count_cpus m4 macro to detect at configure time how many processors are available in order to run multiple cppcheck jobs. This value is also now used as a replacement for nproc when executing the kernel interface checks. * "PHONY =" line moved in to the Rules.am file which is included at the top of all Makefile.am's. This is just convenient becase it allows us to use the += syntax to add phony targets. * One upside of this integration worth mentioning is it now allows `make cppcheck` to be run in any directory to check that subtree. * For the moment, cppcheck is not run against the FreeBSD specific kernel sources. The cppcheck-FreeBSD target will need to be implemented and testing on FreeBSD to support this. Reviewed-by: Ryan Moeller <ryan@ixsystems.com> Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov> Closes #11508
609 lines
16 KiB
C
609 lines
16 KiB
C
/* BEGIN CSTYLED */
|
|
/*
|
|
** $Id: ldebug.c,v 2.90.1.4 2015/02/19 17:05:13 roberto Exp $
|
|
** Debug Interface
|
|
** See Copyright Notice in lua.h
|
|
*/
|
|
|
|
|
|
#define ldebug_c
|
|
#define LUA_CORE
|
|
|
|
#include <sys/lua/lua.h>
|
|
|
|
#include "lapi.h"
|
|
#include "lcode.h"
|
|
#include "ldebug.h"
|
|
#include "ldo.h"
|
|
#include "lfunc.h"
|
|
#include "lobject.h"
|
|
#include "lopcodes.h"
|
|
#include "lstate.h"
|
|
#include "lstring.h"
|
|
#include "ltable.h"
|
|
#include "ltm.h"
|
|
#include "lvm.h"
|
|
|
|
|
|
|
|
#define noLuaClosure(f) ((f) == NULL || (f)->c.tt == LUA_TCCL)
|
|
|
|
|
|
static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name);
|
|
|
|
|
|
static int currentpc (CallInfo *ci) {
|
|
lua_assert(isLua(ci));
|
|
return pcRel(ci->u.l.savedpc, ci_func(ci)->p);
|
|
}
|
|
|
|
|
|
static int currentline (CallInfo *ci) {
|
|
return getfuncline(ci_func(ci)->p, currentpc(ci));
|
|
}
|
|
|
|
|
|
static void swapextra (lua_State *L) {
|
|
if (L->status == LUA_YIELD) {
|
|
CallInfo *ci = L->ci; /* get function that yielded */
|
|
StkId temp = ci->func; /* exchange its 'func' and 'extra' values */
|
|
ci->func = restorestack(L, ci->extra);
|
|
ci->extra = savestack(L, temp);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
** this function can be called asynchronous (e.g. during a signal)
|
|
*/
|
|
LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count) {
|
|
if (func == NULL || mask == 0) { /* turn off hooks? */
|
|
mask = 0;
|
|
func = NULL;
|
|
}
|
|
if (isLua(L->ci))
|
|
L->oldpc = L->ci->u.l.savedpc;
|
|
L->hook = func;
|
|
L->basehookcount = count;
|
|
resethookcount(L);
|
|
L->hookmask = cast_byte(mask);
|
|
return 1;
|
|
}
|
|
|
|
|
|
LUA_API lua_Hook lua_gethook (lua_State *L) {
|
|
return L->hook;
|
|
}
|
|
|
|
|
|
LUA_API int lua_gethookmask (lua_State *L) {
|
|
return L->hookmask;
|
|
}
|
|
|
|
|
|
LUA_API int lua_gethookcount (lua_State *L) {
|
|
return L->basehookcount;
|
|
}
|
|
|
|
|
|
LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) {
|
|
int status;
|
|
CallInfo *ci;
|
|
if (level < 0) return 0; /* invalid (negative) level */
|
|
lua_lock(L);
|
|
for (ci = L->ci; level > 0 && ci != &L->base_ci; ci = ci->previous)
|
|
level--;
|
|
if (level == 0 && ci != &L->base_ci) { /* level found? */
|
|
status = 1;
|
|
ar->i_ci = ci;
|
|
}
|
|
else status = 0; /* no such level */
|
|
lua_unlock(L);
|
|
return status;
|
|
}
|
|
|
|
|
|
static const char *upvalname (Proto *p, int uv) {
|
|
TString *s = check_exp(uv < p->sizeupvalues, p->upvalues[uv].name);
|
|
if (s == NULL) return "?";
|
|
else return getstr(s);
|
|
}
|
|
|
|
|
|
static const char *findvararg (CallInfo *ci, int n, StkId *pos) {
|
|
int nparams = clLvalue(ci->func)->p->numparams;
|
|
if (n >= ci->u.l.base - ci->func - nparams)
|
|
return NULL; /* no such vararg */
|
|
else {
|
|
*pos = ci->func + nparams + n;
|
|
return "(*vararg)"; /* generic name for any vararg */
|
|
}
|
|
}
|
|
|
|
|
|
static const char *findlocal (lua_State *L, CallInfo *ci, int n,
|
|
StkId *pos) {
|
|
const char *name = NULL;
|
|
StkId base;
|
|
if (isLua(ci)) {
|
|
if (n < 0) /* access to vararg values? */
|
|
return findvararg(ci, -n, pos);
|
|
else {
|
|
base = ci->u.l.base;
|
|
name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci));
|
|
}
|
|
}
|
|
else
|
|
base = ci->func + 1;
|
|
if (name == NULL) { /* no 'standard' name? */
|
|
StkId limit = (ci == L->ci) ? L->top : ci->next->func;
|
|
if (limit - base >= n && n > 0) /* is 'n' inside 'ci' stack? */
|
|
name = "(*temporary)"; /* generic name for any valid slot */
|
|
else
|
|
return NULL; /* no name */
|
|
}
|
|
*pos = base + (n - 1);
|
|
return name;
|
|
}
|
|
|
|
|
|
LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) {
|
|
const char *name;
|
|
lua_lock(L);
|
|
swapextra(L);
|
|
if (ar == NULL) { /* information about non-active function? */
|
|
if (!isLfunction(L->top - 1)) /* not a Lua function? */
|
|
name = NULL;
|
|
else /* consider live variables at function start (parameters) */
|
|
name = luaF_getlocalname(clLvalue(L->top - 1)->p, n, 0);
|
|
}
|
|
else { /* active function; get information through 'ar' */
|
|
StkId pos = 0; /* to avoid warnings */
|
|
name = findlocal(L, ar->i_ci, n, &pos);
|
|
if (name) {
|
|
setobj2s(L, L->top, pos);
|
|
api_incr_top(L);
|
|
}
|
|
}
|
|
swapextra(L);
|
|
lua_unlock(L);
|
|
return name;
|
|
}
|
|
|
|
|
|
LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) {
|
|
StkId pos = 0; /* to avoid warnings */
|
|
const char *name;
|
|
lua_lock(L);
|
|
swapextra(L);
|
|
name = findlocal(L, ar->i_ci, n, &pos);
|
|
if (name)
|
|
setobjs2s(L, pos, L->top - 1);
|
|
L->top--; /* pop value */
|
|
swapextra(L);
|
|
lua_unlock(L);
|
|
return name;
|
|
}
|
|
|
|
|
|
static void funcinfo (lua_Debug *ar, Closure *cl) {
|
|
if (noLuaClosure(cl)) {
|
|
ar->source = "=[C]";
|
|
ar->linedefined = -1;
|
|
ar->lastlinedefined = -1;
|
|
ar->what = "C";
|
|
}
|
|
else {
|
|
Proto *p = cl->l.p;
|
|
ar->source = p->source ? getstr(p->source) : "=?";
|
|
ar->linedefined = p->linedefined;
|
|
ar->lastlinedefined = p->lastlinedefined;
|
|
ar->what = (ar->linedefined == 0) ? "main" : "Lua";
|
|
}
|
|
luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE);
|
|
}
|
|
|
|
|
|
static void collectvalidlines (lua_State *L, Closure *f) {
|
|
if (noLuaClosure(f)) {
|
|
setnilvalue(L->top);
|
|
api_incr_top(L);
|
|
}
|
|
else {
|
|
int i;
|
|
TValue v;
|
|
int *lineinfo = f->l.p->lineinfo;
|
|
Table *t = luaH_new(L); /* new table to store active lines */
|
|
sethvalue(L, L->top, t); /* push it on stack */
|
|
api_incr_top(L);
|
|
setbvalue(&v, 1); /* boolean 'true' to be the value of all indices */
|
|
for (i = 0; i < f->l.p->sizelineinfo; i++) /* for all lines with code */
|
|
luaH_setint(L, t, lineinfo[i], &v); /* table[line] = true */
|
|
}
|
|
}
|
|
|
|
|
|
static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar,
|
|
Closure *f, CallInfo *ci) {
|
|
int status = 1;
|
|
for (; *what; what++) {
|
|
switch (*what) {
|
|
case 'S': {
|
|
funcinfo(ar, f);
|
|
break;
|
|
}
|
|
case 'l': {
|
|
ar->currentline = (ci && isLua(ci)) ? currentline(ci) : -1;
|
|
break;
|
|
}
|
|
case 'u': {
|
|
ar->nups = (f == NULL) ? 0 : f->c.nupvalues;
|
|
if (noLuaClosure(f)) {
|
|
ar->isvararg = 1;
|
|
ar->nparams = 0;
|
|
}
|
|
else {
|
|
ar->isvararg = f->l.p->is_vararg;
|
|
ar->nparams = f->l.p->numparams;
|
|
}
|
|
break;
|
|
}
|
|
case 't': {
|
|
ar->istailcall = (ci) ? ci->callstatus & CIST_TAIL : 0;
|
|
break;
|
|
}
|
|
case 'n': {
|
|
/* calling function is a known Lua function? */
|
|
if (ci && !(ci->callstatus & CIST_TAIL) && isLua(ci->previous))
|
|
ar->namewhat = getfuncname(L, ci->previous, &ar->name);
|
|
else
|
|
ar->namewhat = NULL;
|
|
if (ar->namewhat == NULL) {
|
|
ar->namewhat = ""; /* not found */
|
|
ar->name = NULL;
|
|
}
|
|
break;
|
|
}
|
|
case 'L':
|
|
case 'f': /* handled by lua_getinfo */
|
|
break;
|
|
default: status = 0; /* invalid option */
|
|
}
|
|
}
|
|
return status;
|
|
}
|
|
|
|
|
|
LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) {
|
|
int status;
|
|
Closure *cl;
|
|
CallInfo *ci;
|
|
StkId func;
|
|
lua_lock(L);
|
|
swapextra(L);
|
|
if (*what == '>') {
|
|
ci = NULL;
|
|
func = L->top - 1;
|
|
api_check(L, ttisfunction(func), "function expected");
|
|
what++; /* skip the '>' */
|
|
L->top--; /* pop function */
|
|
}
|
|
else {
|
|
ci = ar->i_ci;
|
|
func = ci->func;
|
|
lua_assert(ttisfunction(ci->func));
|
|
}
|
|
cl = ttisclosure(func) ? clvalue(func) : NULL;
|
|
status = auxgetinfo(L, what, ar, cl, ci);
|
|
if (strchr(what, 'f')) {
|
|
setobjs2s(L, L->top, func);
|
|
api_incr_top(L);
|
|
}
|
|
swapextra(L);
|
|
if (strchr(what, 'L'))
|
|
collectvalidlines(L, cl);
|
|
lua_unlock(L);
|
|
return status;
|
|
}
|
|
|
|
|
|
/*
|
|
** {======================================================
|
|
** Symbolic Execution
|
|
** =======================================================
|
|
*/
|
|
|
|
static const char *getobjname (Proto *p, int lastpc, int reg,
|
|
const char **name);
|
|
|
|
|
|
/*
|
|
** find a "name" for the RK value 'c'
|
|
*/
|
|
static void kname (Proto *p, int pc, int c, const char **name) {
|
|
if (ISK(c)) { /* is 'c' a constant? */
|
|
TValue *kvalue = &p->k[INDEXK(c)];
|
|
if (ttisstring(kvalue)) { /* literal constant? */
|
|
*name = svalue(kvalue); /* it is its own name */
|
|
return;
|
|
}
|
|
/* else no reasonable name found */
|
|
}
|
|
else { /* 'c' is a register */
|
|
const char *what = getobjname(p, pc, c, name); /* search for 'c' */
|
|
if (what && *what == 'c') { /* found a constant name? */
|
|
return; /* 'name' already filled */
|
|
}
|
|
/* else no reasonable name found */
|
|
}
|
|
*name = "?"; /* no reasonable name found */
|
|
}
|
|
|
|
|
|
static int filterpc (int pc, int jmptarget) {
|
|
if (pc < jmptarget) /* is code conditional (inside a jump)? */
|
|
return -1; /* cannot know who sets that register */
|
|
else return pc; /* current position sets that register */
|
|
}
|
|
|
|
|
|
/*
|
|
** try to find last instruction before 'lastpc' that modified register 'reg'
|
|
*/
|
|
static int findsetreg (Proto *p, int lastpc, int reg) {
|
|
int pc;
|
|
int setreg = -1; /* keep last instruction that changed 'reg' */
|
|
int jmptarget = 0; /* any code before this address is conditional */
|
|
for (pc = 0; pc < lastpc; pc++) {
|
|
Instruction i = p->code[pc];
|
|
OpCode op = GET_OPCODE(i);
|
|
int a = GETARG_A(i);
|
|
switch (op) {
|
|
case OP_LOADNIL: {
|
|
int b = GETARG_B(i);
|
|
if (a <= reg && reg <= a + b) /* set registers from 'a' to 'a+b' */
|
|
setreg = filterpc(pc, jmptarget);
|
|
break;
|
|
}
|
|
case OP_TFORCALL: {
|
|
if (reg >= a + 2) /* affect all regs above its base */
|
|
setreg = filterpc(pc, jmptarget);
|
|
break;
|
|
}
|
|
case OP_CALL:
|
|
case OP_TAILCALL: {
|
|
if (reg >= a) /* affect all registers above base */
|
|
setreg = filterpc(pc, jmptarget);
|
|
break;
|
|
}
|
|
case OP_JMP: {
|
|
int b = GETARG_sBx(i);
|
|
int dest = pc + 1 + b;
|
|
/* jump is forward and do not skip `lastpc'? */
|
|
if (pc < dest && dest <= lastpc) {
|
|
if (dest > jmptarget)
|
|
jmptarget = dest; /* update 'jmptarget' */
|
|
}
|
|
break;
|
|
}
|
|
case OP_TEST: {
|
|
if (reg == a) /* jumped code can change 'a' */
|
|
setreg = filterpc(pc, jmptarget);
|
|
break;
|
|
}
|
|
default:
|
|
if (testAMode(op) && reg == a) /* any instruction that set A */
|
|
setreg = filterpc(pc, jmptarget);
|
|
break;
|
|
}
|
|
}
|
|
return setreg;
|
|
}
|
|
|
|
|
|
static const char *getobjname (Proto *p, int lastpc, int reg,
|
|
const char **name) {
|
|
int pc;
|
|
*name = luaF_getlocalname(p, reg + 1, lastpc);
|
|
if (*name) /* is a local? */
|
|
return "local";
|
|
/* else try symbolic execution */
|
|
pc = findsetreg(p, lastpc, reg);
|
|
if (pc != -1) { /* could find instruction? */
|
|
Instruction i = p->code[pc];
|
|
OpCode op = GET_OPCODE(i);
|
|
switch (op) {
|
|
case OP_MOVE: {
|
|
int b = GETARG_B(i); /* move from 'b' to 'a' */
|
|
if (b < GETARG_A(i))
|
|
return getobjname(p, pc, b, name); /* get name for 'b' */
|
|
break;
|
|
}
|
|
case OP_GETTABUP:
|
|
case OP_GETTABLE: {
|
|
int k = GETARG_C(i); /* key index */
|
|
int t = GETARG_B(i); /* table index */
|
|
const char *vn = (op == OP_GETTABLE) /* name of indexed variable */
|
|
? luaF_getlocalname(p, t + 1, pc)
|
|
: upvalname(p, t);
|
|
kname(p, pc, k, name);
|
|
return (vn && strcmp(vn, LUA_ENV) == 0) ? "global" : "field";
|
|
}
|
|
case OP_GETUPVAL: {
|
|
*name = upvalname(p, GETARG_B(i));
|
|
return "upvalue";
|
|
}
|
|
case OP_LOADK:
|
|
case OP_LOADKX: {
|
|
int b = (op == OP_LOADK) ? GETARG_Bx(i)
|
|
: GETARG_Ax(p->code[pc + 1]);
|
|
if (ttisstring(&p->k[b])) {
|
|
*name = svalue(&p->k[b]);
|
|
return "constant";
|
|
}
|
|
break;
|
|
}
|
|
case OP_SELF: {
|
|
int k = GETARG_C(i); /* key index */
|
|
kname(p, pc, k, name);
|
|
return "method";
|
|
}
|
|
default: break; /* go through to return NULL */
|
|
}
|
|
}
|
|
return NULL; /* could not find reasonable name */
|
|
}
|
|
|
|
|
|
static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) {
|
|
TMS tm;
|
|
Proto *p = ci_func(ci)->p; /* calling function */
|
|
int pc = currentpc(ci); /* calling instruction index */
|
|
Instruction i = p->code[pc]; /* calling instruction */
|
|
switch (GET_OPCODE(i)) {
|
|
case OP_CALL:
|
|
case OP_TAILCALL: /* get function name */
|
|
return getobjname(p, pc, GETARG_A(i), name);
|
|
case OP_TFORCALL: { /* for iterator */
|
|
*name = "for iterator";
|
|
return "for iterator";
|
|
}
|
|
/* all other instructions can call only through metamethods */
|
|
case OP_SELF:
|
|
case OP_GETTABUP:
|
|
case OP_GETTABLE: tm = TM_INDEX; break;
|
|
case OP_SETTABUP:
|
|
case OP_SETTABLE: tm = TM_NEWINDEX; break;
|
|
case OP_EQ: tm = TM_EQ; break;
|
|
case OP_ADD: tm = TM_ADD; break;
|
|
case OP_SUB: tm = TM_SUB; break;
|
|
case OP_MUL: tm = TM_MUL; break;
|
|
case OP_DIV: tm = TM_DIV; break;
|
|
case OP_MOD: tm = TM_MOD; break;
|
|
case OP_POW: tm = TM_POW; break;
|
|
case OP_UNM: tm = TM_UNM; break;
|
|
case OP_LEN: tm = TM_LEN; break;
|
|
case OP_LT: tm = TM_LT; break;
|
|
case OP_LE: tm = TM_LE; break;
|
|
case OP_CONCAT: tm = TM_CONCAT; break;
|
|
default:
|
|
return NULL; /* else no useful name can be found */
|
|
}
|
|
*name = getstr(G(L)->tmname[tm]);
|
|
return "metamethod";
|
|
}
|
|
|
|
/* }====================================================== */
|
|
|
|
|
|
|
|
/*
|
|
** only ANSI way to check whether a pointer points to an array
|
|
** (used only for error messages, so efficiency is not a big concern)
|
|
*/
|
|
static int isinstack (CallInfo *ci, const TValue *o) {
|
|
StkId p;
|
|
for (p = ci->u.l.base; p < ci->top; p++)
|
|
if (o == p) return 1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
static const char *getupvalname (CallInfo *ci, const TValue *o,
|
|
const char **name) {
|
|
LClosure *c = ci_func(ci);
|
|
int i;
|
|
for (i = 0; i < c->nupvalues; i++) {
|
|
if (c->upvals[i]->v == o) {
|
|
*name = upvalname(c->p, i);
|
|
return "upvalue";
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
l_noret luaG_typeerror (lua_State *L, const TValue *o, const char *op) {
|
|
CallInfo *ci = L->ci;
|
|
const char *name = NULL;
|
|
const char *t = objtypename(o);
|
|
const char *kind = NULL;
|
|
if (isLua(ci)) {
|
|
kind = getupvalname(ci, o, &name); /* check whether 'o' is an upvalue */
|
|
if (!kind && isinstack(ci, o)) /* no? try a register */
|
|
kind = getobjname(ci_func(ci)->p, currentpc(ci),
|
|
cast_int(o - ci->u.l.base), &name);
|
|
}
|
|
if (kind)
|
|
luaG_runerror(L, "attempt to %s %s " LUA_QS " (a %s value)",
|
|
op, kind, name, t);
|
|
else
|
|
luaG_runerror(L, "attempt to %s a %s value", op, t);
|
|
}
|
|
|
|
|
|
l_noret luaG_concaterror (lua_State *L, StkId p1, StkId p2) {
|
|
if (ttisstring(p1) || ttisnumber(p1)) p1 = p2;
|
|
lua_assert(!ttisstring(p1) && !ttisnumber(p1));
|
|
luaG_typeerror(L, p1, "concatenate");
|
|
}
|
|
|
|
|
|
l_noret luaG_aritherror (lua_State *L, const TValue *p1, const TValue *p2) {
|
|
TValue temp;
|
|
if (luaV_tonumber(p1, &temp) == NULL)
|
|
p2 = p1; /* first operand is wrong */
|
|
luaG_typeerror(L, p2, "perform arithmetic on");
|
|
}
|
|
|
|
|
|
l_noret luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) {
|
|
const char *t1 = objtypename(p1);
|
|
const char *t2 = objtypename(p2);
|
|
if (t1 == t2)
|
|
luaG_runerror(L, "attempt to compare two %s values", t1);
|
|
else
|
|
luaG_runerror(L, "attempt to compare %s with %s", t1, t2);
|
|
}
|
|
|
|
|
|
static void addinfo (lua_State *L, const char *msg) {
|
|
CallInfo *ci = L->ci;
|
|
if (isLua(ci)) { /* is Lua code? */
|
|
char buff[LUA_IDSIZE]; /* add file:line information */
|
|
int line = currentline(ci);
|
|
TString *src = ci_func(ci)->p->source;
|
|
if (src)
|
|
luaO_chunkid(buff, getstr(src), LUA_IDSIZE);
|
|
else { /* no source available; use "?" instead */
|
|
buff[0] = '?'; buff[1] = '\0';
|
|
}
|
|
luaO_pushfstring(L, "%s:%d: %s", buff, line, msg);
|
|
}
|
|
}
|
|
|
|
|
|
l_noret luaG_errormsg (lua_State *L) {
|
|
if (L->errfunc != 0) { /* is there an error handling function? */
|
|
StkId errfunc = restorestack(L, L->errfunc);
|
|
if (!ttisfunction(errfunc)) luaD_throw(L, LUA_ERRERR);
|
|
setobjs2s(L, L->top, L->top - 1); /* move argument */
|
|
setobjs2s(L, L->top - 1, errfunc); /* push function */
|
|
L->top++;
|
|
luaD_call(L, L->top - 2, 1, 0); /* call it */
|
|
}
|
|
luaD_throw(L, LUA_ERRRUN);
|
|
}
|
|
|
|
|
|
l_noret luaG_runerror (lua_State *L, const char *fmt, ...) {
|
|
L->runerror++;
|
|
va_list argp;
|
|
va_start(argp, fmt);
|
|
addinfo(L, luaO_pushvfstring(L, fmt, argp));
|
|
va_end(argp);
|
|
luaG_errormsg(L);
|
|
L->runerror--;
|
|
}
|
|
/* END CSTYLED */
|