Author: Armin Rigo <ar...@tunes.org> Branch: vmprof-review Changeset: r78745:6ea3a10ad04e Date: 2015-08-02 17:41 +0200 http://bitbucket.org/pypy/pypy/changeset/6ea3a10ad04e/
Log: Enable(), disable() diff --git a/pypy/interpreter/baseobjspace.py b/pypy/interpreter/baseobjspace.py --- a/pypy/interpreter/baseobjspace.py +++ b/pypy/interpreter/baseobjspace.py @@ -11,7 +11,7 @@ INT_MIN, INT_MAX, UINT_MAX, USHRT_MAX from pypy.interpreter.executioncontext import (ExecutionContext, ActionFlag, - UserDelAction, CodeUniqueIds) + UserDelAction) from pypy.interpreter.error import OperationError, new_exception_class, oefmt from pypy.interpreter.argument import Arguments from pypy.interpreter.miscutils import ThreadLocals, make_weak_value_dictionary @@ -391,7 +391,6 @@ self.actionflag = ActionFlag() # changed by the signal module self.check_signal_action = None # changed by the signal module self.user_del_action = UserDelAction(self) - self.code_unique_ids = CodeUniqueIds() self._code_of_sys_exc_info = None # can be overridden to a subclass @@ -670,16 +669,6 @@ assert ec is not None return ec - def register_code_callback(self, callback): - cui = self.code_unique_ids - cui.code_callback = callback - - def register_code_object(self, pycode): - cui = self.code_unique_ids - if cui.code_callback is None: - return - cui.code_callback(self, pycode) - def _freeze_(self): return True diff --git a/rpython/rlib/rvmprof/cintf.py b/rpython/rlib/rvmprof/cintf.py --- a/rpython/rlib/rvmprof/cintf.py +++ b/rpython/rlib/rvmprof/cintf.py @@ -28,14 +28,13 @@ vmprof_init = rffi.llexternal("rpython_vmprof_init", [], rffi.CCHARP, compilation_info=eci) -## vmprof_enable = rffi.llexternal("vmprof_enable", -## [rffi.INT, rffi.LONG, rffi.INT, -## rffi.CCHARP, rffi.INT], -## rffi.INT, compilation_info=eci, -## save_err=rffi.RFFI_SAVE_ERRNO) -## vmprof_disable = rffi.llexternal("vmprof_disable", [], rffi.INT, -## compilation_info=eci, -## save_err=rffi.RFFI_SAVE_ERRNO) +vmprof_enable = rffi.llexternal("rpython_vmprof_enable", + [rffi.INT, rffi.LONG], + rffi.INT, compilation_info=eci, + save_err=rffi.RFFI_SAVE_ERRNO) +vmprof_disable = rffi.llexternal("rpython_vmprof_disable", [], rffi.INT, + compilation_info=eci, + save_err=rffi.RFFI_SAVE_ERRNO) ## vmprof_register_virtual_function = rffi.llexternal( ## "vmprof_register_virtual_function", @@ -47,9 +46,6 @@ compilation_info=eci) -def vmprof_enable(fileno, interval_usec): return 0 - - def token2lltype(tok): if tok == 'i': return lltype.Signed diff --git a/rpython/rlib/rvmprof/rvmprof.py b/rpython/rlib/rvmprof/rvmprof.py --- a/rpython/rlib/rvmprof/rvmprof.py +++ b/rpython/rlib/rvmprof/rvmprof.py @@ -84,18 +84,17 @@ def enable(self, fileno, interval): """Enable vmprof. Writes go to the given 'fileno'. The sampling interval is given by 'interval' as a number of - seconds, as a float which must be not greater than 1.0. + seconds, as a float which must be smaller than 1.0. Raises VMProfError if something goes wrong. """ assert fileno >= 0 if self.is_enabled: raise VMProfError("vmprof is already enabled") - if not (1e-6 <= interval <= 1.0): + if not (1e-6 <= interval < 1.0): raise VMProfError("bad value for 'interval'") interval_usec = int(interval * 1000000.0) # self.fileno = fileno - self.is_enabled = True self._write_header(interval_usec) if not self.ever_enabled: if we_are_translated(): @@ -109,6 +108,22 @@ res = cintf.vmprof_enable(fileno, interval_usec) if res < 0: raise VMProfError(os.strerror(rposix.get_saved_errno())) + self.is_enabled = True + + def disable(self): + """Disable vmprof. + Raises VMProfError if something goes wrong. + """ + if not self.is_enabled: + raise VMProfError("vmprof is not enabled") + self.is_enabled = False + self._flush_codes() + self.fileno = -1 + if we_are_translated(): + # does not work untranslated + res = cintf.vmprof_disable() + if res < 0: + raise VMProfError(os.strerror(rposix.get_saved_errno())) def _write_code_registration(self, uid, name): b = self._current_codes diff --git a/rpython/rlib/rvmprof/src/rvmprof.c b/rpython/rlib/rvmprof/src/rvmprof.c --- a/rpython/rlib/rvmprof/src/rvmprof.c +++ b/rpython/rlib/rvmprof/src/rvmprof.c @@ -19,6 +19,15 @@ #include "rvmprof_getpc.h" #include "rvmprof_base.h" #include <dlfcn.h> +#include <assert.h> +#include <pthread.h> +#include <sys/time.h> +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> /************************************************************/ @@ -56,7 +65,7 @@ /************************************************************/ -static long volatile ignore_signals = 0; +static long volatile ignore_signals = 1; RPY_EXTERN void rpython_vmprof_ignore_signals(int ignored) @@ -70,3 +79,179 @@ _InterlockedExchange(&ignore_signals, (long)ignored); #endif } + + +/* ************************************************************* + * functions to write a profile file compatible with gperftools + * ************************************************************* + */ + +#define MARKER_STACKTRACE '\x01' +#define MARKER_VIRTUAL_IP '\x02' +#define MARKER_TRAILER '\x03' + +static int profile_file; +static long profile_interval_usec; +static char atfork_hook_installed = 0; + +static int _write_all(const void *buf, size_t bufsize) +{ + while (bufsize > 0) { + ssize_t count = write(profile_file, buf, bufsize); + if (count <= 0) + return -1; /* failed */ + buf += count; + bufsize -= count; + } + return 0; +} + +static void sigprof_handler(int sig_nr, siginfo_t* info, void *ucontext) { + int saved_errno = errno; + /* + void* stack[MAX_STACK_DEPTH]; + stack[0] = GetPC((ucontext_t*)ucontext); + int depth = frame_forcer(get_stack_trace(stack+1, MAX_STACK_DEPTH-1, ucontext)); + depth++; // To account for pc value in stack[0]; + prof_write_stacktrace(stack, depth, 1); + */ + errno = saved_errno; +} + + +/************************************************************/ + +static int install_sigprof_handler(void) +{ + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_sigaction = sigprof_handler; + sa.sa_flags = SA_RESTART | SA_SIGINFO; + if (sigemptyset(&sa.sa_mask) == -1 || + sigaction(SIGPROF, &sa, NULL) == -1) + return -1; + return 0; +} + +static int remove_sigprof_handler(void) +{ + sighandler_t res = signal(SIGPROF, SIG_DFL); + if (res == SIG_ERR) + return -1; + return 0; +} + +static int install_sigprof_timer(void) +{ + static struct itimerval timer; + timer.it_interval.tv_sec = 0; + timer.it_interval.tv_usec = profile_interval_usec; + timer.it_value = timer.it_interval; + if (setitimer(ITIMER_PROF, &timer, NULL) != 0) + return -1; + return 0; +} + +static int remove_sigprof_timer(void) { + static struct itimerval timer; + timer.it_interval.tv_sec = 0; + timer.it_interval.tv_usec = 0; + timer.it_value.tv_sec = 0; + timer.it_value.tv_usec = 0; + if (setitimer(ITIMER_PROF, &timer, NULL) != 0) + return -1; + return 0; +} + +static void atfork_disable_timer(void) { + if (profile_interval_usec > 0) { + remove_sigprof_timer(); + } +} + +static void atfork_enable_timer(void) { + if (profile_interval_usec > 0) { + install_sigprof_timer(); + } +} + +static int install_pthread_atfork_hooks(void) { + /* this is needed to prevent the problems described there: + - http://code.google.com/p/gperftools/issues/detail?id=278 + - http://lists.debian.org/debian-glibc/2010/03/msg00161.html + + TL;DR: if the RSS of the process is large enough, the clone() syscall + will be interrupted by the SIGPROF before it can complete, then + retried, interrupted again and so on, in an endless loop. The + solution is to disable the timer around the fork, and re-enable it + only inside the parent. + */ + if (atfork_hook_installed) + return 0; + int ret = pthread_atfork(atfork_disable_timer, atfork_enable_timer, NULL); + if (ret != 0) + return -1; + atfork_hook_installed = 1; + return 0; +} + +RPY_EXTERN +int rpython_vmprof_enable(int fd, long interval_usec) +{ + assert(fd >= 0); + assert(interval_usec > 0); + profile_file = fd; + profile_interval_usec = interval_usec; + + if (install_pthread_atfork_hooks() == -1) + return -1; + if (install_sigprof_handler() == -1) + return -1; + if (install_sigprof_timer() == -1) + return -1; + rpython_vmprof_ignore_signals(0); + return 0; +} + +RPY_EXTERN +int rpython_vmprof_disable(void) +{ + int srcfd; + char buf[4096]; + ssize_t size; + unsigned char marker = MARKER_TRAILER; + + rpython_vmprof_ignore_signals(1); + profile_interval_usec = 0; + + if (_write_all(&marker, 1) < 0) + return -1; + +#ifdef __linux__ + // copy /proc/PID/maps to the end of the profile file + sprintf(buf, "/proc/%d/maps", getpid()); + srcfd = open(buf, O_RDONLY); + if (srcfd < 0) + return -1; + + while ((size = read(srcfd, buf, sizeof buf)) > 0) { + _write_all(buf, size); + } + close(srcfd); +#else + // freebsd and mac +# error "REVIEW AND FIX ME" + sprintf(buf, "procstat -v %d", getpid()); + src = popen(buf, "r"); + if (!src) { + vmprof_error = "error calling procstat"; + return -1; + } + while ((size = fread(buf, 1, sizeof buf, src))) { + write(profile_file, buf, size); + } + pclose(src); +#endif + + return 0; +} diff --git a/rpython/rlib/rvmprof/src/rvmprof.h b/rpython/rlib/rvmprof/src/rvmprof.h --- a/rpython/rlib/rvmprof/src/rvmprof.h +++ b/rpython/rlib/rvmprof/src/rvmprof.h @@ -1,3 +1,5 @@ RPY_EXTERN char *rpython_vmprof_init(void); RPY_EXTERN void vmprof_ignore_signals(int); +RPY_EXTERN int rpython_vmprof_enable(int, long); +RPY_EXTERN int rpython_vmprof_disable(void); diff --git a/rpython/rlib/rvmprof/test/test_rvmprof.py b/rpython/rlib/rvmprof/test/test_rvmprof.py --- a/rpython/rlib/rvmprof/test/test_rvmprof.py +++ b/rpython/rlib/rvmprof/test/test_rvmprof.py @@ -96,6 +96,8 @@ get_vmprof().enable(fd, 0.5) res = main(code, 5) assert res == 42 + get_vmprof().disable() + os.close(fd) return 0 assert f() == 0 _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit