Files
skynet/lualib-src/lua-profile.c
2014-04-22 20:00:59 +08:00

188 lines
3.9 KiB
C

#include <lua.h>
#include <lauxlib.h>
#include <time.h>
#if defined(__APPLE__)
#include <mach/task.h>
#include <mach/mach.h>
#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;
}