├── README.md ├── examples └── dbg.lua ├── lua-gdb-python2.py ├── lua-gdb.py └── lua54_debug.sh /README.md: -------------------------------------------------------------------------------- 1 | # Lua-gdb 2 | gdb extension for lua5.3+. 3 | 4 | tested on GNU gdb (Ubuntu 7.11.1-0ubuntu1~16.5) 7.11.1. 5 | 6 | ## Features 7 | 8 | ### Pretty printer 9 | 10 | * struct TValue 11 | * struct TString 12 | * struct Table 13 | * struct LClosure 14 | * struct CClosure 15 | * struct lua_State 16 | 17 | ### Command 18 | * luacoroutines [L] 19 | 20 | List all coroutines. Without arguments, uses the current value of "L" as the lua_State*. You can provide an alternate lua_State as the first argument. 21 | 22 | 23 | * luastack [L] 24 | 25 | Prints values on the Lua C stack. Without arguments, uses the current value of "L" as the lua_State*. You can provide an alternate lua_State as the first argument. 26 | 27 | * luatraceback [L] 28 | 29 | Dumps Lua execution stack, as debug.traceback() does. Without arguments, uses the current value of "L" as the lua_State*. You can provide an alternate lua_State as the first argument. 30 | 31 | * luagetlocal [L [f]] 32 | 33 | Print all variables of the function at level 'f' of the stack 'coroutine'. With no arguments, Dump all variables of the current funtion in the stack of 'L'. 34 | 35 | ## Usage (step by step) 36 | 37 | * compile lua with debug symbols 38 | ``` 39 | cd lua-5.3.4 40 | make linux CFLAGS=-g 41 | ``` 42 | 43 | * start gdb 44 | ``` 45 | gdb lua-5.3.4/src/lua 46 | ``` 47 | 48 | * set a breakpoint 49 | ``` 50 | (gdb) break os_time 51 | Breakpoint 1 at 0x42c9fe: file loslib.c, line 324. 52 | ``` 53 | 54 | * run `examples/dbg.lua` 55 | ``` 56 | (gdb) run examples/dbg.lua 57 | Starting program: /usr/local/bin/lua examples/dbg.lua 58 | 59 | Breakpoint 1, os_time (L=0x64b9c8) at loslib.c:324 60 | 324 static int os_time (lua_State *L) { 61 | ``` 62 | 63 | will hit the breakpoint `os_time`. 64 | 65 | * load the extension 66 | ``` 67 | (gdb) source lua-gdb.py 68 | Loading Lua Runtime support. 69 | ``` 70 | 71 | * list all coroutines 72 | ``` 73 | (gdb) luacoroutines 74 | m = {[source] = [C]:-1, [func] = 0x427ff9 } 75 | = {[source] = [C]:-1, [func] = 0x42c9f2 } 76 | = {[source] = [C]:-1, [func] = ?} 77 | ``` 78 | 79 | * dump stack 80 | ``` 81 | (gdb) luastack 0x64b9c8 82 | #0 0x64bb30 83 | #1 0x64bb20 2 84 | #2 0x64bb10 5 85 | #3 0x64bb00 10 86 | #4 0x64baf0 "kkk" 87 | #5 0x64bae0 1 88 | #6 0x64bad0 "nil" 89 | #7 0x64bac0 "nil" 90 | #8 0x64bab0 = {[file] = "@examples/dbg.lua", [linestart] = 17, [lineend] = 20, [nupvalues] = 1 '\001'} 91 | ``` 92 | 93 | * dump traceback 94 | ``` 95 | (gdb) luatraceback 0x64b9c8 96 | stack traceback: 97 | [C]:-1: in 0x42c9f2 98 | "@examples/dbg.lua":19: in ? 99 | ``` 100 | 101 | * list all variables of a closure in the traceback 102 | ``` 103 | (gdb) luagetlocal 0x64b9c8 1 104 | call info: "@examples/dbg.lua":19: in ? 105 | upval _ENV = 3.2627937150349253e-317 106 | ..... (*vararg) = 1 107 | ..... (*vararg) = "kkk" 108 | local x = 10 109 | local i = 5 110 | local n = 2 111 | ``` 112 | 113 | * enjoy it! 114 | -------------------------------------------------------------------------------- /examples/dbg.lua: -------------------------------------------------------------------------------- 1 | local t = { 2 | "hello", 3 | "world", 4 | 1, 2, 3, 5 | vacant = nil, 6 | boolean = true, 7 | int = 5, 8 | float = 3.83, 9 | shrstr = 'short', 10 | lngstr = 'long ' .. string.rep('s', 50), 11 | func = function() end, 12 | co = coroutine.create(function() end), 13 | } 14 | -- hole 15 | t[4] = nil 16 | print(#t) 17 | -- loop 18 | t.t = t 19 | 20 | local function d1(x, i, ...) 21 | local n = select('#', ...) 22 | return os.time() + x + i + n 23 | end 24 | 25 | local function d0(x, t) 26 | local limit = math.random(20) 27 | if x > limit then 28 | local c = d1(0, 1) + 5 29 | end 30 | 31 | local co = coroutine.create(d1) 32 | local i = 5 33 | local j = 1 34 | local k = "kkk" 35 | local ok, v = coroutine.resume(co, x, i, j, k) 36 | return v 37 | end 38 | 39 | print(d0(10, t)) 40 | -------------------------------------------------------------------------------- /lua-gdb-python2.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import re 3 | import sys 4 | import gdb 5 | 6 | import traceback 7 | 8 | print("Loading Lua Runtime support.", file=sys.stderr) 9 | print("python: "+sys.version) 10 | 11 | #http://python3porting.com/differences.html 12 | if sys.version > '3': 13 | xrange = range 14 | 15 | # allow to manually reload while developing 16 | objfile = gdb.current_objfile() or gdb.objfiles()[0] 17 | objfile.pretty_printers = [] 18 | 19 | 20 | # gdb.Value to specific type tt 21 | def cast_to_type_pointer(o, tt): 22 | t = gdb.lookup_type(tt) 23 | return o.cast(t.pointer()) 24 | 25 | # basic types 26 | LUA_TNIL =0 27 | LUA_TBOOLEAN =1 28 | LUA_TLIGHTUSERDATA =2 29 | LUA_TNUMBER =3 30 | LUA_TSTRING =4 31 | LUA_TTABLE =5 32 | LUA_TFUNCTION =6 33 | LUA_TUSERDATA =7 34 | LUA_TTHREAD =8 35 | 36 | def makevariant(t, v): return t | (v << 4) 37 | def ctb(t): return t | (1 << 6) 38 | 39 | LUA_VFALSE = makevariant(LUA_TBOOLEAN, 0) 40 | LUA_VTRUE = makevariant(LUA_TBOOLEAN, 1) 41 | 42 | # test type 43 | def checktype(o, t): return o['tt_']&0x0f == t 44 | def checktag(o, t): return o['tt_'] == t 45 | 46 | # input GCObject* 47 | def cast_u(o,type_str): 48 | return cast_to_type_pointer(o,type_str) 49 | 50 | # -> 51 | def ivalue(o): return o['value_']['i'] 52 | 53 | # -> 54 | def fltvalue(o): return o['value_']['n'] 55 | 56 | # -> 57 | def pvalue(o): return o['value_']['p'] 58 | 59 | # -> 60 | def tsvalue(o): return cast_u(o['value_']['gc'],'TString') 61 | 62 | # -> 63 | def clLvalue(o): return cast_u(o['value_']['gc'],'LClosure') 64 | 65 | # -> 66 | def clCvalue(o): return cast_u(o['value_']['gc'],'CClosure') 67 | 68 | # -> 69 | def fvalue(o): return o['value_']['f'] 70 | 71 | # -> 72 | def hvalue(o): return cast_u(o['value_']['gc'],'Table') 73 | 74 | # -> 75 | def bvalue(o): return checktype(o, LUA_VTRUE) 76 | 77 | # -> 78 | def thvalue(o): return cast_u(o['value_']['gc'],'lua_State') 79 | 80 | LUA_VNUMINT = makevariant(LUA_TNUMBER, 0) 81 | LUA_VNUMFLT = makevariant(LUA_TNUMBER, 1) 82 | 83 | def ttisnumber(o): return checktype(o, LUA_TNUMBER) 84 | def ttisfloat(o): return checktag(o, LUA_VNUMFLT) 85 | def ttisinteger(o): return checktag(o, LUA_VNUMINT) 86 | def ttisnil(o): return checktype(o, LUA_TNIL) 87 | def ttisboolean(o): return checktype(o, LUA_TBOOLEAN) 88 | 89 | LUA_VLIGHTUSERDATA = makevariant(LUA_TLIGHTUSERDATA, 0) 90 | LUA_VUSERDATA = makevariant(LUA_TUSERDATA, 0) 91 | 92 | def ttislightuserdata(o): return checktag(o, LUA_VLIGHTUSERDATA) 93 | def ttisfulluserdata(o): return checktag(o, ctb(LUA_VUSERDATA)) 94 | 95 | LUA_VSHRSTR = makevariant(LUA_TSTRING, 0) 96 | LUA_VLNGSTR = makevariant(LUA_TSTRING, 1) 97 | 98 | def ttisstring(o): return checktype(o, LUA_TSTRING) 99 | def ttisshrstring(o): return checktype(o, ctb(LUA_VSHRSTR)) 100 | def ttislngstring(o): return checktype(o, ctb(LUA_VLNGSTR)) 101 | 102 | LUA_VTABLE = makevariant(LUA_TTABLE, 0) 103 | 104 | def ttistable(o): return checktag(o, ctb(LUA_VTABLE)) 105 | 106 | LUA_VLCL = makevariant(LUA_TFUNCTION, 0) 107 | LUA_VLCF = makevariant(LUA_TFUNCTION, 1) 108 | LUA_VCCL = makevariant(LUA_TFUNCTION, 2) 109 | 110 | def ttisfunction(o): return checktype(o, LUA_TFUNCTION) 111 | def ttisclosure(o): return o['tt_'] & 0x1f == LUA_VLCL 112 | def ttisCclosure(o): return checktag(o, ctb(LUA_VCCL)) 113 | def ttisLclosure(o): return checktag(o, ctb(LUA_VLCL)) 114 | def ttislcf(o): return checktag(o, LUA_VLCF) 115 | 116 | LUA_VTHREAD = makevariant(LUA_TTHREAD, 0) 117 | 118 | def ttisthread(o): return checktag(o, ctb(LUA_VTHREAD)) 119 | 120 | # gdb.Value to string 121 | def value_to_string(val): 122 | s = str(val.dereference()) 123 | if len(s) > 1 and s[0] == '"' and s[-1] == '"': 124 | return s[1:-1] 125 | return s 126 | 127 | def cast_luaState(o): 128 | return cast_to_type_pointer(o, "struct lua_State") 129 | 130 | # 131 | # Value wrappers 132 | # 133 | 134 | # StackValue to TValue 135 | def s2v(stk): return cast_u(stk,'TValue') 136 | 137 | # struct lua_TValue 138 | class TValueValue: 139 | "Wrapper for TValue value." 140 | 141 | def __init__(self, val): 142 | self.val = val 143 | 144 | def upvars(self): 145 | if ttisCclosure(self.val): 146 | f = clCvalue(self.val) 147 | for i in xrange(f['nupvalues']): 148 | yield "(%d)" % (i+1), cast_to_type_pointer(f['upvalue'], "TValue") + i 149 | elif ttisLclosure(self.val): 150 | f = clLvalue(self.val) 151 | proto = f['p'] 152 | for i in xrange(int(proto['sizeupvalues'])): 153 | uv = cast_to_type_pointer(f['upvals'][i], "struct UpVal") 154 | value = cast_u(uv['v'],'TValue') 155 | name = (proto['upvalues'] + i)['name'] 156 | if name: 157 | yield value_to_string(name), value 158 | else: 159 | yield "(no name)", value 160 | 161 | # struct CallInfo 162 | class CallInfoValue: 163 | "Wrapper for CallInfo value." 164 | 165 | CIST_C = 1<<1 166 | CIST_TAIL = 1<<5 167 | CIST_FIN = 1<<7 168 | 169 | def __init__(self, L, ci): 170 | self.L = L 171 | self.ci = ci 172 | 173 | self.name = None 174 | self.namewhat = None 175 | 176 | if self.is_lua(): 177 | proto = clLvalue(s2v(self.ci['func']))['p'] 178 | self.proto = proto 179 | 180 | if not proto['source']: 181 | self.source = "?" 182 | else: 183 | self.source = proto['source'].dereference() 184 | 185 | self.linedefined = proto['linedefined'] 186 | self.lastlinedefined = proto['lastlinedefined'] 187 | 188 | if self.linedefined == 0: 189 | self.what = "main" 190 | else: 191 | self.what = "Lua" 192 | 193 | self.currentpc = (self.ci['u']['l']['savedpc'] - proto['code']) - 1 194 | 195 | self.currentline = self.getfuncline(proto, self.currentpc) 196 | 197 | else: 198 | self.source = "[C]" 199 | self.linedefined = -1 200 | self.lastlinedefined = -1 201 | self.what = "C" 202 | self.currentline = -1 203 | 204 | if self.is_fin(): 205 | self.name = "__gc" 206 | self.namewhat = "metamethod" 207 | 208 | def getfuncline(self, proto, pc): 209 | """ 210 | ldebug.c luaG_getfuncline 211 | """ 212 | if not proto['lineinfo']: 213 | return -1 214 | def getbaseline(proto, pc): 215 | if proto['sizeabslineinfo'] == 0 or pc < proto['abslineinfo'][0]['pc']: 216 | return -1, proto['linedefined'] 217 | if pc >= proto['abslineinfo'][proto['sizeabslineinfo']-1]['pc']: 218 | i = proto['sizeabslineinfo']-1 219 | else: 220 | j = proto['sizeabslineinfo']-1 221 | i = 0 222 | while i < j - 1: 223 | m = (j + i) / 2 224 | if pc >= proto['abslineinfo'][m]['pc']: 225 | i = m 226 | else: 227 | j = m 228 | return proto['abslineinfo'][i]['pc'], proto['abslineinfo'][i]['line'] 229 | basepc, baseline = getbaseline(proto, pc) 230 | while basepc < pc: 231 | basepc+=1 232 | baseline += proto['lineinfo'][basepc] 233 | return baseline 234 | 235 | @property 236 | def funcname(self): 237 | if self.what == "main": 238 | return "main chunk" 239 | if self.namewhat: 240 | return "%s '%s'" % (self.namewhat, self.name) 241 | func = s2v(self.ci['func']) 242 | if ttislcf(func): 243 | return "%s" % fvalue(func) 244 | 245 | if ttisCclosure(func): 246 | return "%s" % clCvalue(func)['f'] 247 | 248 | return '?' 249 | 250 | def is_lua(self): 251 | return not (self.ci['callstatus'] & CallInfoValue.CIST_C) 252 | 253 | def is_tailcall(self): 254 | return self.ci['callstatus'] & CallInfoValue.CIST_TAIL 255 | 256 | def is_fin(self): 257 | return self.ci['callstatus'] & CallInfoValue.CIST_FIN 258 | 259 | # stack frame information 260 | def frame_info(self): 261 | return '%s:%d: in %s' % (self.source, self.currentline, self.funcname) 262 | 263 | # luastack: 264 | # vararg(1) 265 | # ... 266 | # vararg(nextraargs) <- ci->u.l.nextraargs nextra vararg 267 | # callee <- ci->func 268 | # arg(1) 269 | # ... 270 | # arg(n) 271 | # local(1) 272 | # ... 273 | # local(n) 274 | @property 275 | def stack_base(self): 276 | return cast_u(self.ci['func'],'TValue') + 1 277 | 278 | @property 279 | def stack_top(self): 280 | if self.ci == self.L['ci']: 281 | return cast_u(self.L['top'],'TValue') 282 | else: 283 | nextcv = CallInfoValue(self.L, self.ci['next']) 284 | return nextcv.stack_base - 1 285 | 286 | def getlocalname(self, n): 287 | if not self.is_lua(): 288 | return None 289 | 290 | proto = self.proto 291 | currentpc = self.currentpc 292 | 293 | i = 0 294 | while i< proto['sizelocvars']: 295 | locvar = proto['locvars'] + i 296 | if locvar['startpc'] <= currentpc and currentpc < locvar['endpc']: 297 | n = n - 1 298 | if n == 0: 299 | return value_to_string(locvar['varname']) 300 | i = i + 1 301 | return None 302 | 303 | def upvars(self): 304 | tv = TValueValue(s2v(self.ci['func'])) 305 | return tv.upvars() 306 | 307 | def varargs(self): 308 | if not self.is_lua(): 309 | return 310 | 311 | if self.proto['is_vararg'] != 1: 312 | return 313 | 314 | nextra = self.ci['u']['l']['nextraargs'] 315 | for i in xrange(nextra): 316 | yield "(*vararg)", s2v(self.ci['func'] - (i+1)) 317 | 318 | def locvars(self): 319 | base = self.stack_base 320 | limit = self.stack_top 321 | i = 1 322 | while True: 323 | name = self.getlocalname(i) 324 | if not name: 325 | if limit - base >= i: 326 | if self.is_lua(): 327 | name = "(temporary)" 328 | else: 329 | name = "(C temporary)" 330 | else: 331 | return 332 | yield name, s2v(base + i - 1) 333 | i = i + 1 334 | 335 | # 336 | # Pretty Printers 337 | # 338 | 339 | def tvaluestring(value): 340 | if ttisnil(value): # nil 341 | return "nil" 342 | elif ttisboolean(value): # boolean 343 | if bvalue(value) > 0: 344 | return "True" 345 | else: 346 | return "False" 347 | elif ttisnumber(value): # number 348 | if ttisfloat(value): 349 | return fltvalue(value) 350 | elif ttisinteger(value): 351 | return ivalue(value) 352 | elif ttisstring(value): # string 353 | return tsvalue(value) 354 | elif ttistable(value): # table 355 | return hvalue(value) 356 | elif ttisfunction(value): 357 | if ttisLclosure(value): # lua closure 358 | return clLvalue(value) 359 | elif ttislcf(value): # light C function 360 | return fvalue(value) 361 | elif ttisCclosure(value): # 2 C closure 362 | return clCvalue(value) 363 | elif ttisfulluserdata(value): 364 | return "Userdata" 365 | elif ttislightuserdata(value): # lightuserdata 366 | return "" % int(pvalue(value)) 367 | elif ttisthread(value): 368 | return thvalue(value) 369 | assert False, value['tt_'] 370 | 371 | class TValuePrinter: 372 | "Pretty print lua value." 373 | 374 | pattern = re.compile(r'^(struct TValue)|(TValue)$') 375 | 376 | def __init__(self, val): 377 | self.val = val 378 | 379 | def to_string(self): 380 | return tvaluestring(self.val) 381 | 382 | def display_hint(self): 383 | return "string" 384 | 385 | class TStringPrinter: 386 | "Pretty print lua string." 387 | 388 | pattern = re.compile(r'^(struct TString)|(TString)$') 389 | 390 | def __init__(self, val): 391 | self.val = val 392 | 393 | def display_hint(self): 394 | return "string" 395 | 396 | def to_string(self): 397 | s = self.val["contents"] 398 | return s.cast(gdb.lookup_type('char').pointer()) 399 | 400 | class TablePrinter: 401 | "Pretty print lua table." 402 | 403 | pattern = re.compile(r'^(struct Table)|(Table)$') 404 | marked = None 405 | 406 | def __init__(self, val): 407 | self.val = val 408 | 409 | def display_hint(self): 410 | return "map" 411 | 412 | def to_string(self): 413 | return "
" % int(self.val.address) 414 | 415 | def children(self): 416 | setMarked = False 417 | if TablePrinter.marked == None: 418 | TablePrinter.marked = {} 419 | setMarked = True 420 | 421 | address = int(self.val.address) 422 | if address in TablePrinter.marked: 423 | return TablePrinter.marked[address] 424 | TablePrinter.marked[address] = self.to_string() 425 | 426 | # array part 427 | sizearray = self.realasize() 428 | i = 0 429 | result = [] 430 | while i < sizearray: 431 | val = self.val['array'][i] 432 | if ttisnil(val): 433 | i = i + 1 434 | continue 435 | result.append((str(2*i), i)) 436 | result.append((str(2*i + 1), val)) 437 | i = i + 1 438 | 439 | # hash part 440 | j = 0 441 | last = 1 << self.val['lsizenode'] 442 | while j < last: 443 | node = self.val['node'][j] 444 | j = j + 1 445 | value = node['i_val'] 446 | if ttisnil(value): 447 | continue 448 | fakeTValue = { 449 | "tt_": node['u']['key_tt'], 450 | "value_": node['u']['key_val'] 451 | } 452 | result.append(str(2*i + 2*j), tvaluestring(fakeTValue)) 453 | result.append(str(2*i + 2*j + 1), value) 454 | 455 | if setMarked: 456 | TablePrinter.marked = None 457 | 458 | return result 459 | 460 | def realasize(self): 461 | def isrealasize(self): return (self.val['flags'] & (1<<7)) == 0 462 | def ispow2(x): return (((x) & ((x) - 1)) == 0) 463 | if (isrealasize(self) or ispow2(self.val['alimit'])): 464 | return self.val['alimit'] 465 | else: 466 | size = self.val['alimit'] 467 | size |= (size >> 1) 468 | size |= (size >> 2) 469 | size |= (size >> 4) 470 | size |= (size >> 8) 471 | size |= (size >> 16) 472 | size += 1 473 | return size 474 | 475 | 476 | class LClosurePrinter: 477 | "Pretty print lua closure." 478 | 479 | pattern = re.compile(r'^(struct LClosure)|(LClosure)$') 480 | 481 | def __init__(self, val): 482 | self.val = val 483 | 484 | def display_hint(self): 485 | return "map" 486 | 487 | def to_string(self): 488 | return "" % int(self.val.address) 489 | 490 | def children(self): 491 | p = self.val['p'] 492 | result = [] 493 | result.append("1", "file") 494 | result.append("2", p['source'].dereference()) 495 | result.append("3", "linestart") 496 | result.append("4", p['linedefined']) 497 | result.append("5", "lineend") 498 | result.append("6", p['lastlinedefined']) 499 | result.append("7", "nupvalues") 500 | result.append("8", self.val['nupvalues']) 501 | return result 502 | 503 | class CClosurePrinter: 504 | "Pretty print lua closure." 505 | 506 | pattern = re.compile(r'^(struct CClosure)|(CClosure)$') 507 | 508 | def __init__(self, val): 509 | self.val = val 510 | 511 | def display_hint(self): 512 | return "map" 513 | 514 | def to_string(self): 515 | return "" % int(self.val.address) 516 | 517 | def children(self): 518 | result = [] 519 | result.append("1", "nupvalues") 520 | result.append("2", self.val['nupvalues']) 521 | return result 522 | 523 | class LuaStatePrinter: 524 | "Pretty print lua_State." 525 | 526 | pattern = re.compile(r'^struct lua_State$') 527 | 528 | def __init__(self, val): 529 | self.val = val 530 | 531 | def display_hint(self): 532 | return "map" 533 | 534 | def to_string(self): 535 | return "" % int(self.val.address) 536 | 537 | def children(self): 538 | cv = CallInfoValue(self.val, self.val['ci']) 539 | result = [] 540 | result.append( "1", "source") 541 | result.append( "2", "%s:%d" % (cv.source, cv.currentline)) 542 | result.append( "3", "func") 543 | result.append( "4", cv.funcname) 544 | return result 545 | 546 | # 547 | # Register all the *Printer classes above. 548 | # 549 | 550 | def makematcher(klass): 551 | def matcher(val): 552 | try: 553 | if klass.pattern.match(str(val.type)): 554 | return klass(val) 555 | except Exception: 556 | pass 557 | return matcher 558 | 559 | objfile.pretty_printers.extend([makematcher(var) for var in vars().values() if hasattr(var, 'pattern')]) 560 | 561 | class LuaStackCmd(gdb.Command): 562 | """luastack [L] 563 | Prints values on the Lua C stack. Without arguments, uses the current value of "L" 564 | as the lua_State*. You can provide an alternate lua_State as the first argument.""" 565 | 566 | def __init__(self): 567 | gdb.Command.__init__(self, "luastack", gdb.COMMAND_STACK, gdb.COMPLETE_NONE) 568 | 569 | def invoke(self, args, _from_tty): 570 | try: 571 | argv = gdb.string_to_argv(args) 572 | if len(argv) > 0: 573 | L = cast_luaState(gdb.parse_and_eval(argv[0])) 574 | else: 575 | L = gdb.parse_and_eval("L") 576 | stack = L['top']['p'] - 1 577 | 578 | i = 0 579 | while stack > L['stack']['p']: 580 | print("#%d\t%s\t%s" % (i, str(stack), stack['tbclist'])) 581 | stack = stack - 1 582 | i = i + 1 583 | except Exception as e: 584 | traceback.print_exc() 585 | 586 | class LuaTracebackCmd(gdb.Command): 587 | """luabacktrace [L] 588 | Dumps Lua execution stack, as debug.traceback() does. Without 589 | arguments, uses the current value of "L" as the 590 | lua_State*. You can provide an alternate lua_State as the 591 | first argument. 592 | """ 593 | def __init__(self): 594 | gdb.Command.__init__(self, "luatraceback", gdb.COMMAND_STACK, gdb.COMPLETE_NONE) 595 | 596 | def invoke(self, args, _from_tty): 597 | argv = gdb.string_to_argv(args) 598 | if len(argv) > 0: 599 | L = cast_luaState(gdb.parse_and_eval(argv[0])) 600 | else: 601 | L = gdb.parse_and_eval("L") 602 | 603 | ci = L['ci'] 604 | print("stack traceback:") 605 | while ci != L['base_ci'].address: 606 | cv = CallInfoValue(L, ci) 607 | print('\t%s' % (cv.frame_info())) 608 | if cv.is_tailcall(): 609 | print('\t(...tail calls...)') 610 | ci = ci['previous'] 611 | 612 | thread_status_tbl = { 613 | 0:"running", 614 | 1:"dead", 615 | 2:"suspended", 616 | 3:"normal" 617 | } 618 | 619 | class LuaCoroutinesCmd(gdb.Command): 620 | """luacoroutines [L] 621 | List all coroutines. Without arguments, uses the current value of "L" as the 622 | lua_State*. You can provide an alternate lua_State as the 623 | first argument. 624 | """ 625 | def __init__(self): 626 | gdb.Command.__init__(self, "luacoroutines", gdb.COMMAND_STACK, gdb.COMPLETE_NONE) 627 | 628 | def invoke(self, args, _from_tty): 629 | argv = gdb.string_to_argv(args) 630 | if len(argv) > 0: 631 | L = cast_luaState(gdb.parse_and_eval(argv[0])) 632 | else: 633 | L = gdb.parse_and_eval("L") 634 | # global_State 635 | lG = L['l_G'] 636 | # mainthread 637 | print("mainthread", lG['mainthread']) 638 | 639 | obj = lG['allgc'] 640 | while obj: 641 | if obj['tt'] == 8: 642 | th = cast_u(obj,"lua_State") 643 | print("thread", th, thread_status_tbl[int(th['status'])]) 644 | obj = obj['next'] 645 | 646 | def dereference(v): 647 | t = v.type 648 | print(t,t.code==gdb.TYPE_CODE_PTR) 649 | if t.code == gdb.TYPE_CODE_PTR: 650 | return v.dereference() 651 | return str(v) 652 | 653 | class LuaGetLocalCmd(gdb.Command): 654 | """luagetlocal [L [f]] 655 | Print all local variables of the function at level 'f' of the stack 'thread'. 656 | With no arguments, Dump all local variable of the current funtion in the stack of 'L'; 657 | """ 658 | def __init__(self): 659 | gdb.Command.__init__(self, "luagetlocal", gdb.COMMAND_STACK, gdb.COMPLETE_NONE) 660 | 661 | def invoke(self, args, _from_tty): 662 | try: 663 | argv = gdb.string_to_argv(args) 664 | if len(argv) > 0: 665 | L = cast_luaState(gdb.parse_and_eval(argv[0])) 666 | else: 667 | L = gdb.parse_and_eval("L") 668 | 669 | if len(argv) > 1: 670 | arg2 = gdb.parse_and_eval(argv[1]) 671 | else: 672 | arg2 = gdb.parse_and_eval("0") 673 | 674 | level = arg2 675 | ci = L['ci'] 676 | while level > 0: 677 | ci = ci['previous'] 678 | if ci == L['base_ci'].address: 679 | break 680 | level = level - 1 681 | 682 | if level != 0: 683 | print("No function at level %d" % arg2) 684 | return 685 | 686 | cv = CallInfoValue(L, ci) 687 | print("call info: %s" % cv.frame_info()) 688 | 689 | for name, var in cv.upvars(): 690 | print("\tupval %s = %s" % (name, var.dereference())) 691 | 692 | for name, var in cv.varargs(): 693 | print("\t..... %s = %s" % (name, var.dereference())) 694 | 695 | for name, var in cv.locvars(): 696 | print("\tlocal %s = %s" % (name, tvaluestring(var))) 697 | #print("\tlocal %s = %s" % (name, var.dereference())) 698 | except Exception as e: 699 | traceback.print_exc() 700 | 701 | LuaStackCmd() 702 | LuaTracebackCmd() 703 | LuaCoroutinesCmd() 704 | LuaGetLocalCmd() 705 | 706 | # set print elements 0, 无限制显示所有内容(table较大时) -------------------------------------------------------------------------------- /lua-gdb.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import re 3 | import sys 4 | 5 | print("Loading Lua Runtime support.", file=sys.stderr) 6 | #http://python3porting.com/differences.html 7 | if sys.version > '3': 8 | xrange = range 9 | 10 | # allow to manually reload while developing 11 | objfile = gdb.current_objfile() or gdb.objfiles()[0] 12 | objfile.pretty_printers = [] 13 | 14 | 15 | # gdb.Value to specific type tt 16 | def cast_to_type_pointer(o, tt): 17 | t = gdb.lookup_type(tt) 18 | return o.cast(t.pointer()) 19 | 20 | # basic types 21 | LUA_TNIL =0 22 | LUA_TBOOLEAN =1 23 | LUA_TLIGHTUSERDATA =2 24 | LUA_TNUMBER =3 25 | LUA_TSTRING =4 26 | LUA_TTABLE =5 27 | LUA_TFUNCTION =6 28 | LUA_TUSERDATA =7 29 | LUA_TTHREAD =8 30 | 31 | def makevariant(t, v): return t | (v << 4) 32 | def ctb(t): return t | (1 << 6) 33 | 34 | LUA_VFALSE = makevariant(LUA_TBOOLEAN, 0) 35 | LUA_VTRUE = makevariant(LUA_TBOOLEAN, 1) 36 | 37 | # test type 38 | def checktype(o, t): return o['tt_']&0x0f == t 39 | def checktag(o, t): return o['tt_'] == t 40 | 41 | # input GCObject* 42 | def cast_u(o): 43 | return cast_to_type_pointer(o, "union GCUnion") 44 | 45 | # -> 46 | def ivalue(o): return o['value_']['i'] 47 | 48 | # -> 49 | def fltvalue(o): return o['value_']['n'] 50 | 51 | # -> 52 | def pvalue(o): return o['value_']['p'] 53 | 54 | # -> 55 | def tsvalue(o): return cast_u(o['value_']['gc'])['ts'] 56 | 57 | # -> 58 | def clLvalue(o): return cast_u(o['value_']['gc'])['cl']['l'] 59 | 60 | # -> 61 | def clCvalue(o): return cast_u(o['value_']['gc'])['cl']['c'] 62 | 63 | # -> 64 | def fvalue(o): return o['value_']['f'] 65 | 66 | # ->
67 | def hvalue(o): return cast_u(o['value_']['gc'])['h'] 68 | 69 | # -> 70 | def bvalue(o): return checktype(o, LUA_VTRUE) 71 | 72 | # -> 73 | def thvalue(o): return cast_u(o['value_']['gc'])['th'] 74 | 75 | LUA_VNUMINT = makevariant(LUA_TNUMBER, 0) 76 | LUA_VNUMFLT = makevariant(LUA_TNUMBER, 1) 77 | 78 | def ttisnumber(o): return checktype(o, LUA_TNUMBER) 79 | def ttisfloat(o): return checktag(o, LUA_VNUMFLT) 80 | def ttisinteger(o): return checktag(o, LUA_VNUMINT) 81 | def ttisnil(o): return checktype(o, LUA_TNIL) 82 | def ttisboolean(o): return checktype(o, LUA_TBOOLEAN) 83 | 84 | LUA_VLIGHTUSERDATA = makevariant(LUA_TLIGHTUSERDATA, 0) 85 | LUA_VUSERDATA = makevariant(LUA_TUSERDATA, 0) 86 | 87 | def ttislightuserdata(o): return checktag(o, LUA_VLIGHTUSERDATA) 88 | def ttisfulluserdata(o): return checktag(o, ctb(LUA_VUSERDATA)) 89 | 90 | LUA_VSHRSTR = makevariant(LUA_TSTRING, 0) 91 | LUA_VLNGSTR = makevariant(LUA_TSTRING, 1) 92 | 93 | def ttisstring(o): return checktype(o, LUA_TSTRING) 94 | def ttisshrstring(o): return checktype(o, ctb(LUA_VSHRSTR)) 95 | def ttislngstring(o): return checktype(o, ctb(LUA_VLNGSTR)) 96 | 97 | LUA_VTABLE = makevariant(LUA_TTABLE, 0) 98 | 99 | def ttistable(o): return checktag(o, ctb(LUA_VTABLE)) 100 | 101 | LUA_VLCL = makevariant(LUA_TFUNCTION, 0) 102 | LUA_VLCF = makevariant(LUA_TFUNCTION, 1) 103 | LUA_VCCL = makevariant(LUA_TFUNCTION, 2) 104 | 105 | def ttisfunction(o): return checktype(o, LUA_TFUNCTION) 106 | def ttisclosure(o): return o['tt_'] & 0x1f == LUA_VLCL 107 | def ttisCclosure(o): return checktag(o, ctb(LUA_VCCL)) 108 | def ttisLclosure(o): return checktag(o, ctb(LUA_VLCL)) 109 | def ttislcf(o): return checktag(o, LUA_VLCF) 110 | 111 | LUA_VTHREAD = makevariant(LUA_TTHREAD, 0) 112 | 113 | def ttisthread(o): return checktag(o, ctb(LUA_VTHREAD)) 114 | 115 | # gdb.Value to string 116 | def value_to_string(val): 117 | s = str(val.dereference()) 118 | if len(s) > 1 and s[0] == '"' and s[-1] == '"': 119 | return s[1:-1] 120 | return s 121 | 122 | def cast_luaState(o): 123 | return cast_to_type_pointer(o, "struct lua_State") 124 | 125 | # 126 | # Value wrappers 127 | # 128 | 129 | # StackValue to TValue 130 | def s2v(stk): return stk['val'] 131 | 132 | # struct lua_TValue 133 | class TValueValue: 134 | "Wrapper for TValue value." 135 | 136 | def __init__(self, val): 137 | self.val = val 138 | 139 | def upvars(self): 140 | if ttisCclosure(self.val): 141 | f = clCvalue(self.val) 142 | for i in xrange(f['nupvalues']): 143 | yield "(%d)" % (i+1), cast_to_type_pointer(f['upvalue'], "TValue") + i 144 | elif ttisLclosure(self.val): 145 | f = clLvalue(self.val) 146 | proto = f['p'] 147 | for i in xrange(int(proto['sizeupvalues'])): 148 | uv = cast_to_type_pointer(f['upvals'][i], "struct UpVal") 149 | value = uv['v'] 150 | name = (proto['upvalues'] + i)['name'] 151 | if name: 152 | yield value_to_string(name), value 153 | else: 154 | yield "(no name)", value 155 | 156 | # struct CallInfo 157 | class CallInfoValue: 158 | "Wrapper for CallInfo value." 159 | 160 | CIST_C = 1<<1 161 | CIST_TAIL = 1<<5 162 | CIST_FIN = 1<<7 163 | 164 | def __init__(self, L, ci): 165 | self.L = L 166 | self.ci = ci 167 | 168 | self.name = None 169 | self.namewhat = None 170 | 171 | if self.is_lua(): 172 | proto = clLvalue(s2v(self.ci['func']))['p'] 173 | self.proto = proto 174 | 175 | if not proto['source']: 176 | self.source = "?" 177 | else: 178 | self.source = proto['source'].dereference() 179 | 180 | self.linedefined = proto['linedefined'] 181 | self.lastlinedefined = proto['lastlinedefined'] 182 | 183 | if self.linedefined == 0: 184 | self.what = "main" 185 | else: 186 | self.what = "Lua" 187 | 188 | self.currentpc = (self.ci['u']['l']['savedpc'] - proto['code']) - 1 189 | self.currentline = self.getfuncline(proto, self.currentpc) 190 | 191 | else: 192 | self.source = "[C]" 193 | self.linedefined = -1 194 | self.lastlinedefined = -1 195 | self.what = "C" 196 | self.currentline = -1 197 | 198 | if self.is_fin(): 199 | self.name = "__gc" 200 | self.namewhat = "metamethod" 201 | 202 | def getfuncline(self, proto, pc): 203 | """ 204 | ldebug.c luaG_getfuncline 205 | """ 206 | if not proto['lineinfo']: 207 | return -1 208 | def getbaseline(proto, pc): 209 | if proto['sizeabslineinfo'] == 0 or pc < proto['abslineinfo'][0]['pc']: 210 | return -1, proto['linedefined'] 211 | if pc >= proto['abslineinfo'][proto['sizeabslineinfo']-1]['pc']: 212 | i = proto['sizeabslineinfo']-1 213 | else: 214 | j = proto['sizeabslineinfo']-1 215 | i = 0 216 | while i < j - 1: 217 | m = (j + i) / 2 218 | if pc >= proto['abslineinfo'][m]['pc']: 219 | i = m 220 | else: 221 | j = m 222 | return proto['abslineinfo'][i]['pc'], proto['abslineinfo'][i]['line'] 223 | basepc, baseline = getbaseline(proto, pc) 224 | while basepc < pc: 225 | basepc+=1 226 | baseline += proto['lineinfo'][basepc] 227 | return baseline 228 | 229 | @property 230 | def funcname(self): 231 | if self.what == "main": 232 | return "main chunk" 233 | 234 | if self.namewhat: 235 | return "%s '%s'" % (self.namewhat, self.name) 236 | 237 | func = s2v(self.ci['func']) 238 | if ttislcf(func): 239 | return "%s" % fvalue(func) 240 | 241 | if ttisCclosure(func): 242 | return "%s" % clCvalue(func)['f'] 243 | 244 | return '?' 245 | 246 | def is_lua(self): 247 | return not (self.ci['callstatus'] & CallInfoValue.CIST_C) 248 | 249 | def is_tailcall(self): 250 | return self.ci['callstatus'] & CallInfoValue.CIST_TAIL 251 | 252 | def is_fin(self): 253 | return self.ci['callstatus'] & CallInfoValue.CIST_FIN 254 | 255 | # stack frame information 256 | def frame_info(self): 257 | return '%s:%d: in %s' % (self.source, self.currentline, self.funcname) 258 | 259 | # luastack: 260 | # vararg(1) 261 | # ... 262 | # vararg(nextraargs) <- ci->u.l.nextraargs nextra vararg 263 | # callee <- ci->func 264 | # arg(1) 265 | # ... 266 | # arg(n) 267 | # local(1) 268 | # ... 269 | # local(n) 270 | @property 271 | def stack_base(self): 272 | return self.ci['func'] + 1 273 | 274 | @property 275 | def stack_top(self): 276 | if self.ci == self.L['ci']: 277 | return self.L['top'] 278 | else: 279 | nextcv = CallInfoValue(self.L, self.ci['next']) 280 | return nextcv.stack_base - 1 281 | 282 | def getlocalname(self, n): 283 | if not self.is_lua(): 284 | return None 285 | 286 | proto = self.proto 287 | currentpc = self.currentpc 288 | 289 | i = 0 290 | while i< proto['sizelocvars']: 291 | locvar = proto['locvars'] + i 292 | if locvar['startpc'] <= currentpc and currentpc < locvar['endpc']: 293 | n = n - 1 294 | if n == 0: 295 | return value_to_string(locvar['varname']) 296 | i = i + 1 297 | return None 298 | 299 | def upvars(self): 300 | tv = TValueValue(s2v(self.ci['func'])) 301 | return tv.upvars() 302 | 303 | def varargs(self): 304 | if not self.is_lua(): 305 | return 306 | 307 | if self.proto['is_vararg'] != 1: 308 | return 309 | 310 | nextra = self.ci['u']['l']['nextraargs'] 311 | for i in xrange(nextra): 312 | yield "(*vararg)", s2v(self.ci['func'] - (i+1)).address 313 | 314 | def locvars(self): 315 | base = self.stack_base 316 | limit = self.stack_top 317 | i = 1 318 | while True: 319 | name = self.getlocalname(i) 320 | if not name: 321 | if (limit - base) >= i: 322 | if self.is_lua(): 323 | name = "(temporary)" 324 | else: 325 | name = "(C temporary)" 326 | else: 327 | return 328 | yield name, s2v(base + i - 1).address 329 | i = i + 1 330 | 331 | # 332 | # Pretty Printers 333 | # 334 | 335 | def tvaluestring(value): 336 | if ttisnil(value): # nil 337 | return "nil" 338 | elif ttisboolean(value): # boolean 339 | if bvalue(value) > 0: 340 | return "True" 341 | else: 342 | return "False" 343 | elif ttisnumber(value): # number 344 | if ttisfloat(value): 345 | return fltvalue(value) 346 | elif ttisinteger(value): 347 | return ivalue(value) 348 | elif ttisstring(value): # string 349 | return tsvalue(value) 350 | elif ttistable(value): # table 351 | return hvalue(value) 352 | elif ttisfunction(value): 353 | if ttisLclosure(value): # lua closure 354 | return clLvalue(value) 355 | elif ttislcf(value): # light C function 356 | return fvalue(value) 357 | elif ttisCclosure(value): # 2 C closure 358 | return clCvalue(value) 359 | elif ttisfulluserdata(value): 360 | return "Userdata" 361 | elif ttislightuserdata(value): # lightuserdata 362 | return "" % int(pvalue(value)) 363 | elif ttisthread(value): 364 | return thvalue(value) 365 | assert False, value['tt_'] 366 | 367 | class TValuePrinter: 368 | "Pretty print lua value." 369 | 370 | pattern = re.compile(r'^(struct TValue)|(TValue)$') 371 | 372 | def __init__(self, val): 373 | self.val = val 374 | 375 | def to_string(self): 376 | return tvaluestring(self.val) 377 | 378 | def display_hint(self): 379 | return "string" 380 | 381 | class TStringPrinter: 382 | "Pretty print lua string." 383 | 384 | pattern = re.compile(r'^(struct TString)|(TString)$') 385 | 386 | def __init__(self, val): 387 | self.val = val 388 | 389 | def display_hint(self): 390 | return "string" 391 | 392 | def to_string(self): 393 | s = self.val["contents"] 394 | return s.cast(gdb.lookup_type('char').pointer()) 395 | 396 | class TablePrinter: 397 | "Pretty print lua table." 398 | 399 | pattern = re.compile(r'^(struct Table)|(Table)$') 400 | marked = None 401 | 402 | def __init__(self, val): 403 | self.val = val 404 | 405 | def display_hint(self): 406 | return "map" 407 | 408 | def to_string(self): 409 | return "
" % int(self.val.address) 410 | 411 | def children(self): 412 | setMarked = False 413 | if TablePrinter.marked == None: 414 | TablePrinter.marked = {} 415 | setMarked = True 416 | 417 | address = int(self.val.address) 418 | if address in TablePrinter.marked: 419 | return TablePrinter.marked[address] 420 | TablePrinter.marked[address] = self.to_string() 421 | 422 | # array part 423 | sizearray = self.realasize() 424 | i = 0 425 | while i < sizearray: 426 | val = self.val['array'][i] 427 | if ttisnil(val): 428 | i = i + 1 429 | continue 430 | yield str(2*i), i 431 | yield str(2*i + 1), val 432 | i = i + 1 433 | 434 | # hash part 435 | j = 0 436 | last = 1 << self.val['lsizenode'] 437 | while j < last: 438 | node = self.val['node'][j] 439 | j = j + 1 440 | value = node['i_val'] 441 | if ttisnil(value): 442 | continue 443 | fakeTValue = { 444 | "tt_": node['u']['key_tt'], 445 | "value_": node['u']['key_val'] 446 | } 447 | yield str(2*i + 2*j), tvaluestring(fakeTValue) 448 | yield str(2*i + 2*j + 1), value 449 | 450 | if setMarked: 451 | TablePrinter.marked = None 452 | 453 | def realasize(self): 454 | def isrealasize(self): return (self.val['flags'] & (1<<7)) == 0 455 | def ispow2(x): return (((x) & ((x) - 1)) == 0) 456 | if (isrealasize(self) or ispow2(self.val['alimit'])): 457 | return self.val['alimit'] 458 | else: 459 | size = self.val['alimit'] 460 | size |= (size >> 1) 461 | size |= (size >> 2) 462 | size |= (size >> 4) 463 | size |= (size >> 8) 464 | size |= (size >> 16) 465 | size += 1 466 | return size 467 | 468 | 469 | class LClosurePrinter: 470 | "Pretty print lua closure." 471 | 472 | pattern = re.compile(r'^(struct LClosure)|(LClosure)$') 473 | 474 | def __init__(self, val): 475 | self.val = val 476 | 477 | def display_hint(self): 478 | return "map" 479 | 480 | def to_string(self): 481 | return "" % int(self.val.address) 482 | 483 | def children(self): 484 | p = self.val['p'] 485 | yield "1", "file" 486 | yield "2", p['source'].dereference() 487 | yield "3", "linestart" 488 | yield "4", p['linedefined'] 489 | yield "5", "lineend" 490 | yield "6", p['lastlinedefined'] 491 | yield "7", "nupvalues" 492 | yield "8", self.val['nupvalues'] 493 | 494 | class CClosurePrinter: 495 | "Pretty print lua closure." 496 | 497 | pattern = re.compile(r'^(struct CClosure)|(CClosure)$') 498 | 499 | def __init__(self, val): 500 | self.val = val 501 | 502 | def display_hint(self): 503 | return "map" 504 | 505 | def to_string(self): 506 | return "" % int(self.val.address) 507 | 508 | def children(self): 509 | yield "1", "nupvalues" 510 | yield "2", self.val['nupvalues'] 511 | 512 | class LuaStatePrinter: 513 | "Pretty print lua_State." 514 | 515 | pattern = re.compile(r'^struct lua_State$') 516 | 517 | def __init__(self, val): 518 | self.val = val 519 | 520 | def display_hint(self): 521 | return "map" 522 | 523 | def to_string(self): 524 | return "" % int(self.val.address) 525 | 526 | def children(self): 527 | cv = CallInfoValue(self.val, self.val['ci']) 528 | yield "1", "source" 529 | yield "2", "%s:%d" % (cv.source, cv.currentline) 530 | yield "3", "func" 531 | yield "4", cv.funcname 532 | 533 | # 534 | # Register all the *Printer classes above. 535 | # 536 | 537 | def makematcher(klass): 538 | def matcher(val): 539 | try: 540 | if klass.pattern.match(str(val.type)): 541 | return klass(val) 542 | except Exception: 543 | pass 544 | return matcher 545 | 546 | objfile.pretty_printers.extend([makematcher(var) for var in vars().values() if hasattr(var, 'pattern')]) 547 | 548 | class LuaStackCmd(gdb.Command): 549 | """luastack [L] 550 | Prints values on the Lua C stack. Without arguments, uses the current value of "L" 551 | as the lua_State*. You can provide an alternate lua_State as the first argument.""" 552 | 553 | def __init__(self): 554 | gdb.Command.__init__(self, "luastack", gdb.COMMAND_STACK, gdb.COMPLETE_NONE) 555 | 556 | def invoke(self, args, _from_tty): 557 | argv = gdb.string_to_argv(args) 558 | if len(argv) > 0: 559 | L = cast_luaState(gdb.parse_and_eval(argv[0])) 560 | else: 561 | L = gdb.parse_and_eval("L") 562 | 563 | stack = L['top'] - 1 564 | i = 0 565 | while stack > L['stack']: 566 | print("#%d\t0x%x\t%s" % (i, int(stack), stack.dereference())) 567 | stack = stack - 1 568 | i = i + 1 569 | 570 | class LuaTracebackCmd(gdb.Command): 571 | """luabacktrace [L] 572 | Dumps Lua execution stack, as debug.traceback() does. Without 573 | arguments, uses the current value of "L" as the 574 | lua_State*. You can provide an alternate lua_State as the 575 | first argument. 576 | """ 577 | def __init__(self): 578 | gdb.Command.__init__(self, "luatraceback", gdb.COMMAND_STACK, gdb.COMPLETE_NONE) 579 | 580 | def invoke(self, args, _from_tty): 581 | argv = gdb.string_to_argv(args) 582 | if len(argv) > 0: 583 | L = cast_luaState(gdb.parse_and_eval(argv[0])) 584 | else: 585 | L = gdb.parse_and_eval("L") 586 | 587 | ci = L['ci'] 588 | print("stack traceback:") 589 | while ci != L['base_ci'].address: 590 | cv = CallInfoValue(L, ci) 591 | print('\t%s' % (cv.frame_info())) 592 | if cv.is_tailcall(): 593 | print('\t(...tail calls...)') 594 | ci = ci['previous'] 595 | 596 | 597 | class LuaCoroutinesCmd(gdb.Command): 598 | """luacoroutines [L] 599 | List all coroutines. Without arguments, uses the current value of "L" as the 600 | lua_State*. You can provide an alternate lua_State as the 601 | first argument. 602 | """ 603 | def __init__(self): 604 | gdb.Command.__init__(self, "luacoroutines", gdb.COMMAND_STACK, gdb.COMPLETE_NONE) 605 | 606 | def invoke(self, args, _from_tty): 607 | argv = gdb.string_to_argv(args) 608 | if len(argv) > 0: 609 | L = cast_luaState(gdb.parse_and_eval(argv[0])) 610 | else: 611 | L = gdb.parse_and_eval("L") 612 | 613 | # global_State 614 | lG = L['l_G'] 615 | 616 | # mainthread 617 | print("m", lG['mainthread'].dereference()) 618 | 619 | obj = lG['allgc'] 620 | while obj: 621 | if obj['tt'] == 8: 622 | print(" ", cast_u(obj)['th']) 623 | obj = obj['next'] 624 | 625 | class LuaGetLocalCmd(gdb.Command): 626 | """luagetlocal [L [f]] 627 | Print all local variables of the function at level 'f' of the stack 'thread'. 628 | With no arguments, Dump all local variable of the current funtion in the stack of 'L'; 629 | """ 630 | def __init__(self): 631 | gdb.Command.__init__(self, "luagetlocal", gdb.COMMAND_STACK, gdb.COMPLETE_NONE) 632 | 633 | def invoke(self, args, _from_tty): 634 | argv = gdb.string_to_argv(args) 635 | if len(argv) > 0: 636 | L = cast_luaState(gdb.parse_and_eval(argv[0])) 637 | else: 638 | L = gdb.parse_and_eval("L") 639 | 640 | if len(argv) > 1: 641 | arg2 = gdb.parse_and_eval(argv[1]) 642 | else: 643 | arg2 = gdb.parse_and_eval("0") 644 | 645 | level = arg2 646 | ci = L['ci'] 647 | while level > 0: 648 | ci = ci['previous'] 649 | if ci == L['base_ci'].address: 650 | break 651 | level = level - 1 652 | 653 | if level != 0: 654 | print("No function at level %d" % arg2) 655 | return 656 | 657 | cv = CallInfoValue(L, ci) 658 | print("call info: %s" % cv.frame_info()) 659 | 660 | for name, var in cv.upvars(): 661 | print("\tupval %s = %s" % (name, var.dereference())) 662 | 663 | for name, var in cv.varargs(): 664 | print("\t..... %s = %s" % (name, var.dereference())) 665 | 666 | for name, var in cv.locvars(): 667 | print("\tlocal %s = %s" % (name, var.dereference())) 668 | 669 | LuaStackCmd() 670 | LuaTracebackCmd() 671 | LuaCoroutinesCmd() 672 | LuaGetLocalCmd() -------------------------------------------------------------------------------- /lua54_debug.sh: -------------------------------------------------------------------------------- 1 | #for lua5.4 2 | #gdb中加载: source ./lua54_debug.sh 3 | #命令 4 | #1、luabt L 显示调用栈(参数为L) 5 | 6 | # luabt 显示调用栈 7 | define luabt 8 | set $L = $0 9 | print $L 10 | set $ci = $L->ci 11 | while($ci) 12 | set $func = (StackValue*)$ci->func 13 | set $tt = ((TValue)($func->val)->value_)->tt_ & 0x3f 14 | if ($tt==6) 15 | set $p = ((LClosure *)$func->val->value_.gc)->p 16 | set $filename = (char*)($p->source->contents) 17 | set $lineinfo = $p->lineinfo 18 | set $pc1 = (int)(((int)$ci->u->l->savedpc)-(int)$p->code)/4 -1 19 | set $lineno = 0 20 | 21 | set $basepc = 0 22 | set $baseline = 0 23 | set $MAXIWTHABS = 128 24 | set $ABSLINEINFO = -0x80 25 | if ($lineinfo == 0) 26 | set $lineno = -1 27 | else 28 | set $sizeabslineinfo = $p->sizeabslineinfo 29 | if ($sizeabslineinfo == 0 || (long)$pc1 < (long)$p->abslineinfo[0].pc) 30 | set $basepc = (long)(-1) 31 | set $baseline = $p->linedefined 32 | else 33 | set $pcc = ((unsigned int)$pc1) 34 | set $i = $pcc / $MAXIWTHABS - 1 35 | while ((long)($i + 1) < (long)$p->sizeabslineinfo && (long)$pc1 >= (long)$p->abslineinfo[$i + 1].pc) 36 | set $i=$i+1 37 | end 38 | set $basepc = (long)($p->abslineinfo[$i].pc) 39 | set $baseline = $p->abslineinfo[$i].line 40 | end 41 | while((long)$basepc < (long)$pc1) 42 | set $basepc = $basepc + 1 43 | set $baseline = $baseline + $p->lineinfo[$basepc] 44 | end 45 | set $lineno = $baseline 46 | end 47 | printf "LUA FUNCTION : %s:%d\n", $filename, $lineno 48 | end 49 | 50 | if($tt==0x16) 51 | set $f = $func->val->value_->f 52 | printf "LC FUNCTION :" 53 | info line *$f 54 | end 55 | 56 | if($tt==0x26) 57 | set $f = ((CClosure *)$func->val->value_.gc)->f 58 | printf "C FUNCTION :" 59 | info line *$f 60 | end 61 | 62 | set $ci = $ci->previous 63 | end 64 | end --------------------------------------------------------------------------------