lua-profile.c 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. #define LUA_LIB
  2. #include <stdio.h>
  3. #include <lua.h>
  4. #include <lauxlib.h>
  5. #include <time.h>
  6. #if defined(__APPLE__)
  7. #include <mach/task.h>
  8. #include <mach/mach.h>
  9. #endif
  10. #define NANOSEC 1000000000
  11. #define MICROSEC 1000000
  12. // #define DEBUG_LOG
  13. static double
  14. get_time() {
  15. #if !defined(__APPLE__)
  16. struct timespec ti;
  17. clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ti);
  18. int sec = ti.tv_sec & 0xffff;
  19. int nsec = ti.tv_nsec;
  20. return (double)sec + (double)nsec / NANOSEC;
  21. #else
  22. struct task_thread_times_info aTaskInfo;
  23. mach_msg_type_number_t aTaskInfoCount = TASK_THREAD_TIMES_INFO_COUNT;
  24. if (KERN_SUCCESS != task_info(mach_task_self(), TASK_THREAD_TIMES_INFO, (task_info_t )&aTaskInfo, &aTaskInfoCount)) {
  25. return 0;
  26. }
  27. int sec = aTaskInfo.user_time.seconds & 0xffff;
  28. int msec = aTaskInfo.user_time.microseconds;
  29. return (double)sec + (double)msec / MICROSEC;
  30. #endif
  31. }
  32. static inline double
  33. diff_time(double start) {
  34. double now = get_time();
  35. if (now < start) {
  36. return now + 0x10000 - start;
  37. } else {
  38. return now - start;
  39. }
  40. }
  41. static int
  42. lstart(lua_State *L) {
  43. if (lua_gettop(L) != 0) {
  44. lua_settop(L,1);
  45. luaL_checktype(L, 1, LUA_TTHREAD);
  46. } else {
  47. lua_pushthread(L);
  48. }
  49. lua_pushvalue(L, 1); // push coroutine
  50. lua_rawget(L, lua_upvalueindex(2));
  51. if (!lua_isnil(L, -1)) {
  52. return luaL_error(L, "Thread %p start profile more than once", lua_topointer(L, 1));
  53. }
  54. lua_pushvalue(L, 1); // push coroutine
  55. lua_pushnumber(L, 0);
  56. lua_rawset(L, lua_upvalueindex(2));
  57. lua_pushvalue(L, 1); // push coroutine
  58. double ti = get_time();
  59. #ifdef DEBUG_LOG
  60. fprintf(stderr, "PROFILE [%p] start\n", L);
  61. #endif
  62. lua_pushnumber(L, ti);
  63. lua_rawset(L, lua_upvalueindex(1));
  64. return 0;
  65. }
  66. static int
  67. lstop(lua_State *L) {
  68. if (lua_gettop(L) != 0) {
  69. lua_settop(L,1);
  70. luaL_checktype(L, 1, LUA_TTHREAD);
  71. } else {
  72. lua_pushthread(L);
  73. }
  74. lua_pushvalue(L, 1); // push coroutine
  75. lua_rawget(L, lua_upvalueindex(1));
  76. if (lua_type(L, -1) != LUA_TNUMBER) {
  77. return luaL_error(L, "Call profile.start() before profile.stop()");
  78. }
  79. double ti = diff_time(lua_tonumber(L, -1));
  80. lua_pushvalue(L, 1); // push coroutine
  81. lua_rawget(L, lua_upvalueindex(2));
  82. double total_time = lua_tonumber(L, -1);
  83. lua_pushvalue(L, 1); // push coroutine
  84. lua_pushnil(L);
  85. lua_rawset(L, lua_upvalueindex(1));
  86. lua_pushvalue(L, 1); // push coroutine
  87. lua_pushnil(L);
  88. lua_rawset(L, lua_upvalueindex(2));
  89. total_time += ti;
  90. lua_pushnumber(L, total_time);
  91. #ifdef DEBUG_LOG
  92. fprintf(stderr, "PROFILE [%p] stop (%lf/%lf)\n", lua_tothread(L,1), ti, total_time);
  93. #endif
  94. return 1;
  95. }
  96. static int
  97. timing_resume(lua_State *L) {
  98. lua_pushvalue(L, -1);
  99. lua_rawget(L, lua_upvalueindex(2));
  100. if (lua_isnil(L, -1)) { // check total time
  101. lua_pop(L,2); // pop from coroutine
  102. } else {
  103. lua_pop(L,1);
  104. double ti = get_time();
  105. #ifdef DEBUG_LOG
  106. fprintf(stderr, "PROFILE [%p] resume %lf\n", lua_tothread(L, -1), ti);
  107. #endif
  108. lua_pushnumber(L, ti);
  109. lua_rawset(L, lua_upvalueindex(1)); // set start time
  110. }
  111. lua_CFunction co_resume = lua_tocfunction(L, lua_upvalueindex(3));
  112. return co_resume(L);
  113. }
  114. static int
  115. lresume(lua_State *L) {
  116. lua_pushvalue(L,1);
  117. return timing_resume(L);
  118. }
  119. static int
  120. lresume_co(lua_State *L) {
  121. luaL_checktype(L, 2, LUA_TTHREAD);
  122. lua_rotate(L, 2, -1); // 'from' coroutine rotate to the top(index -1)
  123. return timing_resume(L);
  124. }
  125. static int
  126. timing_yield(lua_State *L) {
  127. #ifdef DEBUG_LOG
  128. lua_State *from = lua_tothread(L, -1);
  129. #endif
  130. lua_pushvalue(L, -1);
  131. lua_rawget(L, lua_upvalueindex(2)); // check total time
  132. if (lua_isnil(L, -1)) {
  133. lua_pop(L,2);
  134. } else {
  135. double ti = lua_tonumber(L, -1);
  136. lua_pop(L,1);
  137. lua_pushvalue(L, -1); // push coroutine
  138. lua_rawget(L, lua_upvalueindex(1));
  139. double starttime = lua_tonumber(L, -1);
  140. lua_pop(L,1);
  141. double diff = diff_time(starttime);
  142. ti += diff;
  143. #ifdef DEBUG_LOG
  144. fprintf(stderr, "PROFILE [%p] yield (%lf/%lf)\n", from, diff, ti);
  145. #endif
  146. lua_pushvalue(L, -1); // push coroutine
  147. lua_pushnumber(L, ti);
  148. lua_rawset(L, lua_upvalueindex(2));
  149. lua_pop(L, 1); // pop coroutine
  150. }
  151. lua_CFunction co_yield = lua_tocfunction(L, lua_upvalueindex(3));
  152. return co_yield(L);
  153. }
  154. static int
  155. lyield(lua_State *L) {
  156. lua_pushthread(L);
  157. return timing_yield(L);
  158. }
  159. static int
  160. lyield_co(lua_State *L) {
  161. luaL_checktype(L, 1, LUA_TTHREAD);
  162. lua_rotate(L, 1, -1);
  163. return timing_yield(L);
  164. }
  165. LUAMOD_API int
  166. luaopen_skynet_profile(lua_State *L) {
  167. luaL_checkversion(L);
  168. luaL_Reg l[] = {
  169. { "start", lstart },
  170. { "stop", lstop },
  171. { "resume", lresume },
  172. { "yield", lyield },
  173. { "resume_co", lresume_co },
  174. { "yield_co", lyield_co },
  175. { NULL, NULL },
  176. };
  177. luaL_newlibtable(L,l);
  178. lua_newtable(L); // table thread->start time
  179. lua_newtable(L); // table thread->total time
  180. lua_newtable(L); // weak table
  181. lua_pushliteral(L, "kv");
  182. lua_setfield(L, -2, "__mode");
  183. lua_pushvalue(L, -1);
  184. lua_setmetatable(L, -3);
  185. lua_setmetatable(L, -3);
  186. lua_pushnil(L); // cfunction (coroutine.resume or coroutine.yield)
  187. luaL_setfuncs(L,l,3);
  188. int libtable = lua_gettop(L);
  189. lua_getglobal(L, "coroutine");
  190. lua_getfield(L, -1, "resume");
  191. lua_CFunction co_resume = lua_tocfunction(L, -1);
  192. if (co_resume == NULL)
  193. return luaL_error(L, "Can't get coroutine.resume");
  194. lua_pop(L,1);
  195. lua_getfield(L, libtable, "resume");
  196. lua_pushcfunction(L, co_resume);
  197. lua_setupvalue(L, -2, 3);
  198. lua_pop(L,1);
  199. lua_getfield(L, libtable, "resume_co");
  200. lua_pushcfunction(L, co_resume);
  201. lua_setupvalue(L, -2, 3);
  202. lua_pop(L,1);
  203. lua_getfield(L, -1, "yield");
  204. lua_CFunction co_yield = lua_tocfunction(L, -1);
  205. if (co_yield == NULL)
  206. return luaL_error(L, "Can't get coroutine.yield");
  207. lua_pop(L,1);
  208. lua_getfield(L, libtable, "yield");
  209. lua_pushcfunction(L, co_yield);
  210. lua_setupvalue(L, -2, 3);
  211. lua_pop(L,1);
  212. lua_getfield(L, libtable, "yield_co");
  213. lua_pushcfunction(L, co_yield);
  214. lua_setupvalue(L, -2, 3);
  215. lua_pop(L,1);
  216. lua_settop(L, libtable);
  217. return 1;
  218. }