1
0

mobdebug.lua 67 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707
  1. --
  2. -- MobDebug -- Lua remote debugger
  3. -- Copyright 2011-15 Paul Kulchenko
  4. -- Based on RemDebug 1.0 Copyright Kepler Project 2005
  5. --
  6. -- use loaded modules or load explicitly on those systems that require that
  7. local require = require
  8. local io = io or require "io"
  9. local table = table or require "table"
  10. local string = string or require "string"
  11. local coroutine = coroutine or require "coroutine"
  12. local debug = require "debug"
  13. -- protect require "os" as it may fail on embedded systems without os module
  14. local os = os or (function(module)
  15. local ok, res = pcall(require, module)
  16. return ok and res or nil
  17. end)("os")
  18. local mobdebug = {
  19. _NAME = "mobdebug",
  20. _VERSION = "0.702",
  21. _COPYRIGHT = "Paul Kulchenko",
  22. _DESCRIPTION = "Mobile Remote Debugger for the Lua programming language",
  23. port = os and os.getenv and tonumber((os.getenv("MOBDEBUG_PORT"))) or 8172,
  24. checkcount = 200,
  25. yieldtimeout = 0.02, -- yield timeout (s)
  26. connecttimeout = 2, -- connect timeout (s)
  27. }
  28. local HOOKMASK = "lcr"
  29. local error = error
  30. local getfenv = getfenv
  31. local setfenv = setfenv
  32. local loadstring = loadstring or load -- "load" replaced "loadstring" in Lua 5.2
  33. local pairs = pairs
  34. local setmetatable = setmetatable
  35. local tonumber = tonumber
  36. local unpack = table.unpack or unpack
  37. local rawget = rawget
  38. local gsub, sub, find = string.gsub, string.sub, string.find
  39. -- if strict.lua is used, then need to avoid referencing some global
  40. -- variables, as they can be undefined;
  41. -- use rawget to avoid complaints from strict.lua at run-time.
  42. -- it's safe to do the initialization here as all these variables
  43. -- should get defined values (if any) before the debugging starts.
  44. -- there is also global 'wx' variable, which is checked as part of
  45. -- the debug loop as 'wx' can be loaded at any time during debugging.
  46. local genv = _G or _ENV
  47. local jit = rawget(genv, "jit")
  48. local MOAICoroutine = rawget(genv, "MOAICoroutine")
  49. -- ngx_lua debugging requires a special handling as its coroutine.*
  50. -- methods use a different mechanism that doesn't allow resume calls
  51. -- from debug hook handlers.
  52. -- Instead, the "original" coroutine.* methods are used.
  53. -- `rawget` needs to be used to protect against `strict` checks, but
  54. -- ngx_lua hides those in a metatable, so need to use that.
  55. local metagindex = getmetatable(genv) and getmetatable(genv).__index
  56. local ngx = type(metagindex) == "table" and metagindex.rawget and metagindex:rawget("ngx") or nil
  57. local corocreate = ngx and coroutine._create or coroutine.create
  58. local cororesume = ngx and coroutine._resume or coroutine.resume
  59. local coroyield = ngx and coroutine._yield or coroutine.yield
  60. local corostatus = ngx and coroutine._status or coroutine.status
  61. local corowrap = coroutine.wrap
  62. if not setfenv then -- Lua 5.2+
  63. -- based on http://lua-users.org/lists/lua-l/2010-06/msg00314.html
  64. -- this assumes f is a function
  65. local function findenv(f)
  66. local level = 1
  67. repeat
  68. local name, value = debug.getupvalue(f, level)
  69. if name == '_ENV' then return level, value end
  70. level = level + 1
  71. until name == nil
  72. return nil end
  73. getfenv = function (f) return(select(2, findenv(f)) or _G) end
  74. setfenv = function (f, t)
  75. local level = findenv(f)
  76. if level then debug.setupvalue(f, level, t) end
  77. return f end
  78. end
  79. -- check for OS and convert file names to lower case on windows
  80. -- (its file system is case insensitive, but case preserving), as setting a
  81. -- breakpoint on x:\Foo.lua will not work if the file was loaded as X:\foo.lua.
  82. -- OSX and Windows behave the same way (case insensitive, but case preserving).
  83. -- OSX can be configured to be case-sensitive, so check for that. This doesn't
  84. -- handle the case of different partitions having different case-sensitivity.
  85. local win = os and os.getenv and (os.getenv('WINDIR') or (os.getenv('OS') or ''):match('[Ww]indows')) and true or false
  86. local mac = not win and (os and os.getenv and os.getenv('DYLD_LIBRARY_PATH') or not io.open("/proc")) and true or false
  87. local iscasepreserving = win or (mac and io.open('/library') ~= nil)
  88. -- turn jit off based on Mike Pall's comment in this discussion:
  89. -- http://www.freelists.org/post/luajit/Debug-hooks-and-JIT,2
  90. -- "You need to turn it off at the start if you plan to receive
  91. -- reliable hook calls at any later point in time."
  92. if jit and jit.off then jit.off() end
  93. local socket = require "socket"
  94. local coro_debugger
  95. local coro_debugee
  96. local coroutines = {}; setmetatable(coroutines, {__mode = "k"}) -- "weak" keys
  97. local events = { BREAK = 1, WATCH = 2, RESTART = 3, STACK = 4 }
  98. local breakpoints = {}
  99. local watches = {}
  100. local lastsource
  101. local lastfile
  102. local watchescnt = 0
  103. local abort -- default value is nil; this is used in start/loop distinction
  104. local seen_hook = false
  105. local checkcount = 0
  106. local step_into = false
  107. local step_over = false
  108. local step_level = 0
  109. local stack_level = 0
  110. local server
  111. local buf
  112. local outputs = {}
  113. local iobase = {print = print}
  114. local basedir = ""
  115. local deferror = "execution aborted at default debugee"
  116. local debugee = function ()
  117. local a = 1
  118. for _ = 1, 10 do a = a + 1 end
  119. error(deferror)
  120. end
  121. local function q(s) return string.gsub(s, '([%(%)%.%%%+%-%*%?%[%^%$%]])','%%%1') end
  122. local serpent = (function() ---- include Serpent module for serialization
  123. local n, v = "serpent", "0.30" -- (C) 2012-17 Paul Kulchenko; MIT License
  124. local c, d = "Paul Kulchenko", "Lua serializer and pretty printer"
  125. local snum = {[tostring(1/0)]='1/0 --[[math.huge]]',[tostring(-1/0)]='-1/0 --[[-math.huge]]',[tostring(0/0)]='0/0'}
  126. local badtype = {thread = true, userdata = true, cdata = true}
  127. local getmetatable = debug and debug.getmetatable or getmetatable
  128. local pairs = function(t) return next, t end -- avoid using __pairs in Lua 5.2+
  129. local keyword, globals, G = {}, {}, (_G or _ENV)
  130. for _,k in ipairs({'and', 'break', 'do', 'else', 'elseif', 'end', 'false',
  131. 'for', 'function', 'goto', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat',
  132. 'return', 'then', 'true', 'until', 'while'}) do keyword[k] = true end
  133. for k,v in pairs(G) do globals[v] = k end -- build func to name mapping
  134. for _,g in ipairs({'coroutine', 'debug', 'io', 'math', 'string', 'table', 'os'}) do
  135. for k,v in pairs(type(G[g]) == 'table' and G[g] or {}) do globals[v] = g..'.'..k end end
  136. local function s(t, opts)
  137. local name, indent, fatal, maxnum = opts.name, opts.indent, opts.fatal, opts.maxnum
  138. local sparse, custom, huge = opts.sparse, opts.custom, not opts.nohuge
  139. local space, maxl = (opts.compact and '' or ' '), (opts.maxlevel or math.huge)
  140. local maxlen, metatostring = tonumber(opts.maxlength), opts.metatostring
  141. local iname, comm = '_'..(name or ''), opts.comment and (tonumber(opts.comment) or math.huge)
  142. local numformat = opts.numformat or "%.17g"
  143. local seen, sref, syms, symn = {}, {'local '..iname..'={}'}, {}, 0
  144. local function gensym(val) return '_'..(tostring(tostring(val)):gsub("[^%w]",""):gsub("(%d%w+)",
  145. -- tostring(val) is needed because __tostring may return a non-string value
  146. function(s) if not syms[s] then symn = symn+1; syms[s] = symn end return tostring(syms[s]) end)) end
  147. local function safestr(s) return type(s) == "number" and tostring(huge and snum[tostring(s)] or numformat:format(s))
  148. or type(s) ~= "string" and tostring(s) -- escape NEWLINE/010 and EOF/026
  149. or ("%q"):format(s):gsub("\010","n"):gsub("\026","\\026") end
  150. local function comment(s,l) return comm and (l or 0) < comm and ' --[['..select(2, pcall(tostring, s))..']]' or '' end
  151. local function globerr(s,l) return globals[s] and globals[s]..comment(s,l) or not fatal
  152. and safestr(select(2, pcall(tostring, s))) or error("Can't serialize "..tostring(s)) end
  153. local function safename(path, name) -- generates foo.bar, foo[3], or foo['b a r']
  154. local n = name == nil and '' or name
  155. local plain = type(n) == "string" and n:match("^[%l%u_][%w_]*$") and not keyword[n]
  156. local safe = plain and n or '['..safestr(n)..']'
  157. return (path or '')..(plain and path and '.' or '')..safe, safe end
  158. local alphanumsort = type(opts.sortkeys) == 'function' and opts.sortkeys or function(k, o, n) -- k=keys, o=originaltable, n=padding
  159. local maxn, to = tonumber(n) or 12, {number = 'a', string = 'b'}
  160. local function padnum(d) return ("%0"..tostring(maxn).."d"):format(tonumber(d)) end
  161. table.sort(k, function(a,b)
  162. -- sort numeric keys first: k[key] is not nil for numerical keys
  163. return (k[a] ~= nil and 0 or to[type(a)] or 'z')..(tostring(a):gsub("%d+",padnum))
  164. < (k[b] ~= nil and 0 or to[type(b)] or 'z')..(tostring(b):gsub("%d+",padnum)) end) end
  165. local function val2str(t, name, indent, insref, path, plainindex, level)
  166. local ttype, level, mt = type(t), (level or 0), getmetatable(t)
  167. local spath, sname = safename(path, name)
  168. local tag = plainindex and
  169. ((type(name) == "number") and '' or name..space..'='..space) or
  170. (name ~= nil and sname..space..'='..space or '')
  171. if seen[t] then -- already seen this element
  172. sref[#sref+1] = spath..space..'='..space..seen[t]
  173. return tag..'nil'..comment('ref', level) end
  174. -- protect from those cases where __tostring may fail
  175. if type(mt) == 'table' then
  176. local to, tr = pcall(function() return mt.__tostring(t) end)
  177. local so, sr = pcall(function() return mt.__serialize(t) end)
  178. if (opts.metatostring ~= false and to or so) then -- knows how to serialize itself
  179. seen[t] = insref or spath
  180. t = so and sr or tr
  181. ttype = type(t)
  182. end -- new value falls through to be serialized
  183. end
  184. if ttype == "table" then
  185. if level >= maxl then return tag..'{}'..comment('maxlvl', level) end
  186. seen[t] = insref or spath
  187. if next(t) == nil then return tag..'{}'..comment(t, level) end -- table empty
  188. if maxlen and maxlen < 0 then return tag..'{}'..comment('maxlen', level) end
  189. local maxn, o, out = math.min(#t, maxnum or #t), {}, {}
  190. for key = 1, maxn do o[key] = key end
  191. if not maxnum or #o < maxnum then
  192. local n = #o -- n = n + 1; o[n] is much faster than o[#o+1] on large tables
  193. for key in pairs(t) do if o[key] ~= key then n = n + 1; o[n] = key end end end
  194. if maxnum and #o > maxnum then o[maxnum+1] = nil end
  195. if opts.sortkeys and #o > maxn then alphanumsort(o, t, opts.sortkeys) end
  196. local sparse = sparse and #o > maxn -- disable sparsness if only numeric keys (shorter output)
  197. for n, key in ipairs(o) do
  198. local value, ktype, plainindex = t[key], type(key), n <= maxn and not sparse
  199. if opts.valignore and opts.valignore[value] -- skip ignored values; do nothing
  200. or opts.keyallow and not opts.keyallow[key]
  201. or opts.keyignore and opts.keyignore[key]
  202. or opts.valtypeignore and opts.valtypeignore[type(value)] -- skipping ignored value types
  203. or sparse and value == nil then -- skipping nils; do nothing
  204. elseif ktype == 'table' or ktype == 'function' or badtype[ktype] then
  205. if not seen[key] and not globals[key] then
  206. sref[#sref+1] = 'placeholder'
  207. local sname = safename(iname, gensym(key)) -- iname is table for local variables
  208. sref[#sref] = val2str(key,sname,indent,sname,iname,true) end
  209. sref[#sref+1] = 'placeholder'
  210. local path = seen[t]..'['..tostring(seen[key] or globals[key] or gensym(key))..']'
  211. sref[#sref] = path..space..'='..space..tostring(seen[value] or val2str(value,nil,indent,path))
  212. else
  213. out[#out+1] = val2str(value,key,indent,insref,seen[t],plainindex,level+1)
  214. if maxlen then
  215. maxlen = maxlen - #out[#out]
  216. if maxlen < 0 then break end
  217. end
  218. end
  219. end
  220. local prefix = string.rep(indent or '', level)
  221. local head = indent and '{\n'..prefix..indent or '{'
  222. local body = table.concat(out, ','..(indent and '\n'..prefix..indent or space))
  223. local tail = indent and "\n"..prefix..'}' or '}'
  224. return (custom and custom(tag,head,body,tail,level) or tag..head..body..tail)..comment(t, level)
  225. elseif badtype[ttype] then
  226. seen[t] = insref or spath
  227. return tag..globerr(t, level)
  228. elseif ttype == 'function' then
  229. seen[t] = insref or spath
  230. if opts.nocode then return tag.."function() --[[..skipped..]] end"..comment(t, level) end
  231. local ok, res = pcall(string.dump, t)
  232. local func = ok and "((loadstring or load)("..safestr(res)..",'@serialized'))"..comment(t, level)
  233. return tag..(func or globerr(t, level))
  234. else return tag..safestr(t) end -- handle all other types
  235. end
  236. local sepr = indent and "\n" or ";"..space
  237. local body = val2str(t, name, indent) -- this call also populates sref
  238. local tail = #sref>1 and table.concat(sref, sepr)..sepr or ''
  239. local warn = opts.comment and #sref>1 and space.."--[[incomplete output with shared/self-references skipped]]" or ''
  240. return not name and body..warn or "do local "..body..sepr..tail.."return "..name..sepr.."end"
  241. end
  242. local function deserialize(data, opts)
  243. local env = (opts and opts.safe == false) and G
  244. or setmetatable({}, {
  245. __index = function(t,k) return t end,
  246. __call = function(t,...) error("cannot call functions") end
  247. })
  248. local f, res = (loadstring or load)('return '..data, nil, nil, env)
  249. if not f then f, res = (loadstring or load)(data, nil, nil, env) end
  250. if not f then return f, res end
  251. if setfenv then setfenv(f, env) end
  252. return pcall(f)
  253. end
  254. local function merge(a, b) if b then for k,v in pairs(b) do a[k] = v end end; return a; end
  255. return { _NAME = n, _COPYRIGHT = c, _DESCRIPTION = d, _VERSION = v, serialize = s,
  256. load = deserialize,
  257. dump = function(a, opts) return s(a, merge({name = '_', compact = true, sparse = true}, opts)) end,
  258. line = function(a, opts) return s(a, merge({sortkeys = true, comment = true}, opts)) end,
  259. block = function(a, opts) return s(a, merge({indent = ' ', sortkeys = true, comment = true}, opts)) end }
  260. end)() ---- end of Serpent module
  261. mobdebug.line = serpent.line
  262. mobdebug.dump = serpent.dump
  263. mobdebug.linemap = nil
  264. mobdebug.loadstring = loadstring
  265. local function removebasedir(path, basedir)
  266. if iscasepreserving then
  267. -- check if the lowercased path matches the basedir
  268. -- if so, return substring of the original path (to not lowercase it)
  269. return path:lower():find('^'..q(basedir:lower()))
  270. and path:sub(#basedir+1) or path
  271. else
  272. return string.gsub(path, '^'..q(basedir), '')
  273. end
  274. end
  275. local function stack(start)
  276. local function vars(f)
  277. local func = debug.getinfo(f, "f").func
  278. local i = 1
  279. local locals = {}
  280. -- get locals
  281. while true do
  282. local name, value = debug.getlocal(f, i)
  283. if not name then break end
  284. if string.sub(name, 1, 1) ~= '(' then
  285. locals[name] = {value, select(2,pcall(tostring,value))}
  286. end
  287. i = i + 1
  288. end
  289. -- get varargs (these use negative indices)
  290. i = 1
  291. while true do
  292. local name, value = debug.getlocal(f, -i)
  293. -- `not name` should be enough, but LuaJIT 2.0.0 incorrectly reports `(*temporary)` names here
  294. if not name or name ~= "(*vararg)" then break end
  295. locals[name:gsub("%)$"," "..i..")")] = {value, select(2,pcall(tostring,value))}
  296. i = i + 1
  297. end
  298. -- get upvalues
  299. i = 1
  300. local ups = {}
  301. while func do -- check for func as it may be nil for tail calls
  302. local name, value = debug.getupvalue(func, i)
  303. if not name then break end
  304. ups[name] = {value, select(2,pcall(tostring,value))}
  305. i = i + 1
  306. end
  307. return locals, ups
  308. end
  309. local stack = {}
  310. local linemap = mobdebug.linemap
  311. for i = (start or 0), 100 do
  312. local source = debug.getinfo(i, "Snl")
  313. if not source then break end
  314. local src = source.source
  315. if src:find("@") == 1 then
  316. src = src:sub(2):gsub("\\", "/")
  317. if src:find("%./") == 1 then src = src:sub(3) end
  318. end
  319. table.insert(stack, { -- remove basedir from source
  320. {source.name, removebasedir(src, basedir),
  321. linemap and linemap(source.linedefined, source.source) or source.linedefined,
  322. linemap and linemap(source.currentline, source.source) or source.currentline,
  323. source.what, source.namewhat, source.short_src},
  324. vars(i+1)})
  325. if source.what == 'main' then break end
  326. end
  327. return stack
  328. end
  329. local function set_breakpoint(file, line)
  330. if file == '-' and lastfile then file = lastfile
  331. elseif iscasepreserving then file = string.lower(file) end
  332. if not breakpoints[line] then breakpoints[line] = {} end
  333. breakpoints[line][file] = true
  334. end
  335. local function remove_breakpoint(file, line)
  336. if file == '-' and lastfile then file = lastfile
  337. elseif file == '*' and line == 0 then breakpoints = {}
  338. elseif iscasepreserving then file = string.lower(file) end
  339. if breakpoints[line] then breakpoints[line][file] = nil end
  340. end
  341. local function has_breakpoint(file, line)
  342. return breakpoints[line]
  343. and breakpoints[line][iscasepreserving and string.lower(file) or file]
  344. end
  345. local function restore_vars(vars)
  346. if type(vars) ~= 'table' then return end
  347. -- locals need to be processed in the reverse order, starting from
  348. -- the inner block out, to make sure that the localized variables
  349. -- are correctly updated with only the closest variable with
  350. -- the same name being changed
  351. -- first loop find how many local variables there is, while
  352. -- the second loop processes them from i to 1
  353. local i = 1
  354. while true do
  355. local name = debug.getlocal(3, i)
  356. if not name then break end
  357. i = i + 1
  358. end
  359. i = i - 1
  360. local written_vars = {}
  361. while i > 0 do
  362. local name = debug.getlocal(3, i)
  363. if not written_vars[name] then
  364. if string.sub(name, 1, 1) ~= '(' then
  365. debug.setlocal(3, i, rawget(vars, name))
  366. end
  367. written_vars[name] = true
  368. end
  369. i = i - 1
  370. end
  371. i = 1
  372. local func = debug.getinfo(3, "f").func
  373. while true do
  374. local name = debug.getupvalue(func, i)
  375. if not name then break end
  376. if not written_vars[name] then
  377. if string.sub(name, 1, 1) ~= '(' then
  378. debug.setupvalue(func, i, rawget(vars, name))
  379. end
  380. written_vars[name] = true
  381. end
  382. i = i + 1
  383. end
  384. end
  385. local function capture_vars(level, thread)
  386. level = (level or 0)+2 -- add two levels for this and debug calls
  387. local func = (thread and debug.getinfo(thread, level, "f") or debug.getinfo(level, "f") or {}).func
  388. if not func then return {} end
  389. local vars = {['...'] = {}}
  390. local i = 1
  391. while true do
  392. local name, value = debug.getupvalue(func, i)
  393. if not name then break end
  394. if string.sub(name, 1, 1) ~= '(' then vars[name] = value end
  395. i = i + 1
  396. end
  397. i = 1
  398. while true do
  399. local name, value
  400. if thread then
  401. name, value = debug.getlocal(thread, level, i)
  402. else
  403. name, value = debug.getlocal(level, i)
  404. end
  405. if not name then break end
  406. if string.sub(name, 1, 1) ~= '(' then vars[name] = value end
  407. i = i + 1
  408. end
  409. -- get varargs (these use negative indices)
  410. i = 1
  411. while true do
  412. local name, value
  413. if thread then
  414. name, value = debug.getlocal(thread, level, -i)
  415. else
  416. name, value = debug.getlocal(level, -i)
  417. end
  418. -- `not name` should be enough, but LuaJIT 2.0.0 incorrectly reports `(*temporary)` names here
  419. if not name or name ~= "(*vararg)" then break end
  420. vars['...'][i] = value
  421. i = i + 1
  422. end
  423. -- returned 'vars' table plays a dual role: (1) it captures local values
  424. -- and upvalues to be restored later (in case they are modified in "eval"),
  425. -- and (2) it provides an environment for evaluated chunks.
  426. -- getfenv(func) is needed to provide proper environment for functions,
  427. -- including access to globals, but this causes vars[name] to fail in
  428. -- restore_vars on local variables or upvalues with `nil` values when
  429. -- 'strict' is in effect. To avoid this `rawget` is used in restore_vars.
  430. setmetatable(vars, { __index = getfenv(func), __newindex = getfenv(func) })
  431. return vars
  432. end
  433. local function stack_depth(start_depth)
  434. for i = start_depth, 0, -1 do
  435. if debug.getinfo(i, "l") then return i+1 end
  436. end
  437. return start_depth
  438. end
  439. local function is_safe(stack_level)
  440. -- the stack grows up: 0 is getinfo, 1 is is_safe, 2 is debug_hook, 3 is user function
  441. if stack_level == 3 then return true end
  442. for i = 3, stack_level do
  443. -- return if it is not safe to abort
  444. local info = debug.getinfo(i, "S")
  445. if not info then return true end
  446. if info.what == "C" then return false end
  447. end
  448. return true
  449. end
  450. local function in_debugger()
  451. local this = debug.getinfo(1, "S").source
  452. -- only need to check few frames as mobdebug frames should be close
  453. for i = 3, 7 do
  454. local info = debug.getinfo(i, "S")
  455. if not info then return false end
  456. if info.source == this then return true end
  457. end
  458. return false
  459. end
  460. local function is_pending(peer)
  461. -- if there is something already in the buffer, skip check
  462. if not buf and checkcount >= mobdebug.checkcount then
  463. peer:settimeout(0) -- non-blocking
  464. buf = peer:receive(1)
  465. peer:settimeout() -- back to blocking
  466. checkcount = 0
  467. end
  468. return buf
  469. end
  470. local function readnext(peer, num)
  471. peer:settimeout(0) -- non-blocking
  472. local res, err, partial = peer:receive(num)
  473. peer:settimeout() -- back to blocking
  474. return res or partial or '', err
  475. end
  476. local function handle_breakpoint(peer)
  477. -- check if the buffer has the beginning of SETB/DELB command;
  478. -- this is to avoid reading the entire line for commands that
  479. -- don't need to be handled here.
  480. if not buf or not (buf:sub(1,1) == 'S' or buf:sub(1,1) == 'D') then return end
  481. -- check second character to avoid reading STEP or other S* and D* commands
  482. if #buf == 1 then buf = buf .. readnext(peer, 1) end
  483. if buf:sub(2,2) ~= 'E' then return end
  484. -- need to read few more characters
  485. buf = buf .. readnext(peer, 5-#buf)
  486. if buf ~= 'SETB ' and buf ~= 'DELB ' then return end
  487. local res, _, partial = peer:receive() -- get the rest of the line; blocking
  488. if not res then
  489. if partial then buf = buf .. partial end
  490. return
  491. end
  492. local _, _, cmd, file, line = (buf..res):find("^([A-Z]+)%s+(.-)%s+(%d+)%s*$")
  493. if cmd == 'SETB' then set_breakpoint(file, tonumber(line))
  494. elseif cmd == 'DELB' then remove_breakpoint(file, tonumber(line))
  495. else
  496. -- this looks like a breakpoint command, but something went wrong;
  497. -- return here to let the "normal" processing to handle,
  498. -- although this is likely to not go well.
  499. return
  500. end
  501. buf = nil
  502. end
  503. local function normalize_path(file)
  504. local n
  505. repeat
  506. file, n = file:gsub("/+%.?/+","/") -- remove all `//` and `/./` references
  507. until n == 0
  508. -- collapse all up-dir references: this will clobber UNC prefix (\\?\)
  509. -- and disk on Windows when there are too many up-dir references: `D:\foo\..\..\bar`;
  510. -- handle the case of multiple up-dir references: `foo/bar/baz/../../../more`;
  511. -- only remove one at a time as otherwise `../../` could be removed;
  512. repeat
  513. file, n = file:gsub("[^/]+/%.%./", "", 1)
  514. until n == 0
  515. -- there may still be a leading up-dir reference left (as `/../` or `../`); remove it
  516. return (file:gsub("^(/?)%.%./", "%1"))
  517. end
  518. local function debug_hook(event, line)
  519. -- (1) LuaJIT needs special treatment. Because debug_hook is set for
  520. -- *all* coroutines, and not just the one being debugged as in regular Lua
  521. -- (http://lua-users.org/lists/lua-l/2011-06/msg00513.html),
  522. -- need to avoid debugging mobdebug's own code as LuaJIT doesn't
  523. -- always correctly generate call/return hook events (there are more
  524. -- calls than returns, which breaks stack depth calculation and
  525. -- 'step' and 'step over' commands stop working; possibly because
  526. -- 'tail return' events are not generated by LuaJIT).
  527. -- the next line checks if the debugger is run under LuaJIT and if
  528. -- one of debugger methods is present in the stack, it simply returns.
  529. if jit then
  530. -- when luajit is compiled with LUAJIT_ENABLE_LUA52COMPAT,
  531. -- coroutine.running() returns non-nil for the main thread.
  532. local coro, main = coroutine.running()
  533. if not coro or main then coro = 'main' end
  534. local disabled = coroutines[coro] == false
  535. or coroutines[coro] == nil and coro ~= (coro_debugee or 'main')
  536. if coro_debugee and disabled or not coro_debugee and (disabled or in_debugger())
  537. then return end
  538. end
  539. -- (2) check if abort has been requested and it's safe to abort
  540. if abort and is_safe(stack_level) then error(abort) end
  541. -- (3) also check if this debug hook has not been visited for any reason.
  542. -- this check is needed to avoid stepping in too early
  543. -- (for example, when coroutine.resume() is executed inside start()).
  544. if not seen_hook and in_debugger() then return end
  545. if event == "call" then
  546. stack_level = stack_level + 1
  547. elseif event == "return" or event == "tail return" then
  548. stack_level = stack_level - 1
  549. elseif event == "line" then
  550. if mobdebug.linemap then
  551. local ok, mappedline = pcall(mobdebug.linemap, line, debug.getinfo(2, "S").source)
  552. if ok then line = mappedline end
  553. if not line then return end
  554. end
  555. -- may need to fall through because of the following:
  556. -- (1) step_into
  557. -- (2) step_over and stack_level <= step_level (need stack_level)
  558. -- (3) breakpoint; check for line first as it's known; then for file
  559. -- (4) socket call (only do every Xth check)
  560. -- (5) at least one watch is registered
  561. if not (
  562. step_into or step_over or breakpoints[line] or watchescnt > 0
  563. or is_pending(server)
  564. ) then checkcount = checkcount + 1; return end
  565. checkcount = mobdebug.checkcount -- force check on the next command
  566. -- this is needed to check if the stack got shorter or longer.
  567. -- unfortunately counting call/return calls is not reliable.
  568. -- the discrepancy may happen when "pcall(load, '')" call is made
  569. -- or when "error()" is called in a function.
  570. -- in either case there are more "call" than "return" events reported.
  571. -- this validation is done for every "line" event, but should be "cheap"
  572. -- as it checks for the stack to get shorter (or longer by one call).
  573. -- start from one level higher just in case we need to grow the stack.
  574. -- this may happen after coroutine.resume call to a function that doesn't
  575. -- have any other instructions to execute. it triggers three returns:
  576. -- "return, tail return, return", which needs to be accounted for.
  577. stack_level = stack_depth(stack_level+1)
  578. local caller = debug.getinfo(2, "S")
  579. -- grab the filename and fix it if needed
  580. local file = lastfile
  581. if (lastsource ~= caller.source) then
  582. file, lastsource = caller.source, caller.source
  583. -- technically, users can supply names that may not use '@',
  584. -- for example when they call loadstring('...', 'filename.lua').
  585. -- Unfortunately, there is no reliable/quick way to figure out
  586. -- what is the filename and what is the source code.
  587. -- If the name doesn't start with `@`, assume it's a file name if it's all on one line.
  588. if find(file, "^@") or not find(file, "[\r\n]") then
  589. file = gsub(gsub(file, "^@", ""), "\\", "/")
  590. -- normalize paths that may include up-dir or same-dir references
  591. -- if the path starts from the up-dir or reference,
  592. -- prepend `basedir` to generate absolute path to keep breakpoints working.
  593. -- ignore qualified relative path (`D:../`) and UNC paths (`\\?\`)
  594. if find(file, "^%.%./") then file = basedir..file end
  595. if find(file, "/%.%.?/") then file = normalize_path(file) end
  596. -- need this conversion to be applied to relative and absolute
  597. -- file names as you may write "require 'Foo'" to
  598. -- load "foo.lua" (on a case insensitive file system) and breakpoints
  599. -- set on foo.lua will not work if not converted to the same case.
  600. if iscasepreserving then file = string.lower(file) end
  601. if find(file, "^%./") then file = sub(file, 3)
  602. else file = gsub(file, "^"..q(basedir), "") end
  603. -- some file systems allow newlines in file names; remove these.
  604. file = gsub(file, "\n", ' ')
  605. else
  606. file = mobdebug.line(file)
  607. end
  608. -- set to true if we got here; this only needs to be done once per
  609. -- session, so do it here to at least avoid setting it for every line.
  610. seen_hook = true
  611. lastfile = file
  612. end
  613. if is_pending(server) then handle_breakpoint(server) end
  614. local vars, status, res
  615. if (watchescnt > 0) then
  616. vars = capture_vars(1)
  617. for index, value in pairs(watches) do
  618. setfenv(value, vars)
  619. local ok, fired = pcall(value)
  620. if ok and fired then
  621. status, res = cororesume(coro_debugger, events.WATCH, vars, file, line, index)
  622. break -- any one watch is enough; don't check multiple times
  623. end
  624. end
  625. end
  626. -- need to get into the "regular" debug handler, but only if there was
  627. -- no watch that was fired. If there was a watch, handle its result.
  628. local getin = (status == nil) and
  629. (step_into
  630. -- when coroutine.running() return `nil` (main thread in Lua 5.1),
  631. -- step_over will equal 'main', so need to check for that explicitly.
  632. or (step_over and step_over == (coroutine.running() or 'main') and stack_level <= step_level)
  633. or has_breakpoint(file, line)
  634. or is_pending(server))
  635. if getin then
  636. vars = vars or capture_vars(1)
  637. step_into = false
  638. step_over = false
  639. status, res = cororesume(coro_debugger, events.BREAK, vars, file, line)
  640. end
  641. -- handle 'stack' command that provides stack() information to the debugger
  642. while status and res == 'stack' do
  643. -- resume with the stack trace and variables
  644. if vars then restore_vars(vars) end -- restore vars so they are reflected in stack values
  645. status, res = cororesume(coro_debugger, events.STACK, stack(3), file, line)
  646. end
  647. -- need to recheck once more as resume after 'stack' command may
  648. -- return something else (for example, 'exit'), which needs to be handled
  649. if status and res and res ~= 'stack' then
  650. if not abort and res == "exit" then mobdebug.onexit(1, true); return end
  651. if not abort and res == "done" then mobdebug.done(); return end
  652. abort = res
  653. -- only abort if safe; if not, there is another (earlier) check inside
  654. -- debug_hook, which will abort execution at the first safe opportunity
  655. if is_safe(stack_level) then error(abort) end
  656. elseif not status and res then
  657. error(res, 2) -- report any other (internal) errors back to the application
  658. end
  659. if vars then restore_vars(vars) end
  660. -- last command requested Step Over/Out; store the current thread
  661. if step_over == true then step_over = coroutine.running() or 'main' end
  662. end
  663. end
  664. local function stringify_results(params, status, ...)
  665. if not status then return status, ... end -- on error report as it
  666. params = params or {}
  667. if params.nocode == nil then params.nocode = true end
  668. if params.comment == nil then params.comment = 1 end
  669. local t = {...}
  670. for i,v in pairs(t) do -- stringify each of the returned values
  671. local ok, res = pcall(mobdebug.line, v, params)
  672. t[i] = ok and res or ("%q"):format(res):gsub("\010","n"):gsub("\026","\\026")
  673. end
  674. -- stringify table with all returned values
  675. -- this is done to allow each returned value to be used (serialized or not)
  676. -- intependently and to preserve "original" comments
  677. return pcall(mobdebug.dump, t, {sparse = false})
  678. end
  679. local function isrunning()
  680. return coro_debugger and (corostatus(coro_debugger) == 'suspended' or corostatus(coro_debugger) == 'running')
  681. end
  682. -- this is a function that removes all hooks and closes the socket to
  683. -- report back to the controller that the debugging is done.
  684. -- the script that called `done` can still continue.
  685. local function done()
  686. if not (isrunning() and server) then return end
  687. if not jit then
  688. for co, debugged in pairs(coroutines) do
  689. if debugged then debug.sethook(co) end
  690. end
  691. end
  692. debug.sethook()
  693. server:close()
  694. coro_debugger = nil -- to make sure isrunning() returns `false`
  695. seen_hook = nil -- to make sure that the next start() call works
  696. abort = nil -- to make sure that callback calls use proper "abort" value
  697. end
  698. local function debugger_loop(sev, svars, sfile, sline)
  699. local command
  700. local app, osname
  701. local eval_env = svars or {}
  702. local function emptyWatch () return false end
  703. local loaded = {}
  704. for k in pairs(package.loaded) do loaded[k] = true end
  705. while true do
  706. local line, err
  707. local wx = rawget(genv, "wx") -- use rawread to make strict.lua happy
  708. if (wx or mobdebug.yield) and server.settimeout then server:settimeout(mobdebug.yieldtimeout) end
  709. while true do
  710. line, err = server:receive()
  711. if not line and err == "timeout" then
  712. -- yield for wx GUI applications if possible to avoid "busyness"
  713. app = app or (wx and wx.wxGetApp and wx.wxGetApp())
  714. if app then
  715. local win = app:GetTopWindow()
  716. local inloop = app:IsMainLoopRunning()
  717. osname = osname or wx.wxPlatformInfo.Get():GetOperatingSystemFamilyName()
  718. if win and not inloop then
  719. -- process messages in a regular way
  720. -- and exit as soon as the event loop is idle
  721. if osname == 'Unix' then wx.wxTimer(app):Start(10, true) end
  722. local exitLoop = function()
  723. win:Disconnect(wx.wxID_ANY, wx.wxID_ANY, wx.wxEVT_IDLE)
  724. win:Disconnect(wx.wxID_ANY, wx.wxID_ANY, wx.wxEVT_TIMER)
  725. app:ExitMainLoop()
  726. end
  727. win:Connect(wx.wxEVT_IDLE, exitLoop)
  728. win:Connect(wx.wxEVT_TIMER, exitLoop)
  729. app:MainLoop()
  730. end
  731. elseif mobdebug.yield then mobdebug.yield()
  732. end
  733. elseif not line and err == "closed" then
  734. error("Debugger connection closed", 0)
  735. else
  736. -- if there is something in the pending buffer, prepend it to the line
  737. if buf then line = buf .. line; buf = nil end
  738. break
  739. end
  740. end
  741. if server.settimeout then server:settimeout() end -- back to blocking
  742. command = string.sub(line, string.find(line, "^[A-Z]+"))
  743. if command == "SETB" then
  744. local _, _, _, file, line = string.find(line, "^([A-Z]+)%s+(.-)%s+(%d+)%s*$")
  745. if file and line then
  746. set_breakpoint(file, tonumber(line))
  747. server:send("200 OK\n")
  748. else
  749. server:send("400 Bad Request\n")
  750. end
  751. elseif command == "DELB" then
  752. local _, _, _, file, line = string.find(line, "^([A-Z]+)%s+(.-)%s+(%d+)%s*$")
  753. if file and line then
  754. remove_breakpoint(file, tonumber(line))
  755. server:send("200 OK\n")
  756. else
  757. server:send("400 Bad Request\n")
  758. end
  759. elseif command == "EXEC" then
  760. -- extract any optional parameters
  761. local params = string.match(line, "--%s*(%b{})%s*$")
  762. local _, _, chunk = string.find(line, "^[A-Z]+%s+(.+)$")
  763. if chunk then
  764. local func, res = mobdebug.loadstring(chunk)
  765. local status
  766. if func then
  767. local pfunc = params and loadstring("return "..params) -- use internal function
  768. params = pfunc and pfunc()
  769. params = (type(params) == "table" and params or {})
  770. local stack = tonumber(params.stack)
  771. -- if the requested stack frame is not the current one, then use a new capture
  772. -- with a specific stack frame: `capture_vars(0, coro_debugee)`
  773. local env = stack and coro_debugee and capture_vars(stack-1, coro_debugee) or eval_env
  774. setfenv(func, env)
  775. status, res = stringify_results(params, pcall(func, unpack(env['...'] or {})))
  776. end
  777. if status then
  778. if mobdebug.onscratch then mobdebug.onscratch(res) end
  779. server:send("200 OK " .. tostring(#res) .. "\n")
  780. server:send(res)
  781. else
  782. -- fix error if not set (for example, when loadstring is not present)
  783. if not res then res = "Unknown error" end
  784. server:send("401 Error in Expression " .. tostring(#res) .. "\n")
  785. server:send(res)
  786. end
  787. else
  788. server:send("400 Bad Request\n")
  789. end
  790. elseif command == "LOAD" then
  791. local _, _, size, name = string.find(line, "^[A-Z]+%s+(%d+)%s+(%S.-)%s*$")
  792. size = tonumber(size)
  793. if abort == nil then -- no LOAD/RELOAD allowed inside start()
  794. if size > 0 then server:receive(size) end
  795. if sfile and sline then
  796. server:send("201 Started " .. sfile .. " " .. tostring(sline) .. "\n")
  797. else
  798. server:send("200 OK 0\n")
  799. end
  800. else
  801. -- reset environment to allow required modules to load again
  802. -- remove those packages that weren't loaded when debugger started
  803. for k in pairs(package.loaded) do
  804. if not loaded[k] then package.loaded[k] = nil end
  805. end
  806. if size == 0 and name == '-' then -- RELOAD the current script being debugged
  807. server:send("200 OK 0\n")
  808. coroyield("load")
  809. else
  810. -- receiving 0 bytes blocks (at least in luasocket 2.0.2), so skip reading
  811. local chunk = size == 0 and "" or server:receive(size)
  812. if chunk then -- LOAD a new script for debugging
  813. local func, res = mobdebug.loadstring(chunk, "@"..name)
  814. if func then
  815. server:send("200 OK 0\n")
  816. debugee = func
  817. coroyield("load")
  818. else
  819. server:send("401 Error in Expression " .. tostring(#res) .. "\n")
  820. server:send(res)
  821. end
  822. else
  823. server:send("400 Bad Request\n")
  824. end
  825. end
  826. end
  827. elseif command == "SETW" then
  828. local _, _, exp = string.find(line, "^[A-Z]+%s+(.+)%s*$")
  829. if exp then
  830. local func, res = mobdebug.loadstring("return(" .. exp .. ")")
  831. if func then
  832. watchescnt = watchescnt + 1
  833. local newidx = #watches + 1
  834. watches[newidx] = func
  835. server:send("200 OK " .. tostring(newidx) .. "\n")
  836. else
  837. server:send("401 Error in Expression " .. tostring(#res) .. "\n")
  838. server:send(res)
  839. end
  840. else
  841. server:send("400 Bad Request\n")
  842. end
  843. elseif command == "DELW" then
  844. local _, _, index = string.find(line, "^[A-Z]+%s+(%d+)%s*$")
  845. index = tonumber(index)
  846. if index > 0 and index <= #watches then
  847. watchescnt = watchescnt - (watches[index] ~= emptyWatch and 1 or 0)
  848. watches[index] = emptyWatch
  849. server:send("200 OK\n")
  850. else
  851. server:send("400 Bad Request\n")
  852. end
  853. elseif command == "RUN" then
  854. server:send("200 OK\n")
  855. local ev, vars, file, line, idx_watch = coroyield()
  856. eval_env = vars
  857. if ev == events.BREAK then
  858. server:send("202 Paused " .. file .. " " .. tostring(line) .. "\n")
  859. elseif ev == events.WATCH then
  860. server:send("203 Paused " .. file .. " " .. tostring(line) .. " " .. tostring(idx_watch) .. "\n")
  861. elseif ev == events.RESTART then
  862. -- nothing to do
  863. else
  864. server:send("401 Error in Execution " .. tostring(#file) .. "\n")
  865. server:send(file)
  866. end
  867. elseif command == "STEP" then
  868. server:send("200 OK\n")
  869. step_into = true
  870. local ev, vars, file, line, idx_watch = coroyield()
  871. eval_env = vars
  872. if ev == events.BREAK then
  873. server:send("202 Paused " .. file .. " " .. tostring(line) .. "\n")
  874. elseif ev == events.WATCH then
  875. server:send("203 Paused " .. file .. " " .. tostring(line) .. " " .. tostring(idx_watch) .. "\n")
  876. elseif ev == events.RESTART then
  877. -- nothing to do
  878. else
  879. server:send("401 Error in Execution " .. tostring(#file) .. "\n")
  880. server:send(file)
  881. end
  882. elseif command == "OVER" or command == "OUT" then
  883. server:send("200 OK\n")
  884. step_over = true
  885. -- OVER and OUT are very similar except for
  886. -- the stack level value at which to stop
  887. if command == "OUT" then step_level = stack_level - 1
  888. else step_level = stack_level end
  889. local ev, vars, file, line, idx_watch = coroyield()
  890. eval_env = vars
  891. if ev == events.BREAK then
  892. server:send("202 Paused " .. file .. " " .. tostring(line) .. "\n")
  893. elseif ev == events.WATCH then
  894. server:send("203 Paused " .. file .. " " .. tostring(line) .. " " .. tostring(idx_watch) .. "\n")
  895. elseif ev == events.RESTART then
  896. -- nothing to do
  897. else
  898. server:send("401 Error in Execution " .. tostring(#file) .. "\n")
  899. server:send(file)
  900. end
  901. elseif command == "BASEDIR" then
  902. local _, _, dir = string.find(line, "^[A-Z]+%s+(.+)%s*$")
  903. if dir then
  904. basedir = iscasepreserving and string.lower(dir) or dir
  905. -- reset cached source as it may change with basedir
  906. lastsource = nil
  907. server:send("200 OK\n")
  908. else
  909. server:send("400 Bad Request\n")
  910. end
  911. elseif command == "SUSPEND" then
  912. -- do nothing; it already fulfilled its role
  913. elseif command == "DONE" then
  914. coroyield("done")
  915. return -- done with all the debugging
  916. elseif command == "STACK" then
  917. -- first check if we can execute the stack command
  918. -- as it requires yielding back to debug_hook it cannot be executed
  919. -- if we have not seen the hook yet as happens after start().
  920. -- in this case we simply return an empty result
  921. local vars, ev = {}
  922. if seen_hook then
  923. ev, vars = coroyield("stack")
  924. end
  925. if ev and ev ~= events.STACK then
  926. server:send("401 Error in Execution " .. tostring(#vars) .. "\n")
  927. server:send(vars)
  928. else
  929. local params = string.match(line, "--%s*(%b{})%s*$")
  930. local pfunc = params and loadstring("return "..params) -- use internal function
  931. params = pfunc and pfunc()
  932. params = (type(params) == "table" and params or {})
  933. if params.nocode == nil then params.nocode = true end
  934. if params.sparse == nil then params.sparse = false end
  935. -- take into account additional levels for the stack frames and data management
  936. if tonumber(params.maxlevel) then params.maxlevel = tonumber(params.maxlevel)+4 end
  937. local ok, res = pcall(mobdebug.dump, vars, params)
  938. if ok then
  939. server:send("200 OK " .. tostring(res) .. "\n")
  940. else
  941. server:send("401 Error in Execution " .. tostring(#res) .. "\n")
  942. server:send(res)
  943. end
  944. end
  945. elseif command == "OUTPUT" then
  946. local _, _, stream, mode = string.find(line, "^[A-Z]+%s+(%w+)%s+([dcr])%s*$")
  947. if stream and mode and stream == "stdout" then
  948. -- assign "print" in the global environment
  949. local default = mode == 'd'
  950. genv.print = default and iobase.print or corowrap(function()
  951. -- wrapping into coroutine.wrap protects this function from
  952. -- being stepped through in the debugger.
  953. -- don't use vararg (...) as it adds a reference for its values,
  954. -- which may affect how they are garbage collected
  955. while true do
  956. local tbl = {coroutine.yield()}
  957. if mode == 'c' then iobase.print(unpack(tbl)) end
  958. for n = 1, #tbl do
  959. tbl[n] = select(2, pcall(mobdebug.line, tbl[n], {nocode = true, comment = false})) end
  960. local file = table.concat(tbl, "\t").."\n"
  961. server:send("204 Output " .. stream .. " " .. tostring(#file) .. "\n" .. file)
  962. end
  963. end)
  964. if not default then genv.print() end -- "fake" print to start printing loop
  965. server:send("200 OK\n")
  966. else
  967. server:send("400 Bad Request\n")
  968. end
  969. elseif command == "EXIT" then
  970. server:send("200 OK\n")
  971. coroyield("exit")
  972. else
  973. server:send("400 Bad Request\n")
  974. end
  975. end
  976. end
  977. local function output(stream, data)
  978. if server then return server:send("204 Output "..stream.." "..tostring(#data).."\n"..data) end
  979. end
  980. local function connect(controller_host, controller_port)
  981. local sock, err = socket.tcp()
  982. if not sock then return nil, err end
  983. if sock.settimeout then sock:settimeout(mobdebug.connecttimeout) end
  984. local res, err = sock:connect(controller_host, tostring(controller_port))
  985. if sock.settimeout then sock:settimeout() end
  986. if not res then return nil, err end
  987. return sock
  988. end
  989. local lasthost, lastport
  990. -- Starts a debug session by connecting to a controller
  991. local function start(controller_host, controller_port)
  992. -- only one debugging session can be run (as there is only one debug hook)
  993. if isrunning() then return end
  994. lasthost = controller_host or lasthost
  995. lastport = controller_port or lastport
  996. controller_host = lasthost or "localhost"
  997. controller_port = lastport or mobdebug.port
  998. local err
  999. server, err = mobdebug.connect(controller_host, controller_port)
  1000. if server then
  1001. -- correct stack depth which already has some calls on it
  1002. -- so it doesn't go into negative when those calls return
  1003. -- as this breaks subsequence checks in stack_depth().
  1004. -- start from 16th frame, which is sufficiently large for this check.
  1005. stack_level = stack_depth(16)
  1006. -- provide our own traceback function to report errors remotely
  1007. -- but only under Lua 5.1/LuaJIT as it's not called under Lua 5.2+
  1008. -- (http://lua-users.org/lists/lua-l/2016-05/msg00297.html)
  1009. local function f() return function()end end
  1010. if f() ~= f() then -- Lua 5.1 or LuaJIT
  1011. local dtraceback = debug.traceback
  1012. debug.traceback = function (...)
  1013. if select('#', ...) >= 1 then
  1014. local thr, err, lvl = ...
  1015. if type(thr) ~= 'thread' then err, lvl = thr, err end
  1016. local trace = dtraceback(err, (lvl or 1)+1)
  1017. if genv.print == iobase.print then -- no remote redirect
  1018. return trace
  1019. else
  1020. genv.print(trace) -- report the error remotely
  1021. return -- don't report locally to avoid double reporting
  1022. end
  1023. end
  1024. -- direct call to debug.traceback: return the original.
  1025. -- debug.traceback(nil, level) doesn't work in Lua 5.1
  1026. -- (http://lua-users.org/lists/lua-l/2011-06/msg00574.html), so
  1027. -- simply remove first frame from the stack trace
  1028. local tb = dtraceback("", 2) -- skip debugger frames
  1029. -- if the string is returned, then remove the first new line as it's not needed
  1030. return type(tb) == "string" and tb:gsub("^\n","") or tb
  1031. end
  1032. end
  1033. coro_debugger = corocreate(debugger_loop)
  1034. debug.sethook(debug_hook, HOOKMASK)
  1035. seen_hook = nil -- reset in case the last start() call was refused
  1036. step_into = true -- start with step command
  1037. return true
  1038. else
  1039. print(("Could not connect to %s:%s: %s")
  1040. :format(controller_host, controller_port, err or "unknown error"))
  1041. end
  1042. end
  1043. local function controller(controller_host, controller_port, scratchpad)
  1044. -- only one debugging session can be run (as there is only one debug hook)
  1045. if isrunning() then return end
  1046. lasthost = controller_host or lasthost
  1047. lastport = controller_port or lastport
  1048. controller_host = lasthost or "localhost"
  1049. controller_port = lastport or mobdebug.port
  1050. local exitonerror = not scratchpad
  1051. local err
  1052. server, err = mobdebug.connect(controller_host, controller_port)
  1053. if server then
  1054. local function report(trace, err)
  1055. local msg = err .. "\n" .. trace
  1056. server:send("401 Error in Execution " .. tostring(#msg) .. "\n")
  1057. server:send(msg)
  1058. return err
  1059. end
  1060. seen_hook = true -- allow to accept all commands
  1061. coro_debugger = corocreate(debugger_loop)
  1062. while true do
  1063. step_into = true -- start with step command
  1064. abort = false -- reset abort flag from the previous loop
  1065. if scratchpad then checkcount = mobdebug.checkcount end -- force suspend right away
  1066. coro_debugee = corocreate(debugee)
  1067. debug.sethook(coro_debugee, debug_hook, HOOKMASK)
  1068. local status, err = cororesume(coro_debugee, unpack(arg or {}))
  1069. -- was there an error or is the script done?
  1070. -- 'abort' state is allowed here; ignore it
  1071. if abort then
  1072. if tostring(abort) == 'exit' then break end
  1073. else
  1074. if status then -- no errors
  1075. if corostatus(coro_debugee) == "suspended" then
  1076. -- the script called `coroutine.yield` in the "main" thread
  1077. error("attempt to yield from the main thread", 3)
  1078. end
  1079. break -- normal execution is done
  1080. elseif err and not string.find(tostring(err), deferror) then
  1081. -- report the error back
  1082. -- err is not necessarily a string, so convert to string to report
  1083. report(debug.traceback(coro_debugee), tostring(err))
  1084. if exitonerror then break end
  1085. -- check if the debugging is done (coro_debugger is nil)
  1086. if not coro_debugger then break end
  1087. -- resume once more to clear the response the debugger wants to send
  1088. -- need to use capture_vars(0) to capture only two (default) level,
  1089. -- as even though there is controller() call, because of the tail call,
  1090. -- the caller may not exist for it;
  1091. -- This is not entirely safe as the user may see the local
  1092. -- variable from console, but they will be reset anyway.
  1093. -- This functionality is used when scratchpad is paused to
  1094. -- gain access to remote console to modify global variables.
  1095. local status, err = cororesume(coro_debugger, events.RESTART, capture_vars(0))
  1096. if not status or status and err == "exit" then break end
  1097. end
  1098. end
  1099. end
  1100. else
  1101. print(("Could not connect to %s:%s: %s")
  1102. :format(controller_host, controller_port, err or "unknown error"))
  1103. return false
  1104. end
  1105. return true
  1106. end
  1107. local function scratchpad(controller_host, controller_port)
  1108. return controller(controller_host, controller_port, true)
  1109. end
  1110. local function loop(controller_host, controller_port)
  1111. return controller(controller_host, controller_port, false)
  1112. end
  1113. local function on()
  1114. if not (isrunning() and server) then return end
  1115. -- main is set to true under Lua5.2 for the "main" chunk.
  1116. -- Lua5.1 returns co as `nil` in that case.
  1117. local co, main = coroutine.running()
  1118. if main then co = nil end
  1119. if co then
  1120. coroutines[co] = true
  1121. debug.sethook(co, debug_hook, HOOKMASK)
  1122. else
  1123. if jit then coroutines.main = true end
  1124. debug.sethook(debug_hook, HOOKMASK)
  1125. end
  1126. end
  1127. local function off()
  1128. if not (isrunning() and server) then return end
  1129. -- main is set to true under Lua5.2 for the "main" chunk.
  1130. -- Lua5.1 returns co as `nil` in that case.
  1131. local co, main = coroutine.running()
  1132. if main then co = nil end
  1133. -- don't remove coroutine hook under LuaJIT as there is only one (global) hook
  1134. if co then
  1135. coroutines[co] = false
  1136. if not jit then debug.sethook(co) end
  1137. else
  1138. if jit then coroutines.main = false end
  1139. if not jit then debug.sethook() end
  1140. end
  1141. -- check if there is any thread that is still being debugged under LuaJIT;
  1142. -- if not, turn the debugging off
  1143. if jit then
  1144. local remove = true
  1145. for _, debugged in pairs(coroutines) do
  1146. if debugged then remove = false; break end
  1147. end
  1148. if remove then debug.sethook() end
  1149. end
  1150. end
  1151. -- Handles server debugging commands
  1152. local function handle(params, client, options)
  1153. -- when `options.verbose` is not provided, use normal `print`; verbose output can be
  1154. -- disabled (`options.verbose == false`) or redirected (`options.verbose == function()...end`)
  1155. local verbose = not options or options.verbose ~= nil and options.verbose
  1156. local print = verbose and (type(verbose) == "function" and verbose or print) or function() end
  1157. local file, line, watch_idx
  1158. local _, _, command = string.find(params, "^([a-z]+)")
  1159. if command == "run" or command == "step" or command == "out"
  1160. or command == "over" or command == "exit" then
  1161. client:send(string.upper(command) .. "\n")
  1162. client:receive() -- this should consume the first '200 OK' response
  1163. while true do
  1164. local done = true
  1165. local breakpoint = client:receive()
  1166. if not breakpoint then
  1167. print("Program finished")
  1168. return nil, nil, false
  1169. end
  1170. local _, _, status = string.find(breakpoint, "^(%d+)")
  1171. if status == "200" then
  1172. -- don't need to do anything
  1173. elseif status == "202" then
  1174. _, _, file, line = string.find(breakpoint, "^202 Paused%s+(.-)%s+(%d+)%s*$")
  1175. if file and line then
  1176. print("Paused at file " .. file .. " line " .. line)
  1177. end
  1178. elseif status == "203" then
  1179. _, _, file, line, watch_idx = string.find(breakpoint, "^203 Paused%s+(.-)%s+(%d+)%s+(%d+)%s*$")
  1180. if file and line and watch_idx then
  1181. print("Paused at file " .. file .. " line " .. line .. " (watch expression " .. watch_idx .. ": [" .. watches[watch_idx] .. "])")
  1182. end
  1183. elseif status == "204" then
  1184. local _, _, stream, size = string.find(breakpoint, "^204 Output (%w+) (%d+)$")
  1185. if stream and size then
  1186. local size = tonumber(size)
  1187. local msg = size > 0 and client:receive(size) or ""
  1188. print(msg)
  1189. if outputs[stream] then outputs[stream](msg) end
  1190. -- this was just the output, so go back reading the response
  1191. done = false
  1192. end
  1193. elseif status == "401" then
  1194. local _, _, size = string.find(breakpoint, "^401 Error in Execution (%d+)$")
  1195. if size then
  1196. local msg = client:receive(tonumber(size))
  1197. print("Error in remote application: " .. msg)
  1198. return nil, nil, msg
  1199. end
  1200. else
  1201. print("Unknown error")
  1202. return nil, nil, "Debugger error: unexpected response '" .. breakpoint .. "'"
  1203. end
  1204. if done then break end
  1205. end
  1206. elseif command == "done" then
  1207. client:send(string.upper(command) .. "\n")
  1208. -- no response is expected
  1209. elseif command == "setb" or command == "asetb" then
  1210. _, _, _, file, line = string.find(params, "^([a-z]+)%s+(.-)%s+(%d+)%s*$")
  1211. if file and line then
  1212. -- if this is a file name, and not a file source
  1213. if not file:find('^".*"$') then
  1214. file = string.gsub(file, "\\", "/") -- convert slash
  1215. file = removebasedir(file, basedir)
  1216. end
  1217. client:send("SETB " .. file .. " " .. line .. "\n")
  1218. if command == "asetb" or client:receive() == "200 OK" then
  1219. set_breakpoint(file, line)
  1220. else
  1221. print("Error: breakpoint not inserted")
  1222. end
  1223. else
  1224. print("Invalid command")
  1225. end
  1226. elseif command == "setw" then
  1227. local _, _, exp = string.find(params, "^[a-z]+%s+(.+)$")
  1228. if exp then
  1229. client:send("SETW " .. exp .. "\n")
  1230. local answer = client:receive()
  1231. local _, _, watch_idx = string.find(answer, "^200 OK (%d+)%s*$")
  1232. if watch_idx then
  1233. watches[watch_idx] = exp
  1234. print("Inserted watch exp no. " .. watch_idx)
  1235. else
  1236. local _, _, size = string.find(answer, "^401 Error in Expression (%d+)$")
  1237. if size then
  1238. local err = client:receive(tonumber(size)):gsub(".-:%d+:%s*","")
  1239. print("Error: watch expression not set: " .. err)
  1240. else
  1241. print("Error: watch expression not set")
  1242. end
  1243. end
  1244. else
  1245. print("Invalid command")
  1246. end
  1247. elseif command == "delb" or command == "adelb" then
  1248. _, _, _, file, line = string.find(params, "^([a-z]+)%s+(.-)%s+(%d+)%s*$")
  1249. if file and line then
  1250. -- if this is a file name, and not a file source
  1251. if not file:find('^".*"$') then
  1252. file = string.gsub(file, "\\", "/") -- convert slash
  1253. file = removebasedir(file, basedir)
  1254. end
  1255. client:send("DELB " .. file .. " " .. line .. "\n")
  1256. if command == "adelb" or client:receive() == "200 OK" then
  1257. remove_breakpoint(file, line)
  1258. else
  1259. print("Error: breakpoint not removed")
  1260. end
  1261. else
  1262. print("Invalid command")
  1263. end
  1264. elseif command == "delallb" then
  1265. local file, line = "*", 0
  1266. client:send("DELB " .. file .. " " .. tostring(line) .. "\n")
  1267. if client:receive() == "200 OK" then
  1268. remove_breakpoint(file, line)
  1269. else
  1270. print("Error: all breakpoints not removed")
  1271. end
  1272. elseif command == "delw" then
  1273. local _, _, index = string.find(params, "^[a-z]+%s+(%d+)%s*$")
  1274. if index then
  1275. client:send("DELW " .. index .. "\n")
  1276. if client:receive() == "200 OK" then
  1277. watches[index] = nil
  1278. else
  1279. print("Error: watch expression not removed")
  1280. end
  1281. else
  1282. print("Invalid command")
  1283. end
  1284. elseif command == "delallw" then
  1285. for index, exp in pairs(watches) do
  1286. client:send("DELW " .. index .. "\n")
  1287. if client:receive() == "200 OK" then
  1288. watches[index] = nil
  1289. else
  1290. print("Error: watch expression at index " .. index .. " [" .. exp .. "] not removed")
  1291. end
  1292. end
  1293. elseif command == "eval" or command == "exec"
  1294. or command == "load" or command == "loadstring"
  1295. or command == "reload" then
  1296. local _, _, exp = string.find(params, "^[a-z]+%s+(.+)$")
  1297. if exp or (command == "reload") then
  1298. if command == "eval" or command == "exec" then
  1299. exp = (exp:gsub("%-%-%[(=*)%[.-%]%1%]", "") -- remove comments
  1300. :gsub("%-%-.-\n", " ") -- remove line comments
  1301. :gsub("\n", " ")) -- convert new lines
  1302. if command == "eval" then exp = "return " .. exp end
  1303. client:send("EXEC " .. exp .. "\n")
  1304. elseif command == "reload" then
  1305. client:send("LOAD 0 -\n")
  1306. elseif command == "loadstring" then
  1307. local _, _, _, file, lines = string.find(exp, "^([\"'])(.-)%1%s(.+)")
  1308. if not file then
  1309. _, _, file, lines = string.find(exp, "^(%S+)%s(.+)")
  1310. end
  1311. client:send("LOAD " .. tostring(#lines) .. " " .. file .. "\n")
  1312. client:send(lines)
  1313. else
  1314. local file = io.open(exp, "r")
  1315. if not file and pcall(require, "winapi") then
  1316. -- if file is not open and winapi is there, try with a short path;
  1317. -- this may be needed for unicode paths on windows
  1318. winapi.set_encoding(winapi.CP_UTF8)
  1319. local shortp = winapi.short_path(exp)
  1320. file = shortp and io.open(shortp, "r")
  1321. end
  1322. if not file then return nil, nil, "Cannot open file " .. exp end
  1323. -- read the file and remove the shebang line as it causes a compilation error
  1324. local lines = file:read("*all"):gsub("^#!.-\n", "\n")
  1325. file:close()
  1326. local file = string.gsub(exp, "\\", "/") -- convert slash
  1327. file = removebasedir(file, basedir)
  1328. client:send("LOAD " .. tostring(#lines) .. " " .. file .. "\n")
  1329. if #lines > 0 then client:send(lines) end
  1330. end
  1331. while true do
  1332. local params, err = client:receive()
  1333. if not params then
  1334. return nil, nil, "Debugger connection " .. (err or "error")
  1335. end
  1336. local done = true
  1337. local _, _, status, len = string.find(params, "^(%d+).-%s+(%d+)%s*$")
  1338. if status == "200" then
  1339. len = tonumber(len)
  1340. if len > 0 then
  1341. local status, res
  1342. local str = client:receive(len)
  1343. -- handle serialized table with results
  1344. local func, err = loadstring(str)
  1345. if func then
  1346. status, res = pcall(func)
  1347. if not status then err = res
  1348. elseif type(res) ~= "table" then
  1349. err = "received "..type(res).." instead of expected 'table'"
  1350. end
  1351. end
  1352. if err then
  1353. print("Error in processing results: " .. err)
  1354. return nil, nil, "Error in processing results: " .. err
  1355. end
  1356. print(unpack(res))
  1357. return res[1], res
  1358. end
  1359. elseif status == "201" then
  1360. _, _, file, line = string.find(params, "^201 Started%s+(.-)%s+(%d+)%s*$")
  1361. elseif status == "202" or params == "200 OK" then
  1362. -- do nothing; this only happens when RE/LOAD command gets the response
  1363. -- that was for the original command that was aborted
  1364. elseif status == "204" then
  1365. local _, _, stream, size = string.find(params, "^204 Output (%w+) (%d+)$")
  1366. if stream and size then
  1367. local size = tonumber(size)
  1368. local msg = size > 0 and client:receive(size) or ""
  1369. print(msg)
  1370. if outputs[stream] then outputs[stream](msg) end
  1371. -- this was just the output, so go back reading the response
  1372. done = false
  1373. end
  1374. elseif status == "401" then
  1375. len = tonumber(len)
  1376. local res = client:receive(len)
  1377. print("Error in expression: " .. res)
  1378. return nil, nil, res
  1379. else
  1380. print("Unknown error")
  1381. return nil, nil, "Debugger error: unexpected response after EXEC/LOAD '" .. params .. "'"
  1382. end
  1383. if done then break end
  1384. end
  1385. else
  1386. print("Invalid command")
  1387. end
  1388. elseif command == "listb" then
  1389. for l, v in pairs(breakpoints) do
  1390. for f in pairs(v) do
  1391. print(f .. ": " .. l)
  1392. end
  1393. end
  1394. elseif command == "listw" then
  1395. for i, v in pairs(watches) do
  1396. print("Watch exp. " .. i .. ": " .. v)
  1397. end
  1398. elseif command == "suspend" then
  1399. client:send("SUSPEND\n")
  1400. elseif command == "stack" then
  1401. local opts = string.match(params, "^[a-z]+%s+(.+)$")
  1402. client:send("STACK" .. (opts and " "..opts or "") .."\n")
  1403. local resp = client:receive()
  1404. local _, _, status, res = string.find(resp, "^(%d+)%s+%w+%s+(.+)%s*$")
  1405. if status == "200" then
  1406. local func, err = loadstring(res)
  1407. if func == nil then
  1408. print("Error in stack information: " .. err)
  1409. return nil, nil, err
  1410. end
  1411. local ok, stack = pcall(func)
  1412. if not ok then
  1413. print("Error in stack information: " .. stack)
  1414. return nil, nil, stack
  1415. end
  1416. for _,frame in ipairs(stack) do
  1417. print(mobdebug.line(frame[1], {comment = false}))
  1418. end
  1419. return stack
  1420. elseif status == "401" then
  1421. local _, _, len = string.find(resp, "%s+(%d+)%s*$")
  1422. len = tonumber(len)
  1423. local res = len > 0 and client:receive(len) or "Invalid stack information."
  1424. print("Error in expression: " .. res)
  1425. return nil, nil, res
  1426. else
  1427. print("Unknown error")
  1428. return nil, nil, "Debugger error: unexpected response after STACK"
  1429. end
  1430. elseif command == "output" then
  1431. local _, _, stream, mode = string.find(params, "^[a-z]+%s+(%w+)%s+([dcr])%s*$")
  1432. if stream and mode then
  1433. client:send("OUTPUT "..stream.." "..mode.."\n")
  1434. local resp, err = client:receive()
  1435. if not resp then
  1436. print("Unknown error: "..err)
  1437. return nil, nil, "Debugger connection error: "..err
  1438. end
  1439. local _, _, status = string.find(resp, "^(%d+)%s+%w+%s*$")
  1440. if status == "200" then
  1441. print("Stream "..stream.." redirected")
  1442. outputs[stream] = type(options) == 'table' and options.handler or nil
  1443. -- the client knows when she is doing, so install the handler
  1444. elseif type(options) == 'table' and options.handler then
  1445. outputs[stream] = options.handler
  1446. else
  1447. print("Unknown error")
  1448. return nil, nil, "Debugger error: can't redirect "..stream
  1449. end
  1450. else
  1451. print("Invalid command")
  1452. end
  1453. elseif command == "basedir" then
  1454. local _, _, dir = string.find(params, "^[a-z]+%s+(.+)$")
  1455. if dir then
  1456. dir = string.gsub(dir, "\\", "/") -- convert slash
  1457. if not string.find(dir, "/$") then dir = dir .. "/" end
  1458. local remdir = dir:match("\t(.+)")
  1459. if remdir then dir = dir:gsub("/?\t.+", "/") end
  1460. basedir = dir
  1461. client:send("BASEDIR "..(remdir or dir).."\n")
  1462. local resp, err = client:receive()
  1463. if not resp then
  1464. print("Unknown error: "..err)
  1465. return nil, nil, "Debugger connection error: "..err
  1466. end
  1467. local _, _, status = string.find(resp, "^(%d+)%s+%w+%s*$")
  1468. if status == "200" then
  1469. print("New base directory is " .. basedir)
  1470. else
  1471. print("Unknown error")
  1472. return nil, nil, "Debugger error: unexpected response after BASEDIR"
  1473. end
  1474. else
  1475. print(basedir)
  1476. end
  1477. elseif command == "help" then
  1478. print("setb <file> <line> -- sets a breakpoint")
  1479. print("delb <file> <line> -- removes a breakpoint")
  1480. print("delallb -- removes all breakpoints")
  1481. print("setw <exp> -- adds a new watch expression")
  1482. print("delw <index> -- removes the watch expression at index")
  1483. print("delallw -- removes all watch expressions")
  1484. print("run -- runs until next breakpoint")
  1485. print("step -- runs until next line, stepping into function calls")
  1486. print("over -- runs until next line, stepping over function calls")
  1487. print("out -- runs until line after returning from current function")
  1488. print("listb -- lists breakpoints")
  1489. print("listw -- lists watch expressions")
  1490. print("eval <exp> -- evaluates expression on the current context and returns its value")
  1491. print("exec <stmt> -- executes statement on the current context")
  1492. print("load <file> -- loads a local file for debugging")
  1493. print("reload -- restarts the current debugging session")
  1494. print("stack -- reports stack trace")
  1495. print("output stdout <d|c|r> -- capture and redirect io stream (default|copy|redirect)")
  1496. print("basedir [<path>] -- sets the base path of the remote application, or shows the current one")
  1497. print("done -- stops the debugger and continues application execution")
  1498. print("exit -- exits debugger and the application")
  1499. else
  1500. local _, _, spaces = string.find(params, "^(%s*)$")
  1501. if spaces then
  1502. return nil, nil, "Empty command"
  1503. else
  1504. print("Invalid command")
  1505. return nil, nil, "Invalid command"
  1506. end
  1507. end
  1508. return file, line
  1509. end
  1510. -- Starts debugging server
  1511. local function listen(host, port)
  1512. host = host or "*"
  1513. port = port or mobdebug.port
  1514. local socket = require "socket"
  1515. print("Lua Remote Debugger")
  1516. print("Run the program you wish to debug")
  1517. local server = socket.bind(host, port)
  1518. local client = server:accept()
  1519. client:send("STEP\n")
  1520. client:receive()
  1521. local breakpoint = client:receive()
  1522. local _, _, file, line = string.find(breakpoint, "^202 Paused%s+(.-)%s+(%d+)%s*$")
  1523. if file and line then
  1524. print("Paused at file " .. file )
  1525. print("Type 'help' for commands")
  1526. else
  1527. local _, _, size = string.find(breakpoint, "^401 Error in Execution (%d+)%s*$")
  1528. if size then
  1529. print("Error in remote application: ")
  1530. print(client:receive(size))
  1531. end
  1532. end
  1533. while true do
  1534. io.write("> ")
  1535. local file, line, err = handle(io.read("*line"), client)
  1536. if not file and err == false then break end -- completed debugging
  1537. end
  1538. client:close()
  1539. end
  1540. local cocreate
  1541. local function coro()
  1542. if cocreate then return end -- only set once
  1543. cocreate = cocreate or coroutine.create
  1544. coroutine.create = function(f, ...)
  1545. return cocreate(function(...)
  1546. mobdebug.on()
  1547. return f(...)
  1548. end, ...)
  1549. end
  1550. end
  1551. local moconew
  1552. local function moai()
  1553. if moconew then return end -- only set once
  1554. moconew = moconew or (MOAICoroutine and MOAICoroutine.new)
  1555. if not moconew then return end
  1556. MOAICoroutine.new = function(...)
  1557. local thread = moconew(...)
  1558. -- need to support both thread.run and getmetatable(thread).run, which
  1559. -- was used in earlier MOAI versions
  1560. local mt = thread.run and thread or getmetatable(thread)
  1561. local patched = mt.run
  1562. mt.run = function(self, f, ...)
  1563. return patched(self, function(...)
  1564. mobdebug.on()
  1565. return f(...)
  1566. end, ...)
  1567. end
  1568. return thread
  1569. end
  1570. end
  1571. -- make public functions available
  1572. mobdebug.setbreakpoint = set_breakpoint
  1573. mobdebug.removebreakpoint = remove_breakpoint
  1574. mobdebug.listen = listen
  1575. mobdebug.loop = loop
  1576. mobdebug.scratchpad = scratchpad
  1577. mobdebug.handle = handle
  1578. mobdebug.connect = connect
  1579. mobdebug.start = start
  1580. mobdebug.on = on
  1581. mobdebug.off = off
  1582. mobdebug.moai = moai
  1583. mobdebug.coro = coro
  1584. mobdebug.done = done
  1585. mobdebug.pause = function() step_into = true end
  1586. mobdebug.yield = nil -- callback
  1587. mobdebug.output = output
  1588. mobdebug.onexit = os and os.exit or done
  1589. mobdebug.onscratch = nil -- callback
  1590. mobdebug.basedir = function(b) if b then basedir = b end return basedir end
  1591. return mobdebug