diff options
author | Mike Pall <mike> | 2013-09-02 01:49:19 +0200 |
---|---|---|
committer | Mike Pall <mike> | 2013-09-02 01:49:19 +0200 |
commit | 4dce22c40d38dd7dc3d05cc383ddf004fb1f92c4 (patch) | |
tree | 34800356c01e0a4f7f820d5affb131de8c091d89 | |
parent | 19c2dd17dbaf66f1341efe2a71a26f0b5724f9e1 (diff) | |
download | luajit-4dce22c40d38dd7dc3d05cc383ddf004fb1f92c4.tar.gz luajit-4dce22c40d38dd7dc3d05cc383ddf004fb1f92c4.tar.bz2 luajit-4dce22c40d38dd7dc3d05cc383ddf004fb1f92c4.zip |
Add low-overhead profiler. Part 1: interpreter, low-level C API.
-rw-r--r-- | src/Makefile | 2 | ||||
-rw-r--r-- | src/Makefile.dep | 36 | ||||
-rw-r--r-- | src/lj_arch.h | 12 | ||||
-rw-r--r-- | src/lj_debug.c | 111 | ||||
-rw-r--r-- | src/lj_debug.h | 6 | ||||
-rw-r--r-- | src/lj_dispatch.c | 55 | ||||
-rw-r--r-- | src/lj_dispatch.h | 6 | ||||
-rw-r--r-- | src/lj_obj.h | 1 | ||||
-rw-r--r-- | src/lj_profile.c | 274 | ||||
-rw-r--r-- | src/lj_profile.h | 17 | ||||
-rw-r--r-- | src/lj_state.c | 6 | ||||
-rw-r--r-- | src/lj_vm.h | 1 | ||||
-rw-r--r-- | src/ljamalg.c | 1 | ||||
-rw-r--r-- | src/luajit.h | 9 | ||||
-rw-r--r-- | src/vm_arm.dasc | 12 | ||||
-rw-r--r-- | src/vm_mips.dasc | 14 | ||||
-rw-r--r-- | src/vm_ppc.dasc | 13 | ||||
-rw-r--r-- | src/vm_x86.dasc | 15 |
18 files changed, 558 insertions, 33 deletions
diff --git a/src/Makefile b/src/Makefile index 441feffb..e7f48fdd 100644 --- a/src/Makefile +++ b/src/Makefile | |||
@@ -446,7 +446,7 @@ LJLIB_C= $(LJLIB_O:.o=.c) | |||
446 | LJCORE_O= lj_gc.o lj_err.o lj_char.o lj_bc.o lj_obj.o lj_buf.o \ | 446 | LJCORE_O= lj_gc.o lj_err.o lj_char.o lj_bc.o lj_obj.o lj_buf.o \ |
447 | lj_str.o lj_tab.o lj_func.o lj_udata.o lj_meta.o lj_debug.o \ | 447 | lj_str.o lj_tab.o lj_func.o lj_udata.o lj_meta.o lj_debug.o \ |
448 | lj_state.o lj_dispatch.o lj_vmevent.o lj_vmmath.o lj_strscan.o \ | 448 | lj_state.o lj_dispatch.o lj_vmevent.o lj_vmmath.o lj_strscan.o \ |
449 | lj_strfmt.o lj_api.o \ | 449 | lj_strfmt.o lj_api.o lj_profile.o \ |
450 | lj_lex.o lj_parse.o lj_bcread.o lj_bcwrite.o lj_load.o \ | 450 | lj_lex.o lj_parse.o lj_bcread.o lj_bcwrite.o lj_load.o \ |
451 | lj_ir.o lj_opt_mem.o lj_opt_fold.o lj_opt_narrow.o \ | 451 | lj_ir.o lj_opt_mem.o lj_opt_fold.o lj_opt_narrow.o \ |
452 | lj_opt_dce.o lj_opt_loop.o lj_opt_split.o lj_opt_sink.o \ | 452 | lj_opt_dce.o lj_opt_loop.o lj_opt_split.o lj_opt_sink.o \ |
diff --git a/src/Makefile.dep b/src/Makefile.dep index 0ea0d98e..7991bd16 100644 --- a/src/Makefile.dep +++ b/src/Makefile.dep | |||
@@ -102,7 +102,7 @@ lj_dispatch.o: lj_dispatch.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ | |||
102 | lj_err.h lj_errmsg.h lj_buf.h lj_gc.h lj_str.h lj_func.h lj_tab.h \ | 102 | lj_err.h lj_errmsg.h lj_buf.h lj_gc.h lj_str.h lj_func.h lj_tab.h \ |
103 | lj_meta.h lj_debug.h lj_state.h lj_frame.h lj_bc.h lj_ff.h lj_ffdef.h \ | 103 | lj_meta.h lj_debug.h lj_state.h lj_frame.h lj_bc.h lj_ff.h lj_ffdef.h \ |
104 | lj_strfmt.h lj_jit.h lj_ir.h lj_ccallback.h lj_ctype.h lj_trace.h \ | 104 | lj_strfmt.h lj_jit.h lj_ir.h lj_ccallback.h lj_ctype.h lj_trace.h \ |
105 | lj_dispatch.h lj_traceerr.h lj_vm.h luajit.h | 105 | lj_dispatch.h lj_traceerr.h lj_profile.h lj_vm.h luajit.h |
106 | lj_err.o: lj_err.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_err.h \ | 106 | lj_err.o: lj_err.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_err.h \ |
107 | lj_errmsg.h lj_debug.h lj_str.h lj_func.h lj_state.h lj_frame.h lj_bc.h \ | 107 | lj_errmsg.h lj_debug.h lj_str.h lj_func.h lj_state.h lj_frame.h lj_bc.h \ |
108 | lj_ff.h lj_ffdef.h lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h \ | 108 | lj_ff.h lj_ffdef.h lj_trace.h lj_jit.h lj_ir.h lj_dispatch.h \ |
@@ -168,6 +168,9 @@ lj_parse.o: lj_parse.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ | |||
168 | lj_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_buf.h lj_str.h lj_tab.h \ | 168 | lj_gc.h lj_err.h lj_errmsg.h lj_debug.h lj_buf.h lj_str.h lj_tab.h \ |
169 | lj_func.h lj_state.h lj_bc.h lj_ctype.h lj_strfmt.h lj_lex.h lj_parse.h \ | 169 | lj_func.h lj_state.h lj_bc.h lj_ctype.h lj_strfmt.h lj_lex.h lj_parse.h \ |
170 | lj_vm.h lj_vmevent.h | 170 | lj_vm.h lj_vmevent.h |
171 | lj_profile.o: lj_profile.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ | ||
172 | lj_buf.h lj_gc.h lj_str.h lj_frame.h lj_bc.h lj_debug.h lj_dispatch.h \ | ||
173 | lj_jit.h lj_ir.h lj_profile.h luajit.h | ||
171 | lj_record.o: lj_record.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ | 174 | lj_record.o: lj_record.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
172 | lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_meta.h lj_frame.h lj_bc.h \ | 175 | lj_err.h lj_errmsg.h lj_str.h lj_tab.h lj_meta.h lj_frame.h lj_bc.h \ |
173 | lj_ctype.h lj_gc.h lj_ff.h lj_ffdef.h lj_ir.h lj_jit.h lj_ircall.h \ | 176 | lj_ctype.h lj_gc.h lj_ff.h lj_ffdef.h lj_ir.h lj_jit.h lj_ircall.h \ |
@@ -180,7 +183,7 @@ lj_snap.o: lj_snap.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ | |||
180 | lj_state.o: lj_state.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ | 183 | lj_state.o: lj_state.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
181 | lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_tab.h lj_func.h \ | 184 | lj_gc.h lj_err.h lj_errmsg.h lj_buf.h lj_str.h lj_tab.h lj_func.h \ |
182 | lj_meta.h lj_state.h lj_frame.h lj_bc.h lj_ctype.h lj_trace.h lj_jit.h \ | 185 | lj_meta.h lj_state.h lj_frame.h lj_bc.h lj_ctype.h lj_trace.h lj_jit.h \ |
183 | lj_ir.h lj_dispatch.h lj_traceerr.h lj_vm.h lj_lex.h lj_alloc.h | 186 | lj_ir.h lj_dispatch.h lj_traceerr.h lj_vm.h lj_lex.h lj_alloc.h luajit.h |
184 | lj_str.o: lj_str.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ | 187 | lj_str.o: lj_str.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h lj_gc.h \ |
185 | lj_err.h lj_errmsg.h lj_str.h lj_char.h | 188 | lj_err.h lj_errmsg.h lj_str.h lj_char.h |
186 | lj_strfmt.o: lj_strfmt.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ | 189 | lj_strfmt.o: lj_strfmt.c lj_obj.h lua.h luaconf.h lj_def.h lj_arch.h \ |
@@ -208,20 +211,21 @@ ljamalg.o: ljamalg.c lua.h luaconf.h lauxlib.h lj_gc.c lj_obj.h lj_def.h \ | |||
208 | lj_vm.h lj_err.c lj_debug.h lj_ff.h lj_ffdef.h lj_strfmt.h lj_char.c \ | 211 | lj_vm.h lj_err.c lj_debug.h lj_ff.h lj_ffdef.h lj_strfmt.h lj_char.c \ |
209 | lj_char.h lj_bc.c lj_bcdef.h lj_obj.c lj_buf.c lj_str.c lj_tab.c \ | 212 | lj_char.h lj_bc.c lj_bcdef.h lj_obj.c lj_buf.c lj_str.c lj_tab.c \ |
210 | lj_func.c lj_udata.c lj_meta.c lj_strscan.h lj_lib.h lj_debug.c \ | 213 | lj_func.c lj_udata.c lj_meta.c lj_strscan.h lj_lib.h lj_debug.c \ |
211 | lj_state.c lj_lex.h lj_alloc.h lj_dispatch.c lj_ccallback.h luajit.h \ | 214 | lj_state.c lj_lex.h lj_alloc.h luajit.h lj_dispatch.c lj_ccallback.h \ |
212 | lj_vmevent.c lj_vmevent.h lj_vmmath.c lj_strscan.c lj_strfmt.c lj_api.c \ | 215 | lj_profile.h lj_vmevent.c lj_vmevent.h lj_vmmath.c lj_strscan.c \ |
213 | lj_lex.c lualib.h lj_parse.h lj_parse.c lj_bcread.c lj_bcdump.h \ | 216 | lj_strfmt.c lj_api.c lj_profile.c lj_lex.c lualib.h lj_parse.h \ |
214 | lj_bcwrite.c lj_load.c lj_ctype.c lj_cdata.c lj_cconv.h lj_cconv.c \ | 217 | lj_parse.c lj_bcread.c lj_bcdump.h lj_bcwrite.c lj_load.c lj_ctype.c \ |
215 | lj_ccall.c lj_ccall.h lj_ccallback.c lj_target.h lj_target_*.h \ | 218 | lj_cdata.c lj_cconv.h lj_cconv.c lj_ccall.c lj_ccall.h lj_ccallback.c \ |
216 | lj_mcode.h lj_carith.c lj_carith.h lj_clib.c lj_clib.h lj_cparse.c \ | 219 | lj_target.h lj_target_*.h lj_mcode.h lj_carith.c lj_carith.h lj_clib.c \ |
217 | lj_cparse.h lj_lib.c lj_ir.c lj_ircall.h lj_iropt.h lj_opt_mem.c \ | 220 | lj_clib.h lj_cparse.c lj_cparse.h lj_lib.c lj_ir.c lj_ircall.h \ |
218 | lj_opt_fold.c lj_folddef.h lj_opt_narrow.c lj_opt_dce.c lj_opt_loop.c \ | 221 | lj_iropt.h lj_opt_mem.c lj_opt_fold.c lj_folddef.h lj_opt_narrow.c \ |
219 | lj_snap.h lj_opt_split.c lj_opt_sink.c lj_mcode.c lj_snap.c lj_record.c \ | 222 | lj_opt_dce.c lj_opt_loop.c lj_snap.h lj_opt_split.c lj_opt_sink.c \ |
220 | lj_record.h lj_ffrecord.h lj_crecord.c lj_crecord.h lj_ffrecord.c \ | 223 | lj_mcode.c lj_snap.c lj_record.c lj_record.h lj_ffrecord.h lj_crecord.c \ |
221 | lj_recdef.h lj_asm.c lj_asm.h lj_emit_*.h lj_asm_*.h lj_trace.c \ | 224 | lj_crecord.h lj_ffrecord.c lj_recdef.h lj_asm.c lj_asm.h lj_emit_*.h \ |
222 | lj_gdbjit.h lj_gdbjit.c lj_alloc.c lib_aux.c lib_base.c lj_libdef.h \ | 225 | lj_asm_*.h lj_trace.c lj_gdbjit.h lj_gdbjit.c lj_alloc.c lib_aux.c \ |
223 | lib_math.c lib_string.c lib_table.c lib_io.c lib_os.c lib_package.c \ | 226 | lib_base.c lj_libdef.h lib_math.c lib_string.c lib_table.c lib_io.c \ |
224 | lib_debug.c lib_bit.c lib_jit.c lib_ffi.c lib_init.c | 227 | lib_os.c lib_package.c lib_debug.c lib_bit.c lib_jit.c lib_ffi.c \ |
228 | lib_init.c | ||
225 | luajit.o: luajit.c lua.h luaconf.h lauxlib.h lualib.h luajit.h lj_arch.h | 229 | luajit.o: luajit.c lua.h luaconf.h lauxlib.h lualib.h luajit.h lj_arch.h |
226 | host/buildvm.o: host/buildvm.c host/buildvm.h lj_def.h lua.h luaconf.h \ | 230 | host/buildvm.o: host/buildvm.c host/buildvm.h lj_def.h lua.h luaconf.h \ |
227 | lj_arch.h lj_obj.h lj_def.h lj_arch.h lj_gc.h lj_obj.h lj_bc.h lj_ir.h \ | 231 | lj_arch.h lj_obj.h lj_def.h lj_arch.h lj_gc.h lj_obj.h lj_bc.h lj_ir.h \ |
diff --git a/src/lj_arch.h b/src/lj_arch.h index c5f2fb3d..ccb54270 100644 --- a/src/lj_arch.h +++ b/src/lj_arch.h | |||
@@ -366,6 +366,18 @@ | |||
366 | #define LJ_HASFFI 1 | 366 | #define LJ_HASFFI 1 |
367 | #endif | 367 | #endif |
368 | 368 | ||
369 | #if defined(LUAJIT_DISABLE_PROFILE) | ||
370 | #define LJ_HASPROFILE 0 | ||
371 | #elif LJ_TARGET_POSIX | ||
372 | #define LJ_HASPROFILE 1 | ||
373 | #define LJ_PROFILE_SIGPROF 1 | ||
374 | #elif LJ_TARGET_WINDOWS | ||
375 | #define LJ_HASPROFILE 1 | ||
376 | #define LJ_PROFILE_WTHREAD 1 | ||
377 | #else | ||
378 | #define LJ_HASPROFILE 0 | ||
379 | #endif | ||
380 | |||
369 | #ifndef LJ_ARCH_HASFPU | 381 | #ifndef LJ_ARCH_HASFPU |
370 | #define LJ_ARCH_HASFPU 1 | 382 | #define LJ_ARCH_HASFPU 1 |
371 | #endif | 383 | #endif |
diff --git a/src/lj_debug.c b/src/lj_debug.c index 24efd2ff..8166fcc0 100644 --- a/src/lj_debug.c +++ b/src/lj_debug.c | |||
@@ -28,7 +28,7 @@ cTValue *lj_debug_frame(lua_State *L, int level, int *size) | |||
28 | /* Traverse frames backwards. */ | 28 | /* Traverse frames backwards. */ |
29 | for (nextframe = frame = L->base-1; frame > bot; ) { | 29 | for (nextframe = frame = L->base-1; frame > bot; ) { |
30 | if (frame_gc(frame) == obj2gco(L)) | 30 | if (frame_gc(frame) == obj2gco(L)) |
31 | level++; /* Skip dummy frames. See lj_meta_call(). */ | 31 | level++; /* Skip dummy frames. See lj_err_optype_call(). */ |
32 | if (level-- == 0) { | 32 | if (level-- == 0) { |
33 | *size = (int)(nextframe - frame); | 33 | *size = (int)(nextframe - frame); |
34 | return frame; /* Level found. */ | 34 | return frame; /* Level found. */ |
@@ -278,9 +278,9 @@ restart: | |||
278 | } | 278 | } |
279 | 279 | ||
280 | /* Deduce function name from caller of a frame. */ | 280 | /* Deduce function name from caller of a frame. */ |
281 | const char *lj_debug_funcname(lua_State *L, TValue *frame, const char **name) | 281 | const char *lj_debug_funcname(lua_State *L, cTValue *frame, const char **name) |
282 | { | 282 | { |
283 | TValue *pframe; | 283 | cTValue *pframe; |
284 | GCfunc *fn; | 284 | GCfunc *fn; |
285 | BCPos pc; | 285 | BCPos pc; |
286 | if (frame <= tvref(L->stack)) | 286 | if (frame <= tvref(L->stack)) |
@@ -534,6 +534,111 @@ LUA_API int lua_getstack(lua_State *L, int level, lua_Debug *ar) | |||
534 | } | 534 | } |
535 | } | 535 | } |
536 | 536 | ||
537 | #if LJ_HASPROFILE | ||
538 | /* Put the chunkname into a buffer. */ | ||
539 | static int debug_putchunkname(SBuf *sb, GCproto *pt, int pathstrip) | ||
540 | { | ||
541 | GCstr *name = proto_chunkname(pt); | ||
542 | const char *p = strdata(name); | ||
543 | if (pt->firstline == ~(BCLine)0) { | ||
544 | lj_buf_putmem(sb, "[builtin:", 9); | ||
545 | lj_buf_putstr(sb, name); | ||
546 | lj_buf_putb(sb, ']'); | ||
547 | return 0; | ||
548 | } | ||
549 | if (*p == '=' || *p == '@') { | ||
550 | MSize len = name->len-1; | ||
551 | p++; | ||
552 | if (pathstrip) { | ||
553 | int i; | ||
554 | for (i = len-1; i >= 0; i--) | ||
555 | if (p[i] == '/' || p[i] == '\\') { | ||
556 | len -= i+1; | ||
557 | p = p+i+1; | ||
558 | break; | ||
559 | } | ||
560 | } | ||
561 | lj_buf_putmem(sb, p, len); | ||
562 | } else { | ||
563 | lj_buf_putmem(sb, "[string]", 9); | ||
564 | } | ||
565 | return 1; | ||
566 | } | ||
567 | |||
568 | /* Put a compact stack dump into a buffer. */ | ||
569 | void lj_debug_dumpstack(lua_State *L, SBuf *sb, const char *fmt, int depth) | ||
570 | { | ||
571 | int level = 0, dir = 1, pathstrip = 1; | ||
572 | MSize lastlen = 0; | ||
573 | if (depth < 0) { level = ~depth; depth = dir = -1; } /* Reverse frames. */ | ||
574 | while (level != depth) { /* Loop through all frame. */ | ||
575 | int size; | ||
576 | cTValue *frame = lj_debug_frame(L, level, &size); | ||
577 | if (frame) { | ||
578 | cTValue *nextframe = size ? frame+size : NULL; | ||
579 | GCfunc *fn = frame_func(frame); | ||
580 | const uint8_t *p = (const uint8_t *)fmt; | ||
581 | int c; | ||
582 | while ((c = *p++)) { | ||
583 | switch (c) { | ||
584 | case 'p': /* Preserve full path. */ | ||
585 | pathstrip = 0; | ||
586 | break; | ||
587 | case 'F': case 'f': { /* Dump function name. */ | ||
588 | const char *name; | ||
589 | const char *what = lj_debug_funcname(L, frame, &name); | ||
590 | if (what) { | ||
591 | if (c == 'F' && isluafunc(fn)) { /* Dump module:name for 'F'. */ | ||
592 | GCproto *pt = funcproto(fn); | ||
593 | if (pt->firstline != ~(BCLine)0) { /* Not a bytecode builtin. */ | ||
594 | debug_putchunkname(sb, pt, pathstrip); | ||
595 | lj_buf_putb(sb, ':'); | ||
596 | } | ||
597 | } | ||
598 | lj_buf_putmem(sb, name, (MSize)strlen(name)); | ||
599 | break; | ||
600 | } /* else: can't derive a name, dump module:line. */ | ||
601 | } | ||
602 | /* fallthrough */ | ||
603 | case 'l': /* Dump module:line. */ | ||
604 | if (isluafunc(fn)) { | ||
605 | GCproto *pt = funcproto(fn); | ||
606 | if (debug_putchunkname(sb, pt, pathstrip)) { | ||
607 | /* Regular Lua function. */ | ||
608 | BCLine line = c == 'l' ? debug_frameline(L, fn, nextframe) : | ||
609 | pt->firstline; | ||
610 | lj_buf_putb(sb, ':'); | ||
611 | lj_strfmt_putint(sb, line >= 0 ? line : pt->firstline); | ||
612 | } | ||
613 | } else if (isffunc(fn)) { /* Dump numbered builtins. */ | ||
614 | lj_buf_putmem(sb, "[builtin#", 9); | ||
615 | lj_strfmt_putint(sb, fn->c.ffid); | ||
616 | lj_buf_putb(sb, ']'); | ||
617 | } else { /* Dump C function address. */ | ||
618 | lj_buf_putb(sb, '@'); | ||
619 | lj_strfmt_putptr(sb, fn->c.f); | ||
620 | } | ||
621 | break; | ||
622 | case 'Z': /* Zap trailing separator. */ | ||
623 | lastlen = sbuflen(sb); | ||
624 | break; | ||
625 | default: | ||
626 | lj_buf_putb(sb, c); | ||
627 | break; | ||
628 | } | ||
629 | } | ||
630 | } else if (dir == 1) { | ||
631 | break; | ||
632 | } else { | ||
633 | level -= size; /* Reverse frame order: quickly skip missing level. */ | ||
634 | } | ||
635 | level += dir; | ||
636 | } | ||
637 | if (lastlen) | ||
638 | setsbufP(sb, sbufB(sb) + lastlen); /* Zap trailing separator. */ | ||
639 | } | ||
640 | #endif | ||
641 | |||
537 | /* Number of frames for the leading and trailing part of a traceback. */ | 642 | /* Number of frames for the leading and trailing part of a traceback. */ |
538 | #define TRACEBACK_LEVELS1 12 | 643 | #define TRACEBACK_LEVELS1 12 |
539 | #define TRACEBACK_LEVELS2 10 | 644 | #define TRACEBACK_LEVELS2 10 |
diff --git a/src/lj_debug.h b/src/lj_debug.h index 4144b47e..d93380ac 100644 --- a/src/lj_debug.h +++ b/src/lj_debug.h | |||
@@ -32,7 +32,7 @@ LJ_FUNC const char *lj_debug_uvname(GCproto *pt, uint32_t idx); | |||
32 | LJ_FUNC const char *lj_debug_uvnamev(cTValue *o, uint32_t idx, TValue **tvp); | 32 | LJ_FUNC const char *lj_debug_uvnamev(cTValue *o, uint32_t idx, TValue **tvp); |
33 | LJ_FUNC const char *lj_debug_slotname(GCproto *pt, const BCIns *pc, | 33 | LJ_FUNC const char *lj_debug_slotname(GCproto *pt, const BCIns *pc, |
34 | BCReg slot, const char **name); | 34 | BCReg slot, const char **name); |
35 | LJ_FUNC const char *lj_debug_funcname(lua_State *L, TValue *frame, | 35 | LJ_FUNC const char *lj_debug_funcname(lua_State *L, cTValue *frame, |
36 | const char **name); | 36 | const char **name); |
37 | LJ_FUNC void lj_debug_shortname(char *out, GCstr *str, BCLine line); | 37 | LJ_FUNC void lj_debug_shortname(char *out, GCstr *str, BCLine line); |
38 | LJ_FUNC void lj_debug_addloc(lua_State *L, const char *msg, | 38 | LJ_FUNC void lj_debug_addloc(lua_State *L, const char *msg, |
@@ -40,6 +40,10 @@ LJ_FUNC void lj_debug_addloc(lua_State *L, const char *msg, | |||
40 | LJ_FUNC void lj_debug_pushloc(lua_State *L, GCproto *pt, BCPos pc); | 40 | LJ_FUNC void lj_debug_pushloc(lua_State *L, GCproto *pt, BCPos pc); |
41 | LJ_FUNC int lj_debug_getinfo(lua_State *L, const char *what, lj_Debug *ar, | 41 | LJ_FUNC int lj_debug_getinfo(lua_State *L, const char *what, lj_Debug *ar, |
42 | int ext); | 42 | int ext); |
43 | #if LJ_HASPROFILE | ||
44 | LJ_FUNC void lj_debug_dumpstack(lua_State *L, SBuf *sb, const char *fmt, | ||
45 | int depth); | ||
46 | #endif | ||
43 | 47 | ||
44 | /* Fixed internal variable names. */ | 48 | /* Fixed internal variable names. */ |
45 | #define VARNAMEDEF(_) \ | 49 | #define VARNAMEDEF(_) \ |
diff --git a/src/lj_dispatch.c b/src/lj_dispatch.c index d76dda41..01bc4239 100644 --- a/src/lj_dispatch.c +++ b/src/lj_dispatch.c | |||
@@ -27,6 +27,9 @@ | |||
27 | #endif | 27 | #endif |
28 | #include "lj_trace.h" | 28 | #include "lj_trace.h" |
29 | #include "lj_dispatch.h" | 29 | #include "lj_dispatch.h" |
30 | #if LJ_HASPROFILE | ||
31 | #include "lj_profile.h" | ||
32 | #endif | ||
30 | #include "lj_vm.h" | 33 | #include "lj_vm.h" |
31 | #include "luajit.h" | 34 | #include "luajit.h" |
32 | 35 | ||
@@ -84,11 +87,12 @@ void lj_dispatch_init_hotcount(global_State *g) | |||
84 | #endif | 87 | #endif |
85 | 88 | ||
86 | /* Internal dispatch mode bits. */ | 89 | /* Internal dispatch mode bits. */ |
87 | #define DISPMODE_JIT 0x01 /* JIT compiler on. */ | 90 | #define DISPMODE_CALL 0x01 /* Override call dispatch. */ |
88 | #define DISPMODE_REC 0x02 /* Recording active. */ | 91 | #define DISPMODE_RET 0x02 /* Override return dispatch. */ |
89 | #define DISPMODE_INS 0x04 /* Override instruction dispatch. */ | 92 | #define DISPMODE_INS 0x04 /* Override instruction dispatch. */ |
90 | #define DISPMODE_CALL 0x08 /* Override call dispatch. */ | 93 | #define DISPMODE_JIT 0x10 /* JIT compiler on. */ |
91 | #define DISPMODE_RET 0x10 /* Override return dispatch. */ | 94 | #define DISPMODE_REC 0x20 /* Recording active. */ |
95 | #define DISPMODE_PROF 0x40 /* Profiling active. */ | ||
92 | 96 | ||
93 | /* Update dispatch table depending on various flags. */ | 97 | /* Update dispatch table depending on various flags. */ |
94 | void lj_dispatch_update(global_State *g) | 98 | void lj_dispatch_update(global_State *g) |
@@ -100,6 +104,9 @@ void lj_dispatch_update(global_State *g) | |||
100 | mode |= G2J(g)->state != LJ_TRACE_IDLE ? | 104 | mode |= G2J(g)->state != LJ_TRACE_IDLE ? |
101 | (DISPMODE_REC|DISPMODE_INS|DISPMODE_CALL) : 0; | 105 | (DISPMODE_REC|DISPMODE_INS|DISPMODE_CALL) : 0; |
102 | #endif | 106 | #endif |
107 | #if LJ_HASPROFILE | ||
108 | mode |= (g->hookmask & HOOK_PROFILE) ? (DISPMODE_PROF|DISPMODE_INS) : 0; | ||
109 | #endif | ||
103 | mode |= (g->hookmask & (LUA_MASKLINE|LUA_MASKCOUNT)) ? DISPMODE_INS : 0; | 110 | mode |= (g->hookmask & (LUA_MASKLINE|LUA_MASKCOUNT)) ? DISPMODE_INS : 0; |
104 | mode |= (g->hookmask & LUA_MASKCALL) ? DISPMODE_CALL : 0; | 111 | mode |= (g->hookmask & LUA_MASKCALL) ? DISPMODE_CALL : 0; |
105 | mode |= (g->hookmask & LUA_MASKRET) ? DISPMODE_RET : 0; | 112 | mode |= (g->hookmask & LUA_MASKRET) ? DISPMODE_RET : 0; |
@@ -128,9 +135,9 @@ void lj_dispatch_update(global_State *g) | |||
128 | disp[GG_LEN_DDISP+BC_LOOP] = f_loop; | 135 | disp[GG_LEN_DDISP+BC_LOOP] = f_loop; |
129 | 136 | ||
130 | /* Set dynamic instruction dispatch. */ | 137 | /* Set dynamic instruction dispatch. */ |
131 | if ((oldmode ^ mode) & (DISPMODE_REC|DISPMODE_INS)) { | 138 | if ((oldmode ^ mode) & (DISPMODE_PROF|DISPMODE_REC|DISPMODE_INS)) { |
132 | /* Need to update the whole table. */ | 139 | /* Need to update the whole table. */ |
133 | if (!(mode & (DISPMODE_REC|DISPMODE_INS))) { /* No ins dispatch? */ | 140 | if (!(mode & DISPMODE_INS)) { /* No ins dispatch? */ |
134 | /* Copy static dispatch table to dynamic dispatch table. */ | 141 | /* Copy static dispatch table to dynamic dispatch table. */ |
135 | memcpy(&disp[0], &disp[GG_LEN_DDISP], GG_LEN_SDISP*sizeof(ASMFunction)); | 142 | memcpy(&disp[0], &disp[GG_LEN_DDISP], GG_LEN_SDISP*sizeof(ASMFunction)); |
136 | /* Overwrite with dynamic return dispatch. */ | 143 | /* Overwrite with dynamic return dispatch. */ |
@@ -142,12 +149,13 @@ void lj_dispatch_update(global_State *g) | |||
142 | } | 149 | } |
143 | } else { | 150 | } else { |
144 | /* The recording dispatch also checks for hooks. */ | 151 | /* The recording dispatch also checks for hooks. */ |
145 | ASMFunction f = (mode & DISPMODE_REC) ? lj_vm_record : lj_vm_inshook; | 152 | ASMFunction f = (mode & DISPMODE_PROF) ? lj_vm_profhook : |
153 | (mode & DISPMODE_REC) ? lj_vm_record : lj_vm_inshook; | ||
146 | uint32_t i; | 154 | uint32_t i; |
147 | for (i = 0; i < GG_LEN_SDISP; i++) | 155 | for (i = 0; i < GG_LEN_SDISP; i++) |
148 | disp[i] = f; | 156 | disp[i] = f; |
149 | } | 157 | } |
150 | } else if (!(mode & (DISPMODE_REC|DISPMODE_INS))) { | 158 | } else if (!(mode & DISPMODE_INS)) { |
151 | /* Otherwise set dynamic counting ins. */ | 159 | /* Otherwise set dynamic counting ins. */ |
152 | disp[BC_FORL] = f_forl; | 160 | disp[BC_FORL] = f_forl; |
153 | disp[BC_ITERL] = f_iterl; | 161 | disp[BC_ITERL] = f_iterl; |
@@ -495,3 +503,34 @@ out: | |||
495 | return makeasmfunc(lj_bc_ofs[op]); /* Return static dispatch target. */ | 503 | return makeasmfunc(lj_bc_ofs[op]); /* Return static dispatch target. */ |
496 | } | 504 | } |
497 | 505 | ||
506 | #if LJ_HASPROFILE | ||
507 | /* Profile dispatch. */ | ||
508 | void LJ_FASTCALL lj_dispatch_profile(lua_State *L, const BCIns *pc) | ||
509 | { | ||
510 | ERRNO_SAVE | ||
511 | global_State *g = G(L); | ||
512 | uint8_t mask = g->hookmask; | ||
513 | g->hookmask = (mask & ~HOOK_PROFILE); | ||
514 | lj_dispatch_update(g); | ||
515 | if (!(mask & HOOK_VMEVENT)) { | ||
516 | GCfunc *fn = curr_func(L); | ||
517 | GCproto *pt = funcproto(fn); | ||
518 | void *cf = cframe_raw(L->cframe); | ||
519 | const BCIns *oldpc = cframe_pc(cf); | ||
520 | uint8_t oldh = hook_save(g); | ||
521 | BCReg slots; | ||
522 | hook_vmevent(g); | ||
523 | setcframe_pc(cf, pc); | ||
524 | slots = cur_topslot(pt, pc, cframe_multres_n(cf)); | ||
525 | L->top = L->base + slots; /* Fix top. */ | ||
526 | lj_profile_interpreter(L); | ||
527 | setgcref(g->cur_L, obj2gco(L)); | ||
528 | setcframe_pc(cf, oldpc); | ||
529 | hook_restore(g, oldh); | ||
530 | lj_trace_abort(g); | ||
531 | setvmstate(g, INTERP); | ||
532 | } | ||
533 | ERRNO_RESTORE | ||
534 | } | ||
535 | #endif | ||
536 | |||
diff --git a/src/lj_dispatch.h b/src/lj_dispatch.h index 326297cd..811a0ae4 100644 --- a/src/lj_dispatch.h +++ b/src/lj_dispatch.h | |||
@@ -29,7 +29,7 @@ | |||
29 | _(floor) _(ceil) _(trunc) _(log) _(log10) _(exp) _(sin) _(cos) _(tan) \ | 29 | _(floor) _(ceil) _(trunc) _(log) _(log10) _(exp) _(sin) _(cos) _(tan) \ |
30 | _(asin) _(acos) _(atan) _(sinh) _(cosh) _(tanh) _(frexp) _(modf) _(atan2) \ | 30 | _(asin) _(acos) _(atan) _(sinh) _(cosh) _(tanh) _(frexp) _(modf) _(atan2) \ |
31 | _(pow) _(fmod) _(ldexp) \ | 31 | _(pow) _(fmod) _(ldexp) \ |
32 | _(lj_dispatch_call) _(lj_dispatch_ins) _(lj_err_throw) \ | 32 | _(lj_dispatch_call) _(lj_dispatch_ins) _(lj_dispatch_profile) _(lj_err_throw)\ |
33 | _(lj_ffh_coroutine_wrap_err) _(lj_func_closeuv) _(lj_func_newL_gc) \ | 33 | _(lj_ffh_coroutine_wrap_err) _(lj_func_closeuv) _(lj_func_newL_gc) \ |
34 | _(lj_gc_barrieruv) _(lj_gc_step) _(lj_gc_step_fixtop) _(lj_meta_arith) \ | 34 | _(lj_gc_barrieruv) _(lj_gc_step) _(lj_gc_step_fixtop) _(lj_meta_arith) \ |
35 | _(lj_meta_call) _(lj_meta_cat) _(lj_meta_comp) _(lj_meta_equal) \ | 35 | _(lj_meta_call) _(lj_meta_cat) _(lj_meta_comp) _(lj_meta_equal) \ |
@@ -110,7 +110,9 @@ LJ_FUNC void lj_dispatch_update(global_State *g); | |||
110 | /* Instruction dispatch callback for hooks or when recording. */ | 110 | /* Instruction dispatch callback for hooks or when recording. */ |
111 | LJ_FUNCA void LJ_FASTCALL lj_dispatch_ins(lua_State *L, const BCIns *pc); | 111 | LJ_FUNCA void LJ_FASTCALL lj_dispatch_ins(lua_State *L, const BCIns *pc); |
112 | LJ_FUNCA ASMFunction LJ_FASTCALL lj_dispatch_call(lua_State *L, const BCIns*pc); | 112 | LJ_FUNCA ASMFunction LJ_FASTCALL lj_dispatch_call(lua_State *L, const BCIns*pc); |
113 | LJ_FUNCA void LJ_FASTCALL lj_dispatch_return(lua_State *L, const BCIns *pc); | 113 | #if LJ_HASPROFILE |
114 | LJ_FUNCA void LJ_FASTCALL lj_dispatch_profile(lua_State *L, const BCIns *pc); | ||
115 | #endif | ||
114 | 116 | ||
115 | #if LJ_HASFFI && !defined(_BUILDVM_H) | 117 | #if LJ_HASFFI && !defined(_BUILDVM_H) |
116 | /* Save/restore errno and GetLastError() around hooks, exits and recording. */ | 118 | /* Save/restore errno and GetLastError() around hooks, exits and recording. */ |
diff --git a/src/lj_obj.h b/src/lj_obj.h index 846c290f..b1bbf1c9 100644 --- a/src/lj_obj.h +++ b/src/lj_obj.h | |||
@@ -554,6 +554,7 @@ typedef struct global_State { | |||
554 | #define HOOK_ACTIVE_SHIFT 4 | 554 | #define HOOK_ACTIVE_SHIFT 4 |
555 | #define HOOK_VMEVENT 0x20 | 555 | #define HOOK_VMEVENT 0x20 |
556 | #define HOOK_GC 0x40 | 556 | #define HOOK_GC 0x40 |
557 | #define HOOK_PROFILE 0x80 | ||
557 | #define hook_active(g) ((g)->hookmask & HOOK_ACTIVE) | 558 | #define hook_active(g) ((g)->hookmask & HOOK_ACTIVE) |
558 | #define hook_enter(g) ((g)->hookmask |= HOOK_ACTIVE) | 559 | #define hook_enter(g) ((g)->hookmask |= HOOK_ACTIVE) |
559 | #define hook_entergc(g) ((g)->hookmask |= (HOOK_ACTIVE|HOOK_GC)) | 560 | #define hook_entergc(g) ((g)->hookmask |= (HOOK_ACTIVE|HOOK_GC)) |
diff --git a/src/lj_profile.c b/src/lj_profile.c new file mode 100644 index 00000000..0baad06c --- /dev/null +++ b/src/lj_profile.c | |||
@@ -0,0 +1,274 @@ | |||
1 | /* | ||
2 | ** Low-overhead profiling. | ||
3 | ** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h | ||
4 | */ | ||
5 | |||
6 | #define lj_profile_c | ||
7 | #define LUA_CORE | ||
8 | |||
9 | #include "lj_obj.h" | ||
10 | |||
11 | #if LJ_HASPROFILE | ||
12 | |||
13 | #include "lj_buf.h" | ||
14 | #include "lj_frame.h" | ||
15 | #include "lj_debug.h" | ||
16 | #include "lj_dispatch.h" | ||
17 | #include "lj_profile.h" | ||
18 | |||
19 | #include "luajit.h" | ||
20 | |||
21 | #if LJ_PROFILE_SIGPROF | ||
22 | |||
23 | #include <sys/time.h> | ||
24 | #include <signal.h> | ||
25 | |||
26 | #elif LJ_PROFILE_PTHREAD | ||
27 | |||
28 | #include <pthread.h> | ||
29 | |||
30 | #elif LJ_PROFILE_WTHREAD | ||
31 | |||
32 | #define WIN32_LEAN_AND_MEAN | ||
33 | #include <windows.h> | ||
34 | typedef unsigned int (WINAPI *WMM_TPFUNC)(unsigned int); | ||
35 | |||
36 | #endif | ||
37 | |||
38 | /* Profiler state. */ | ||
39 | typedef struct ProfileState { | ||
40 | global_State *g; /* VM state that started the profiler. */ | ||
41 | luaJIT_profile_callback cb; /* Profiler callback. */ | ||
42 | void *data; /* Profiler callback data. */ | ||
43 | SBuf sb; /* String buffer for stack dumps. */ | ||
44 | int interval; /* Sample interval in milliseconds. */ | ||
45 | int samples; /* Number of samples for next callback. */ | ||
46 | int vmstate; /* VM state when profile timer triggered. */ | ||
47 | #if LJ_PROFILE_SIGPROF | ||
48 | struct sigaction oldsa; /* Previous SIGPROF state. */ | ||
49 | #elif LJ_PROFILE_PTHREAD | ||
50 | pthread_t thread; /* Timer thread. */ | ||
51 | int abort; /* Abort timer thread. */ | ||
52 | #elif LJ_PROFILE_WTHREAD | ||
53 | HINSTANCE wmm; /* WinMM library handle. */ | ||
54 | WMM_TPFUNC wmm_tbp; /* WinMM timeBeginPeriod function. */ | ||
55 | WMM_TPFUNC wmm_tep; /* WinMM timeEndPeriod function. */ | ||
56 | HANDLE thread; /* Timer thread. */ | ||
57 | int abort; /* Abort timer thread. */ | ||
58 | #endif | ||
59 | } ProfileState; | ||
60 | |||
61 | /* Sadly, we have to use a static profiler state. | ||
62 | ** | ||
63 | ** The SIGPROF variant needs a static pointer to the global state, anyway. | ||
64 | ** And it would be hard to extend for multiple threads. You can still use | ||
65 | ** multiple VMs in multiple threads, but only profile one at a time. | ||
66 | */ | ||
67 | static ProfileState profile_state; | ||
68 | |||
69 | /* Default sample interval in milliseconds. */ | ||
70 | #define LJ_PROFILE_INTERVAL_DEFAULT 10 | ||
71 | |||
72 | /* -- Profile callbacks --------------------------------------------------- */ | ||
73 | |||
74 | /* Callback from profile hook (HOOK_PROFILE already cleared). */ | ||
75 | void LJ_FASTCALL lj_profile_interpreter(lua_State *L) | ||
76 | { | ||
77 | ProfileState *ps = &profile_state; | ||
78 | int samples = ps->samples; | ||
79 | ps->samples = 0; | ||
80 | ps->cb(ps->data, L, samples, ps->vmstate); /* Invoke user callback. */ | ||
81 | } | ||
82 | |||
83 | /* Trigger profile hook. Asynchronous call from OS-specific profile timer. */ | ||
84 | static void profile_trigger(ProfileState *ps) | ||
85 | { | ||
86 | global_State *g = ps->g; | ||
87 | uint8_t mask; | ||
88 | ps->samples++; /* Always increment number of samples. */ | ||
89 | mask = g->hookmask; | ||
90 | if (!(mask & HOOK_PROFILE)) { /* Set profile hook, unless already set. */ | ||
91 | int st = g->vmstate; | ||
92 | ps->vmstate = st >= 0 ? 'N' : | ||
93 | st == ~LJ_VMST_INTERP ? 'I' : | ||
94 | st == ~LJ_VMST_C ? 'C' : | ||
95 | st == ~LJ_VMST_GC ? 'G' : 'J'; | ||
96 | g->hookmask = (mask | HOOK_PROFILE); | ||
97 | lj_dispatch_update(g); | ||
98 | } | ||
99 | } | ||
100 | |||
101 | /* -- OS-specific profile timer handling ---------------------------------- */ | ||
102 | |||
103 | #if LJ_PROFILE_SIGPROF | ||
104 | |||
105 | /* SIGPROF handler. */ | ||
106 | static void profile_signal(int sig) | ||
107 | { | ||
108 | UNUSED(sig); | ||
109 | profile_trigger(&profile_state); | ||
110 | } | ||
111 | |||
112 | /* Start profiling timer. */ | ||
113 | static void profile_timer_start(ProfileState *ps) | ||
114 | { | ||
115 | int interval = ps->interval; | ||
116 | struct itimerval tm; | ||
117 | struct sigaction sa; | ||
118 | tm.it_value.tv_sec = tm.it_interval.tv_sec = interval / 1000; | ||
119 | tm.it_value.tv_usec = tm.it_interval.tv_usec = (interval % 1000) * 1000; | ||
120 | setitimer(ITIMER_PROF, &tm, NULL); | ||
121 | sa.sa_flags = SA_RESTART; | ||
122 | sa.sa_handler = profile_signal; | ||
123 | sigemptyset(&sa.sa_mask); | ||
124 | sigaction(SIGPROF, &sa, &ps->oldsa); | ||
125 | } | ||
126 | |||
127 | /* Stop profiling timer. */ | ||
128 | static void profile_timer_stop(ProfileState *ps) | ||
129 | { | ||
130 | struct itimerval tm; | ||
131 | tm.it_value.tv_sec = tm.it_interval.tv_sec = 0; | ||
132 | tm.it_value.tv_usec = tm.it_interval.tv_usec = 0; | ||
133 | setitimer(ITIMER_PROF, &tm, NULL); | ||
134 | sigaction(SIGPROF, &ps->oldsa, NULL); | ||
135 | } | ||
136 | |||
137 | #elif LJ_PROFILE_PTHREAD | ||
138 | |||
139 | /* POSIX timer thread. */ | ||
140 | static void *profile_thread(ProfileState *ps) | ||
141 | { | ||
142 | int interval = ps->interval; | ||
143 | struct timespec ts; | ||
144 | ts.tv_sec = interval / 1000; | ||
145 | ts.tv_nsec = (interval % 1000) * 1000000; | ||
146 | while (1) { | ||
147 | nanosleep(&ts, NULL); | ||
148 | if (ps->abort) break; | ||
149 | profile_trigger(ps); | ||
150 | } | ||
151 | return NULL; | ||
152 | } | ||
153 | |||
154 | /* Start profiling timer thread. */ | ||
155 | static void profile_timer_start(ProfileState *ps) | ||
156 | { | ||
157 | ps->abort = 0; | ||
158 | pthread_create(&ps->thread, NULL, (void *(*)(void *))profile_thread, ps); | ||
159 | } | ||
160 | |||
161 | /* Stop profiling timer thread. */ | ||
162 | static void profile_timer_stop(ProfileState *ps) | ||
163 | { | ||
164 | ps->abort = 1; | ||
165 | pthread_join(ps->thread, NULL); | ||
166 | } | ||
167 | |||
168 | #elif LJ_PROFILE_WTHREAD | ||
169 | |||
170 | /* Windows timer thread. */ | ||
171 | static DWORD WINAPI profile_thread(void *psx) | ||
172 | { | ||
173 | ProfileState *ps = (ProfileState *)psx; | ||
174 | int interval = ps->interval; | ||
175 | ps->wmm_tbp(1); | ||
176 | while (1) { | ||
177 | Sleep(interval); | ||
178 | if (ps->abort) break; | ||
179 | profile_trigger(ps); | ||
180 | } | ||
181 | ps->wmm_tep(1); | ||
182 | return 0; | ||
183 | } | ||
184 | |||
185 | /* Start profiling timer thread. */ | ||
186 | static void profile_timer_start(ProfileState *ps) | ||
187 | { | ||
188 | if (!ps->wmm) { /* Load WinMM library on-demand. */ | ||
189 | ps->wmm = LoadLibraryA("winmm.dll"); | ||
190 | if (ps->wmm) { | ||
191 | ps->wmm_tbp = (WMM_TPFUNC)GetProcAddress(ps->wmm, "timeBeginPeriod"); | ||
192 | ps->wmm_tep = (WMM_TPFUNC)GetProcAddress(ps->wmm, "timeEndPeriod"); | ||
193 | if (!ps->wmm_tbp || !ps->wmm_tep) { | ||
194 | ps->wmm = NULL; | ||
195 | return; | ||
196 | } | ||
197 | } | ||
198 | } | ||
199 | ps->abort = 0; | ||
200 | ps->thread = CreateThread(NULL, 0, profile_thread, ps, 0, NULL); | ||
201 | } | ||
202 | |||
203 | /* Stop profiling timer thread. */ | ||
204 | static void profile_timer_stop(ProfileState *ps) | ||
205 | { | ||
206 | ps->abort = 1; | ||
207 | WaitForSingleObject(ps->thread, INFINITE); | ||
208 | } | ||
209 | |||
210 | #endif | ||
211 | |||
212 | /* -- Public profiling API ------------------------------------------------ */ | ||
213 | |||
214 | /* Start profiling. */ | ||
215 | LUA_API void luaJIT_profile_start(lua_State *L, const char *mode, | ||
216 | luaJIT_profile_callback cb, void *data) | ||
217 | { | ||
218 | ProfileState *ps = &profile_state; | ||
219 | int interval = LJ_PROFILE_INTERVAL_DEFAULT; | ||
220 | while (*mode) { | ||
221 | switch (*mode++) { | ||
222 | case 'i': | ||
223 | interval = 0; | ||
224 | while (*mode >= '0' && *mode <= '9') | ||
225 | interval = interval * 10 + (*mode++ - '0'); | ||
226 | if (interval <= 0) interval = 1; | ||
227 | break; | ||
228 | default: /* Ignore unknown mode chars. */ | ||
229 | break; | ||
230 | } | ||
231 | } | ||
232 | if (ps->g) { | ||
233 | luaJIT_profile_stop(L); | ||
234 | if (ps->g) return; /* Profiler in use by another VM. */ | ||
235 | } | ||
236 | ps->g = G(L); | ||
237 | ps->interval = interval; | ||
238 | ps->cb = cb; | ||
239 | ps->data = data; | ||
240 | ps->samples = 0; | ||
241 | lj_buf_init(L, &ps->sb); | ||
242 | profile_timer_start(ps); | ||
243 | } | ||
244 | |||
245 | /* Stop profiling. */ | ||
246 | LUA_API void luaJIT_profile_stop(lua_State *L) | ||
247 | { | ||
248 | ProfileState *ps = &profile_state; | ||
249 | global_State *g = ps->g; | ||
250 | if (G(L) == g) { /* Only stop profiler if started by this VM. */ | ||
251 | profile_timer_stop(ps); | ||
252 | g->hookmask &= ~HOOK_PROFILE; | ||
253 | lj_dispatch_update(g); | ||
254 | lj_buf_free(g, &ps->sb); | ||
255 | setmref(ps->sb.b, NULL); | ||
256 | setmref(ps->sb.e, NULL); | ||
257 | ps->g = NULL; | ||
258 | } | ||
259 | } | ||
260 | |||
261 | /* Return a compact stack dump. */ | ||
262 | LUA_API const char *luaJIT_profile_dumpstack(lua_State *L, const char *fmt, | ||
263 | int depth, size_t *len) | ||
264 | { | ||
265 | ProfileState *ps = &profile_state; | ||
266 | SBuf *sb = &ps->sb; | ||
267 | setsbufL(sb, L); | ||
268 | lj_buf_reset(sb); | ||
269 | lj_debug_dumpstack(L, sb, fmt, depth); | ||
270 | *len = (size_t)sbuflen(sb); | ||
271 | return sbufB(sb); | ||
272 | } | ||
273 | |||
274 | #endif | ||
diff --git a/src/lj_profile.h b/src/lj_profile.h new file mode 100644 index 00000000..ad26f2b6 --- /dev/null +++ b/src/lj_profile.h | |||
@@ -0,0 +1,17 @@ | |||
1 | /* | ||
2 | ** Low-overhead profiling. | ||
3 | ** Copyright (C) 2005-2013 Mike Pall. See Copyright Notice in luajit.h | ||
4 | */ | ||
5 | |||
6 | #ifndef _LJ_PROFILE_H | ||
7 | #define _LJ_PROFILE_H | ||
8 | |||
9 | #include "lj_obj.h" | ||
10 | |||
11 | #if LJ_HASPROFILE | ||
12 | |||
13 | LJ_FUNC void LJ_FASTCALL lj_profile_interpreter(lua_State *L); | ||
14 | |||
15 | #endif | ||
16 | |||
17 | #endif | ||
diff --git a/src/lj_state.c b/src/lj_state.c index f11467fe..8fd11b2f 100644 --- a/src/lj_state.c +++ b/src/lj_state.c | |||
@@ -27,6 +27,7 @@ | |||
27 | #include "lj_vm.h" | 27 | #include "lj_vm.h" |
28 | #include "lj_lex.h" | 28 | #include "lj_lex.h" |
29 | #include "lj_alloc.h" | 29 | #include "lj_alloc.h" |
30 | #include "luajit.h" | ||
30 | 31 | ||
31 | /* -- Stack handling ------------------------------------------------------ */ | 32 | /* -- Stack handling ------------------------------------------------------ */ |
32 | 33 | ||
@@ -237,8 +238,11 @@ LUA_API void lua_close(lua_State *L) | |||
237 | { | 238 | { |
238 | global_State *g = G(L); | 239 | global_State *g = G(L); |
239 | int i; | 240 | int i; |
240 | setgcrefnull(g->cur_L); | ||
241 | L = mainthread(g); /* Only the main thread can be closed. */ | 241 | L = mainthread(g); /* Only the main thread can be closed. */ |
242 | #if LJ_HASPROFILE | ||
243 | luaJIT_profile_stop(L); | ||
244 | #endif | ||
245 | setgcrefnull(g->cur_L); | ||
242 | lj_func_closeuv(L, tvref(L->stack)); | 246 | lj_func_closeuv(L, tvref(L->stack)); |
243 | lj_gc_separateudata(g, 1); /* Separate udata which have GC metamethods. */ | 247 | lj_gc_separateudata(g, 1); /* Separate udata which have GC metamethods. */ |
244 | #if LJ_HASJIT | 248 | #if LJ_HASJIT |
diff --git a/src/lj_vm.h b/src/lj_vm.h index 948d63c2..5893d0b2 100644 --- a/src/lj_vm.h +++ b/src/lj_vm.h | |||
@@ -43,6 +43,7 @@ LJ_ASMF void lj_vm_record(void); | |||
43 | LJ_ASMF void lj_vm_inshook(void); | 43 | LJ_ASMF void lj_vm_inshook(void); |
44 | LJ_ASMF void lj_vm_rethook(void); | 44 | LJ_ASMF void lj_vm_rethook(void); |
45 | LJ_ASMF void lj_vm_callhook(void); | 45 | LJ_ASMF void lj_vm_callhook(void); |
46 | LJ_ASMF void lj_vm_profhook(void); | ||
46 | 47 | ||
47 | /* Trace exit handling. */ | 48 | /* Trace exit handling. */ |
48 | LJ_ASMF void lj_vm_exit_handler(void); | 49 | LJ_ASMF void lj_vm_exit_handler(void); |
diff --git a/src/ljamalg.c b/src/ljamalg.c index 7198a09f..29fed380 100644 --- a/src/ljamalg.c +++ b/src/ljamalg.c | |||
@@ -47,6 +47,7 @@ | |||
47 | #include "lj_strscan.c" | 47 | #include "lj_strscan.c" |
48 | #include "lj_strfmt.c" | 48 | #include "lj_strfmt.c" |
49 | #include "lj_api.c" | 49 | #include "lj_api.c" |
50 | #include "lj_profile.c" | ||
50 | #include "lj_lex.c" | 51 | #include "lj_lex.c" |
51 | #include "lj_parse.c" | 52 | #include "lj_parse.c" |
52 | #include "lj_bcread.c" | 53 | #include "lj_bcread.c" |
diff --git a/src/luajit.h b/src/luajit.h index a4c939bf..80530d95 100644 --- a/src/luajit.h +++ b/src/luajit.h | |||
@@ -64,6 +64,15 @@ enum { | |||
64 | /* Control the JIT engine. */ | 64 | /* Control the JIT engine. */ |
65 | LUA_API int luaJIT_setmode(lua_State *L, int idx, int mode); | 65 | LUA_API int luaJIT_setmode(lua_State *L, int idx, int mode); |
66 | 66 | ||
67 | /* Low-overhead profiling API. */ | ||
68 | typedef void (*luaJIT_profile_callback)(void *data, lua_State *L, | ||
69 | int samples, int vmstate); | ||
70 | LUA_API void luaJIT_profile_start(lua_State *L, const char *mode, | ||
71 | luaJIT_profile_callback cb, void *data); | ||
72 | LUA_API void luaJIT_profile_stop(lua_State *L); | ||
73 | LUA_API const char *luaJIT_profile_dumpstack(lua_State *L, const char *fmt, | ||
74 | int depth, size_t *len); | ||
75 | |||
67 | /* Enforce (dynamic) linker error for version mismatches. Call from main. */ | 76 | /* Enforce (dynamic) linker error for version mismatches. Call from main. */ |
68 | LUA_API void LUAJIT_VERSION_SYM(void); | 77 | LUA_API void LUAJIT_VERSION_SYM(void); |
69 | 78 | ||
diff --git a/src/vm_arm.dasc b/src/vm_arm.dasc index dd39052c..0bd929fd 100644 --- a/src/vm_arm.dasc +++ b/src/vm_arm.dasc | |||
@@ -2082,6 +2082,18 @@ static void build_subroutines(BuildCtx *ctx) | |||
2082 | | ldr INS, [PC, #-4] | 2082 | | ldr INS, [PC, #-4] |
2083 | | bx CRET1 | 2083 | | bx CRET1 |
2084 | | | 2084 | | |
2085 | |->vm_profhook: // Dispatch target for profiler hook. | ||
2086 | #if LJ_HASPROFILE | ||
2087 | | mov CARG1, L | ||
2088 | | str BASE, L->base | ||
2089 | | mov CARG2, PC | ||
2090 | | bl extern lj_dispatch_profile // (lua_State *L, const BCIns *pc) | ||
2091 | | // HOOK_PROFILE is off again, so re-dispatch to dynamic instruction. | ||
2092 | | ldr BASE, L->base | ||
2093 | | sub PC, PC, #4 | ||
2094 | | b ->cont_nop | ||
2095 | #endif | ||
2096 | | | ||
2085 | |//----------------------------------------------------------------------- | 2097 | |//----------------------------------------------------------------------- |
2086 | |//-- Trace exit handler ------------------------------------------------- | 2098 | |//-- Trace exit handler ------------------------------------------------- |
2087 | |//----------------------------------------------------------------------- | 2099 | |//----------------------------------------------------------------------- |
diff --git a/src/vm_mips.dasc b/src/vm_mips.dasc index 39d1521d..e7d01dc4 100644 --- a/src/vm_mips.dasc +++ b/src/vm_mips.dasc | |||
@@ -2011,6 +2011,20 @@ static void build_subroutines(BuildCtx *ctx) | |||
2011 | | jr CRET1 | 2011 | | jr CRET1 |
2012 | |. lw INS, -4(PC) | 2012 | |. lw INS, -4(PC) |
2013 | | | 2013 | | |
2014 | |->vm_profhook: // Dispatch target for profiler hook. | ||
2015 | #if LJ_HASPROFILE | ||
2016 | | load_got lj_dispatch_profile | ||
2017 | | sw MULTRES, SAVE_MULTRES | ||
2018 | | move CARG2, PC | ||
2019 | | sw BASE, L->base | ||
2020 | | call_intern lj_dispatch_profile // (lua_State *L, const BCIns *pc) | ||
2021 | |. move CARG1, L | ||
2022 | | // HOOK_PROFILE is off again, so re-dispatch to dynamic instruction. | ||
2023 | | addiu PC, PC, -4 | ||
2024 | | b ->cont_nop | ||
2025 | |. lw BASE, L->base | ||
2026 | #endif | ||
2027 | | | ||
2014 | |//----------------------------------------------------------------------- | 2028 | |//----------------------------------------------------------------------- |
2015 | |//-- Trace exit handler ------------------------------------------------- | 2029 | |//-- Trace exit handler ------------------------------------------------- |
2016 | |//----------------------------------------------------------------------- | 2030 | |//----------------------------------------------------------------------- |
diff --git a/src/vm_ppc.dasc b/src/vm_ppc.dasc index d9186c44..293c5c6f 100644 --- a/src/vm_ppc.dasc +++ b/src/vm_ppc.dasc | |||
@@ -2505,6 +2505,19 @@ static void build_subroutines(BuildCtx *ctx) | |||
2505 | | mtctr CRET1 | 2505 | | mtctr CRET1 |
2506 | | bctr | 2506 | | bctr |
2507 | | | 2507 | | |
2508 | |->vm_profhook: // Dispatch target for profiler hook. | ||
2509 | #if LJ_HASPROFILE | ||
2510 | | mr CARG1, L | ||
2511 | | stw MULTRES, SAVE_MULTRES | ||
2512 | | mr CARG2, PC | ||
2513 | | stp BASE, L->base | ||
2514 | | bl extern lj_dispatch_profile // (lua_State *L, const BCIns *pc) | ||
2515 | | // HOOK_PROFILE is off again, so re-dispatch to dynamic instruction. | ||
2516 | | lp BASE, L->base | ||
2517 | | subi PC, PC, 4 | ||
2518 | | b ->cont_nop | ||
2519 | #endif | ||
2520 | | | ||
2508 | |//----------------------------------------------------------------------- | 2521 | |//----------------------------------------------------------------------- |
2509 | |//-- Trace exit handler ------------------------------------------------- | 2522 | |//-- Trace exit handler ------------------------------------------------- |
2510 | |//----------------------------------------------------------------------- | 2523 | |//----------------------------------------------------------------------- |
diff --git a/src/vm_x86.dasc b/src/vm_x86.dasc index 7fe891b1..eaa99740 100644 --- a/src/vm_x86.dasc +++ b/src/vm_x86.dasc | |||
@@ -2588,7 +2588,7 @@ static void build_subroutines(BuildCtx *ctx) | |||
2588 | | mov FCARG2, PC // Caveat: FCARG2 == BASE | 2588 | | mov FCARG2, PC // Caveat: FCARG2 == BASE |
2589 | | mov FCARG1, L:RB | 2589 | | mov FCARG1, L:RB |
2590 | | // SAVE_PC must hold the _previous_ PC. The callee updates it with PC. | 2590 | | // SAVE_PC must hold the _previous_ PC. The callee updates it with PC. |
2591 | | call extern lj_dispatch_ins@8 // (lua_State *L, BCIns *pc) | 2591 | | call extern lj_dispatch_ins@8 // (lua_State *L, const BCIns *pc) |
2592 | |3: | 2592 | |3: |
2593 | | mov BASE, L:RB->base | 2593 | | mov BASE, L:RB->base |
2594 | |4: | 2594 | |4: |
@@ -2659,6 +2659,19 @@ static void build_subroutines(BuildCtx *ctx) | |||
2659 | | add NARGS:RD, 1 | 2659 | | add NARGS:RD, 1 |
2660 | | jmp RBa | 2660 | | jmp RBa |
2661 | | | 2661 | | |
2662 | |->vm_profhook: // Dispatch target for profiler hook. | ||
2663 | #if LJ_HASPROFILE | ||
2664 | | mov L:RB, SAVE_L | ||
2665 | | mov L:RB->base, BASE | ||
2666 | | mov FCARG2, PC // Caveat: FCARG2 == BASE | ||
2667 | | mov FCARG1, L:RB | ||
2668 | | call extern lj_dispatch_profile@8 // (lua_State *L, const BCIns *pc) | ||
2669 | | mov BASE, L:RB->base | ||
2670 | | // HOOK_PROFILE is off again, so re-dispatch to dynamic instruction. | ||
2671 | | sub PC, 4 | ||
2672 | | jmp ->cont_nop | ||
2673 | #endif | ||
2674 | | | ||
2662 | |//----------------------------------------------------------------------- | 2675 | |//----------------------------------------------------------------------- |
2663 | |//-- Trace exit handler ------------------------------------------------- | 2676 | |//-- Trace exit handler ------------------------------------------------- |
2664 | |//----------------------------------------------------------------------- | 2677 | |//----------------------------------------------------------------------- |