snapshot.c 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427
  1. #include <lua.h>
  2. #include <lauxlib.h>
  3. static void mark_object(lua_State *L, lua_State *dL, const void * parent, const char * desc);
  4. #if LUA_VERSION_NUM == 501
  5. static void
  6. luaL_checkversion(lua_State *L) {
  7. if (lua_pushthread(L) == 0) {
  8. luaL_error(L, "Must require in main thread");
  9. }
  10. lua_setfield(L, LUA_REGISTRYINDEX, "mainthread");
  11. }
  12. static void
  13. lua_rawsetp(lua_State *L, int idx, const void *p) {
  14. if (idx < 0) {
  15. idx += lua_gettop(L) + 1;
  16. }
  17. lua_pushlightuserdata(L, (void *)p);
  18. lua_insert(L, -2);
  19. lua_rawset(L, idx);
  20. }
  21. static void
  22. lua_rawgetp(lua_State *L, int idx, const void *p) {
  23. if (idx < 0) {
  24. idx += lua_gettop(L) + 1;
  25. }
  26. lua_pushlightuserdata(L, (void *)p);
  27. lua_rawget(L, idx);
  28. }
  29. static void
  30. lua_getuservalue(lua_State *L, int idx) {
  31. lua_getfenv(L, idx);
  32. }
  33. static void
  34. mark_function_env(lua_State *L, lua_State *dL, const void * t) {
  35. lua_getfenv(L,-1);
  36. if (lua_istable(L,-1)) {
  37. mark_object(L, dL, t, "[environment]");
  38. } else {
  39. lua_pop(L,1);
  40. }
  41. }
  42. // lua 5.1 has no light c function
  43. #define is_lightcfunction(L, idx) (0)
  44. #else
  45. #define mark_function_env(L,dL,t)
  46. static int
  47. is_lightcfunction(lua_State *L, int idx) {
  48. if (lua_iscfunction(L, idx)) {
  49. if (lua_getupvalue(L, idx, 1) == NULL) {
  50. return 1;
  51. }
  52. lua_pop(L, 1);
  53. }
  54. return 0;
  55. }
  56. #endif
  57. #include <stdbool.h>
  58. #include <stdio.h>
  59. #include <string.h>
  60. #define TABLE 1
  61. #define FUNCTION 2
  62. #define SOURCE 3
  63. #define THREAD 4
  64. #define USERDATA 5
  65. #define MARK 6
  66. static bool
  67. ismarked(lua_State *dL, const void *p) {
  68. lua_rawgetp(dL, MARK, p);
  69. if (lua_isnil(dL,-1)) {
  70. lua_pop(dL,1);
  71. lua_pushboolean(dL,1);
  72. lua_rawsetp(dL, MARK, p);
  73. return false;
  74. }
  75. lua_pop(dL,1);
  76. return true;
  77. }
  78. static const void *
  79. readobject(lua_State *L, lua_State *dL, const void *parent, const char *desc) {
  80. int t = lua_type(L, -1);
  81. int tidx = 0;
  82. switch (t) {
  83. case LUA_TTABLE:
  84. tidx = TABLE;
  85. break;
  86. case LUA_TFUNCTION:
  87. if (is_lightcfunction(L, -1)) {
  88. lua_pop(L, 1);
  89. return NULL;
  90. }
  91. tidx = FUNCTION;
  92. break;
  93. case LUA_TTHREAD:
  94. tidx = THREAD;
  95. break;
  96. case LUA_TUSERDATA:
  97. tidx = USERDATA;
  98. break;
  99. default:
  100. lua_pop(L, 1);
  101. return NULL;
  102. }
  103. const void * p = lua_topointer(L, -1);
  104. if (ismarked(dL, p)) {
  105. lua_rawgetp(dL, tidx, p);
  106. if (!lua_isnil(dL,-1)) {
  107. lua_pushstring(dL,desc);
  108. lua_rawsetp(dL, -2, parent);
  109. }
  110. lua_pop(dL,1);
  111. lua_pop(L,1);
  112. return NULL;
  113. }
  114. lua_newtable(dL);
  115. lua_pushstring(dL,desc);
  116. lua_rawsetp(dL, -2, parent);
  117. lua_rawsetp(dL, tidx, p);
  118. return p;
  119. }
  120. static const char *
  121. keystring(lua_State *L, int index, char * buffer, size_t size) {
  122. int t = lua_type(L,index);
  123. switch (t) {
  124. case LUA_TSTRING:
  125. return lua_tostring(L,index);
  126. case LUA_TNUMBER:
  127. snprintf(buffer, size, "[%lg]",lua_tonumber(L,index));
  128. break;
  129. case LUA_TBOOLEAN:
  130. snprintf(buffer, size, "[%s]",lua_toboolean(L,index) ? "true" : "false");
  131. break;
  132. case LUA_TNIL:
  133. snprintf(buffer, size, "[nil]");
  134. break;
  135. default:
  136. snprintf(buffer, size, "[%s:%p]",lua_typename(L,t),lua_topointer(L,index));
  137. break;
  138. }
  139. return buffer;
  140. }
  141. static void
  142. mark_table(lua_State *L, lua_State *dL, const void * parent, const char * desc) {
  143. const void * t = readobject(L, dL, parent, desc);
  144. if (t == NULL)
  145. return;
  146. bool weakk = false;
  147. bool weakv = false;
  148. if (lua_getmetatable(L, -1)) {
  149. lua_pushliteral(L, "__mode");
  150. lua_rawget(L, -2);
  151. if (lua_isstring(L,-1)) {
  152. const char *mode = lua_tostring(L, -1);
  153. if (strchr(mode, 'k')) {
  154. weakk = true;
  155. }
  156. if (strchr(mode, 'v')) {
  157. weakv = true;
  158. }
  159. }
  160. lua_pop(L,1);
  161. luaL_checkstack(L, LUA_MINSTACK, NULL);
  162. mark_table(L, dL, t, "[metatable]");
  163. }
  164. lua_pushnil(L);
  165. while (lua_next(L, -2) != 0) {
  166. if (weakv) {
  167. lua_pop(L,1);
  168. } else {
  169. char temp[32];
  170. const char * desc = keystring(L, -2, temp, sizeof(temp));
  171. mark_object(L, dL, t , desc);
  172. }
  173. if (!weakk) {
  174. lua_pushvalue(L,-1);
  175. mark_object(L, dL, t , "[key]");
  176. }
  177. }
  178. lua_pop(L,1);
  179. }
  180. static void
  181. mark_userdata(lua_State *L, lua_State *dL, const void * parent, const char *desc) {
  182. const void * t = readobject(L, dL, parent, desc);
  183. if (t == NULL)
  184. return;
  185. if (lua_getmetatable(L, -1)) {
  186. mark_table(L, dL, t, "[metatable]");
  187. }
  188. lua_getuservalue(L,-1);
  189. if (lua_isnil(L,-1)) {
  190. lua_pop(L,2);
  191. } else {
  192. mark_object(L, dL, t, "[uservalue]");
  193. lua_pop(L,1);
  194. }
  195. }
  196. static void
  197. mark_function(lua_State *L, lua_State *dL, const void * parent, const char *desc) {
  198. const void * t = readobject(L, dL, parent, desc);
  199. if (t == NULL)
  200. return;
  201. mark_function_env(L,dL,t);
  202. int i;
  203. for (i=1;;i++) {
  204. const char *name = lua_getupvalue(L,-1,i);
  205. if (name == NULL)
  206. break;
  207. mark_object(L, dL, t, name[0] ? name : "[upvalue]");
  208. }
  209. if (lua_iscfunction(L,-1)) {
  210. lua_pop(L,1);
  211. } else {
  212. lua_Debug ar;
  213. lua_getinfo(L, ">S", &ar);
  214. luaL_Buffer b;
  215. luaL_buffinit(dL, &b);
  216. luaL_addstring(&b, "func: ");
  217. luaL_addstring(&b, ar.short_src);
  218. char tmp[16];
  219. sprintf(tmp,":%d",ar.linedefined);
  220. luaL_addstring(&b, tmp);
  221. luaL_pushresult(&b);
  222. lua_rawsetp(dL, SOURCE, t);
  223. }
  224. }
  225. static void
  226. mark_thread(lua_State *L, lua_State *dL, const void * parent, const char *desc) {
  227. const void * t = readobject(L, dL, parent, desc);
  228. if (t == NULL)
  229. return;
  230. int level = 0;
  231. lua_State *cL = lua_tothread(L,-1);
  232. if (cL == L) {
  233. level = 1;
  234. } else {
  235. // mark stack
  236. int top = lua_gettop(cL);
  237. luaL_checkstack(cL, 1, NULL);
  238. int i;
  239. char tmp[16];
  240. for (i=0;i<top;i++) {
  241. lua_pushvalue(cL, i+1);
  242. sprintf(tmp, "[%d]", i+1);
  243. mark_object(cL, dL, cL, tmp);
  244. }
  245. }
  246. lua_Debug ar;
  247. luaL_Buffer b;
  248. luaL_buffinit(dL, &b);
  249. while (lua_getstack(cL, level, &ar)) {
  250. char tmp[128];
  251. lua_getinfo(cL, "Sl", &ar);
  252. luaL_addstring(&b, ar.short_src);
  253. if (ar.currentline >=0) {
  254. char tmp[16];
  255. sprintf(tmp,":%d ",ar.currentline);
  256. luaL_addstring(&b, tmp);
  257. }
  258. int i,j;
  259. for (j=1;j>-1;j-=2) {
  260. for (i=j;;i+=j) {
  261. const char * name = lua_getlocal(cL, &ar, i);
  262. if (name == NULL)
  263. break;
  264. snprintf(tmp, sizeof(tmp), "%s : %s:%d",name,ar.short_src,ar.currentline);
  265. mark_object(cL, dL, t, tmp);
  266. }
  267. }
  268. ++level;
  269. }
  270. luaL_addstring(&b, "thread: ");
  271. luaL_pushresult(&b);
  272. lua_rawsetp(dL, SOURCE, t);
  273. lua_pop(L,1);
  274. }
  275. static void
  276. mark_object(lua_State *L, lua_State *dL, const void * parent, const char *desc) {
  277. luaL_checkstack(L, LUA_MINSTACK, NULL);
  278. int t = lua_type(L, -1);
  279. switch (t) {
  280. case LUA_TTABLE:
  281. mark_table(L, dL, parent, desc);
  282. break;
  283. case LUA_TUSERDATA:
  284. mark_userdata(L, dL, parent, desc);
  285. break;
  286. case LUA_TFUNCTION:
  287. mark_function(L, dL, parent, desc);
  288. break;
  289. case LUA_TTHREAD:
  290. mark_thread(L, dL, parent, desc);
  291. break;
  292. default:
  293. lua_pop(L,1);
  294. break;
  295. }
  296. }
  297. static int
  298. count_table(lua_State *L, int idx) {
  299. int n = 0;
  300. lua_pushnil(L);
  301. while (lua_next(L, idx) != 0) {
  302. ++n;
  303. lua_pop(L,1);
  304. }
  305. return n;
  306. }
  307. static void
  308. gen_table_desc(lua_State *dL, luaL_Buffer *b, const void * parent, const char *desc) {
  309. char tmp[32];
  310. size_t l = snprintf(tmp, sizeof(tmp), "%p : ",parent);
  311. luaL_addlstring(b, tmp, l);
  312. luaL_addstring(b, desc);
  313. luaL_addchar(b, '\n');
  314. }
  315. static void
  316. pdesc(lua_State *L, lua_State *dL, int idx, const char * typename) {
  317. lua_pushnil(dL);
  318. while (lua_next(dL, idx) != 0) {
  319. luaL_Buffer b;
  320. luaL_buffinit(L, &b);
  321. const void * key = lua_touserdata(dL, -2);
  322. if (idx == FUNCTION) {
  323. lua_rawgetp(dL, SOURCE, key);
  324. if (lua_isnil(dL, -1)) {
  325. luaL_addstring(&b,"cfunction\n");
  326. } else {
  327. size_t l = 0;
  328. const char * s = lua_tolstring(dL, -1, &l);
  329. luaL_addlstring(&b,s,l);
  330. luaL_addchar(&b,'\n');
  331. }
  332. lua_pop(dL, 1);
  333. } else if (idx == THREAD) {
  334. lua_rawgetp(dL, SOURCE, key);
  335. size_t l = 0;
  336. const char * s = lua_tolstring(dL, -1, &l);
  337. luaL_addlstring(&b,s,l);
  338. luaL_addchar(&b,'\n');
  339. lua_pop(dL, 1);
  340. } else {
  341. luaL_addstring(&b, typename);
  342. luaL_addchar(&b,'\n');
  343. }
  344. lua_pushnil(dL);
  345. while (lua_next(dL, -2) != 0) {
  346. const void * parent = lua_touserdata(dL,-2);
  347. const char * desc = luaL_checkstring(dL,-1);
  348. gen_table_desc(dL, &b, parent, desc);
  349. lua_pop(dL,1);
  350. }
  351. luaL_pushresult(&b);
  352. lua_rawsetp(L, -2, key);
  353. lua_pop(dL,1);
  354. }
  355. }
  356. static void
  357. gen_result(lua_State *L, lua_State *dL) {
  358. int count = 0;
  359. count += count_table(dL, TABLE);
  360. count += count_table(dL, FUNCTION);
  361. count += count_table(dL, USERDATA);
  362. count += count_table(dL, THREAD);
  363. lua_createtable(L, 0, count);
  364. pdesc(L, dL, TABLE, "table");
  365. pdesc(L, dL, USERDATA, "userdata");
  366. pdesc(L, dL, FUNCTION, "function");
  367. pdesc(L, dL, THREAD, "thread");
  368. }
  369. static int
  370. snapshot(lua_State *L) {
  371. int i;
  372. lua_State *dL = luaL_newstate();
  373. for (i=0;i<MARK;i++) {
  374. lua_newtable(dL);
  375. }
  376. lua_pushvalue(L, LUA_REGISTRYINDEX);
  377. mark_table(L, dL, NULL, "[registry]");
  378. gen_result(L, dL);
  379. lua_close(dL);
  380. return 1;
  381. }
  382. int
  383. luaopen_snapshot(lua_State *L) {
  384. luaL_checkversion(L);
  385. lua_pushcfunction(L, snapshot);
  386. return 1;
  387. }