#include #include #include #if defined(__APPLE__) #include #include #endif #define NANOSEC 1000000000 #define MICROSEC 1000000 static double get_time() { #if !defined(__APPLE__) struct timespec ti; clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ti); int sec = ti.tv_sec & 0xffff; int nsec = ti.tv_nsec; return (double)sec + (double)nsec / NANOSEC; #else struct task_thread_times_info aTaskInfo; mach_msg_type_number_t aTaskInfoCount = TASK_THREAD_TIMES_INFO_COUNT; if (KERN_SUCCESS != task_info(mach_task_self(), TASK_THREAD_TIMES_INFO, (task_info_t )&aTaskInfo, &aTaskInfoCount)) { return 0; } int sec = aTaskInfo.user_time.seconds & 0xffff; int msec = aTaskInfo.user_time.microseconds; return (double)sec + (double)msec / MICROSEC; #endif } static inline double diff_time(double start) { double now = get_time(); if (now < start) { return now + 0x10000 - start; } else { return now - start; } } static int lstart(lua_State *L) { lua_pushthread(L); lua_rawget(L, lua_upvalueindex(2)); if (!lua_isnil(L, -1)) { return luaL_error(L, "Thread %p start profile more than once", lua_topointer(L, 1)); } lua_pushthread(L); lua_pushnumber(L, 0); lua_rawset(L, lua_upvalueindex(2)); lua_pushthread(L); lua_pushnumber(L, get_time()); lua_rawset(L, lua_upvalueindex(1)); return 0; } static int lstop(lua_State *L) { lua_pushthread(L); lua_rawget(L, lua_upvalueindex(1)); luaL_checktype(L, -1, LUA_TNUMBER); double ti = diff_time(lua_tonumber(L, -1)); lua_pushthread(L); lua_rawget(L, lua_upvalueindex(2)); double total_time = lua_tonumber(L, -1); lua_pushthread(L); lua_pushnil(L); lua_rawset(L, lua_upvalueindex(1)); lua_pushthread(L); lua_pushnil(L); lua_rawset(L, lua_upvalueindex(2)); lua_pushnumber(L, ti + total_time); return 1; } static int lresume(lua_State *L) { lua_pushvalue(L,1); lua_rawget(L, lua_upvalueindex(2)); if (lua_isnil(L, -1)) { // check total time lua_pop(L,1); } else { lua_pop(L,1); lua_pushvalue(L,1); double ti = get_time(); lua_pushnumber(L, ti); lua_rawset(L, lua_upvalueindex(1)); // set start time } lua_CFunction co_resume = lua_tocfunction(L, lua_upvalueindex(3)); return co_resume(L); } static int lyield(lua_State *L) { lua_pushthread(L); lua_rawget(L, lua_upvalueindex(2)); // check total time if (lua_isnil(L, -1)) { lua_pop(L,1); } else { double ti = lua_tonumber(L, -1); lua_pop(L,1); lua_pushthread(L); lua_rawget(L, lua_upvalueindex(1)); double starttime = lua_tonumber(L, -1); lua_pop(L,1); ti += diff_time(starttime); lua_pushthread(L); lua_pushnumber(L, ti); lua_rawset(L, lua_upvalueindex(2)); } lua_CFunction co_yield = lua_tocfunction(L, lua_upvalueindex(3)); return co_yield(L); } int luaopen_profile(lua_State *L) { luaL_checkversion(L); luaL_Reg l[] = { { "start", lstart }, { "stop", lstop }, { "resume", lresume }, { "yield", lyield }, { NULL, NULL }, }; luaL_newlibtable(L,l); lua_newtable(L); // table thread->start time lua_newtable(L); // table thread->total time lua_newtable(L); // weak table lua_pushliteral(L, "kv"); lua_setfield(L, -2, "__mode"); lua_pushvalue(L, -1); lua_setmetatable(L, -3); lua_setmetatable(L, -3); lua_pushnil(L); luaL_setfuncs(L,l,3); int libtable = lua_gettop(L); lua_getglobal(L, "coroutine"); lua_getfield(L, -1, "resume"); lua_CFunction co_resume = lua_tocfunction(L, -1); if (co_resume == NULL) return luaL_error(L, "Can't get coroutine.resume"); lua_pop(L,1); lua_getfield(L, libtable, "resume"); lua_pushcfunction(L, co_resume); lua_setupvalue(L, -2, 3); lua_pop(L,1); lua_getfield(L, -1, "yield"); lua_CFunction co_yield = lua_tocfunction(L, -1); if (co_yield == NULL) return luaL_error(L, "Can't get coroutine.yield"); lua_pop(L,1); lua_getfield(L, libtable, "yield"); lua_pushcfunction(L, co_yield); lua_setupvalue(L, -2, 3); lua_settop(L, libtable); return 1; }