debug_console.lua 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. local skynet = require "skynet"
  2. local codecache = require "skynet.codecache"
  3. local core = require "skynet.core"
  4. local socket = require "skynet.socket"
  5. local snax = require "skynet.snax"
  6. local memory = require "skynet.memory"
  7. local httpd = require "http.httpd"
  8. local sockethelper = require "http.sockethelper"
  9. local arg = table.pack(...)
  10. assert(arg.n <= 2)
  11. local ip = (arg.n == 2 and arg[1] or "127.0.0.1")
  12. local port = tonumber(arg[arg.n])
  13. local COMMAND = {}
  14. local COMMANDX = {}
  15. local function format_table(t)
  16. local index = {}
  17. for k in pairs(t) do
  18. table.insert(index, k)
  19. end
  20. table.sort(index, function(a, b) return tostring(a) < tostring(b) end)
  21. local result = {}
  22. for _,v in ipairs(index) do
  23. table.insert(result, string.format("%s:%s",v,tostring(t[v])))
  24. end
  25. return table.concat(result,"\t")
  26. end
  27. local function dump_line(print, key, value)
  28. if type(value) == "table" then
  29. print(key, format_table(value))
  30. else
  31. print(key,tostring(value))
  32. end
  33. end
  34. local function dump_list(print, list)
  35. local index = {}
  36. for k in pairs(list) do
  37. table.insert(index, k)
  38. end
  39. table.sort(index, function(a, b) return tostring(a) < tostring(b) end)
  40. for _,v in ipairs(index) do
  41. dump_line(print, v, list[v])
  42. end
  43. end
  44. local function split_cmdline(cmdline)
  45. local split = {}
  46. for i in string.gmatch(cmdline, "%S+") do
  47. table.insert(split,i)
  48. end
  49. return split
  50. end
  51. local function docmd(cmdline, print, fd)
  52. local split = split_cmdline(cmdline)
  53. local command = split[1]
  54. local cmd = COMMAND[command]
  55. local ok, list
  56. if cmd then
  57. ok, list = pcall(cmd, table.unpack(split,2))
  58. else
  59. cmd = COMMANDX[command]
  60. if cmd then
  61. split.fd = fd
  62. split[1] = cmdline
  63. ok, list = pcall(cmd, split)
  64. else
  65. print("Invalid command, type help for command list")
  66. end
  67. end
  68. if ok then
  69. if list then
  70. if type(list) == "string" then
  71. print(list)
  72. else
  73. dump_list(print, list)
  74. end
  75. end
  76. print("<CMD OK>")
  77. else
  78. print(list)
  79. print("<CMD Error>")
  80. end
  81. end
  82. local function console_main_loop(stdin, print)
  83. print("Welcome to skynet console")
  84. skynet.error(stdin, "connected")
  85. local ok, err = pcall(function()
  86. while true do
  87. local cmdline = socket.readline(stdin, "\n")
  88. if not cmdline then
  89. break
  90. end
  91. if cmdline:sub(1,4) == "GET " then
  92. -- http
  93. local code, url = httpd.read_request(sockethelper.readfunc(stdin, cmdline.. "\n"), 8192)
  94. local cmdline = url:sub(2):gsub("/"," ")
  95. docmd(cmdline, print, stdin)
  96. break
  97. end
  98. if cmdline ~= "" then
  99. docmd(cmdline, print, stdin)
  100. end
  101. end
  102. end)
  103. if not ok then
  104. skynet.error(stdin, err)
  105. end
  106. skynet.error(stdin, "disconnected")
  107. socket.close(stdin)
  108. end
  109. skynet.start(function()
  110. local listen_socket = socket.listen (ip, port)
  111. skynet.error("Start debug console at " .. ip .. ":" .. port)
  112. socket.start(listen_socket , function(id, addr)
  113. local function print(...)
  114. local t = { ... }
  115. for k,v in ipairs(t) do
  116. t[k] = tostring(v)
  117. end
  118. socket.write(id, table.concat(t,"\t"))
  119. socket.write(id, "\n")
  120. end
  121. socket.start(id)
  122. skynet.fork(console_main_loop, id , print)
  123. end)
  124. end)
  125. function COMMAND.help()
  126. return {
  127. help = "This help message",
  128. list = "List all the service",
  129. stat = "Dump all stats",
  130. info = "info address : get service infomation",
  131. exit = "exit address : kill a lua service",
  132. kill = "kill address : kill service",
  133. mem = "mem : show memory status",
  134. gc = "gc : force every lua service do garbage collect",
  135. start = "lanuch a new lua service",
  136. snax = "lanuch a new snax service",
  137. clearcache = "clear lua code cache",
  138. service = "List unique service",
  139. task = "task address : show service task detail",
  140. inject = "inject address luascript.lua",
  141. logon = "logon address",
  142. logoff = "logoff address",
  143. log = "launch a new lua service with log",
  144. debug = "debug address : debug a lua service",
  145. signal = "signal address sig",
  146. cmem = "Show C memory info",
  147. shrtbl = "Show shared short string table info",
  148. ping = "ping address",
  149. call = "call address ...",
  150. }
  151. end
  152. function COMMAND.clearcache()
  153. codecache.clear()
  154. end
  155. function COMMAND.start(...)
  156. local ok, addr = pcall(skynet.newservice, ...)
  157. if ok then
  158. if addr then
  159. return { [skynet.address(addr)] = ... }
  160. else
  161. return "Exit"
  162. end
  163. else
  164. return "Failed"
  165. end
  166. end
  167. function COMMAND.log(...)
  168. local ok, addr = pcall(skynet.call, ".launcher", "lua", "LOGLAUNCH", "snlua", ...)
  169. if ok then
  170. if addr then
  171. return { [skynet.address(addr)] = ... }
  172. else
  173. return "Failed"
  174. end
  175. else
  176. return "Failed"
  177. end
  178. end
  179. function COMMAND.snax(...)
  180. local ok, s = pcall(snax.newservice, ...)
  181. if ok then
  182. local addr = s.handle
  183. return { [skynet.address(addr)] = ... }
  184. else
  185. return "Failed"
  186. end
  187. end
  188. function COMMAND.service()
  189. return skynet.call("SERVICE", "lua", "LIST")
  190. end
  191. local function adjust_address(address)
  192. if address:sub(1,1) ~= ":" then
  193. address = assert(tonumber("0x" .. address), "Need an address") | (skynet.harbor(skynet.self()) << 24)
  194. end
  195. return address
  196. end
  197. function COMMAND.list()
  198. return skynet.call(".launcher", "lua", "LIST")
  199. end
  200. function COMMAND.stat()
  201. return skynet.call(".launcher", "lua", "STAT")
  202. end
  203. function COMMAND.mem()
  204. return skynet.call(".launcher", "lua", "MEM")
  205. end
  206. function COMMAND.kill(address)
  207. return skynet.call(".launcher", "lua", "KILL", address)
  208. end
  209. function COMMAND.gc()
  210. return skynet.call(".launcher", "lua", "GC")
  211. end
  212. function COMMAND.exit(address)
  213. skynet.send(adjust_address(address), "debug", "EXIT")
  214. end
  215. function COMMAND.inject(address, filename)
  216. address = adjust_address(address)
  217. local f = io.open(filename, "rb")
  218. if not f then
  219. return "Can't open " .. filename
  220. end
  221. local source = f:read "*a"
  222. f:close()
  223. local ok, output = skynet.call(address, "debug", "RUN", source, filename)
  224. if ok == false then
  225. error(output)
  226. end
  227. return output
  228. end
  229. function COMMAND.task(address)
  230. address = adjust_address(address)
  231. return skynet.call(address,"debug","TASK")
  232. end
  233. function COMMAND.info(address, ...)
  234. address = adjust_address(address)
  235. return skynet.call(address,"debug","INFO", ...)
  236. end
  237. function COMMANDX.debug(cmd)
  238. local address = adjust_address(cmd[2])
  239. local agent = skynet.newservice "debug_agent"
  240. local stop
  241. local term_co = coroutine.running()
  242. local function forward_cmd()
  243. repeat
  244. -- notice : It's a bad practice to call socket.readline from two threads (this one and console_main_loop), be careful.
  245. skynet.call(agent, "lua", "ping") -- detect agent alive, if agent exit, raise error
  246. local cmdline = socket.readline(cmd.fd, "\n")
  247. cmdline = cmdline and cmdline:gsub("(.*)\r$", "%1")
  248. if not cmdline then
  249. skynet.send(agent, "lua", "cmd", "cont")
  250. break
  251. end
  252. skynet.send(agent, "lua", "cmd", cmdline)
  253. until stop or cmdline == "cont"
  254. end
  255. skynet.fork(function()
  256. pcall(forward_cmd)
  257. if not stop then -- block at skynet.call "start"
  258. term_co = nil
  259. else
  260. skynet.wakeup(term_co)
  261. end
  262. end)
  263. local ok, err = skynet.call(agent, "lua", "start", address, cmd.fd)
  264. stop = true
  265. if term_co then
  266. -- wait for fork coroutine exit.
  267. skynet.wait(term_co)
  268. end
  269. if not ok then
  270. error(err)
  271. end
  272. end
  273. function COMMAND.logon(address)
  274. address = adjust_address(address)
  275. core.command("LOGON", skynet.address(address))
  276. end
  277. function COMMAND.logoff(address)
  278. address = adjust_address(address)
  279. core.command("LOGOFF", skynet.address(address))
  280. end
  281. function COMMAND.signal(address, sig)
  282. address = skynet.address(adjust_address(address))
  283. if sig then
  284. core.command("SIGNAL", string.format("%s %d",address,sig))
  285. else
  286. core.command("SIGNAL", address)
  287. end
  288. end
  289. function COMMAND.cmem()
  290. local info = memory.info()
  291. local tmp = {}
  292. for k,v in pairs(info) do
  293. tmp[skynet.address(k)] = v
  294. end
  295. tmp.total = memory.total()
  296. tmp.block = memory.block()
  297. return tmp
  298. end
  299. function COMMAND.shrtbl()
  300. local n, total, longest, space = memory.ssinfo()
  301. return { n = n, total = total, longest = longest, space = space }
  302. end
  303. function COMMAND.ping(address)
  304. address = adjust_address(address)
  305. local ti = skynet.now()
  306. skynet.call(address, "debug", "PING")
  307. ti = skynet.now() - ti
  308. return tostring(ti)
  309. end
  310. function COMMANDX.call(cmd)
  311. local address = adjust_address(cmd[2])
  312. local cmdline = assert(cmd[1]:match("%S+%s+%S+%s(.+)") , "need arguments")
  313. local args_func = assert(load("return " .. cmdline, "debug console", "t", {}), "Invalid arguments")
  314. local args = table.pack(pcall(args_func))
  315. if not args[1] then
  316. error(args[2])
  317. end
  318. local rets = table.pack(skynet.call(address, "lua", table.unpack(args, 2, args.n)))
  319. return rets
  320. end