[pypy-commit] pypy vmprof-review: Enable(), disable()
arigo
noreply at buildbot.pypy.org
Sun Aug 2 17:41:43 CEST 2015
Author: Armin Rigo <arigo at 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
More information about the pypy-commit
mailing list