Author: Richard Plangger <[email protected]>
Branch:
Changeset: r91523:cf5d41cbe737
Date: 2017-06-05 07:48 -0400
http://bitbucket.org/pypy/pypy/changeset/cf5d41cbe737/
Log: copy over revision 8426aa942feecfa48d92952654e91788248655b8
(includes several pull request, such as """real time profiling""")
diff --git a/rpython/rlib/rvmprof/src/shared/_vmprof.c
b/rpython/rlib/rvmprof/src/shared/_vmprof.c
--- a/rpython/rlib/rvmprof/src/shared/_vmprof.c
+++ b/rpython/rlib/rvmprof/src/shared/_vmprof.c
@@ -214,6 +214,46 @@
Py_XDECREF(gc_module);
}
+static int emit_all_code_objects_helper(int only_needed, int disable_vmprof)
+{
+ int fd = vmp_profile_fileno();
+
+ if (!is_enabled) {
+ PyErr_SetString(PyExc_ValueError, "vmprof is not enabled");
+ return -1;
+ }
+
+#if VMPROF_UNIX
+ if ((read(fd, NULL, 0) != 0) && (only_needed != 0)) {
+ PyErr_SetString(PyExc_ValueError,
+ "file descriptor must be readable to save only needed
code objects");
+ return -1;
+ }
+#else
+ if (only_needed) {
+ PyErr_SetString(PyExc_ValueError,
+ "saving only needed code objects is not supported for
windows");
+ return -1;
+ }
+#endif
+
+ if (disable_vmprof) {
+ is_enabled = 0;
+ vmprof_ignore_signals(1);
+ }
+
+#if VMPROF_UNIX
+ if (only_needed)
+ emit_all_code_objects_seen(fd);
+ else
+ emit_all_code_objects();
+#else
+ emit_all_code_objects();
+#endif
+
+ return 0;
+}
+
static void cpyprof_code_dealloc(PyObject *co)
{
if (is_enabled) {
@@ -229,10 +269,11 @@
int memory = 0;
int lines = 0;
int native = 0;
+ int real_time = 0;
double interval;
char *p_error;
- if (!PyArg_ParseTuple(args, "id|iii", &fd, &interval, &memory, &lines,
&native)) {
+ if (!PyArg_ParseTuple(args, "id|iiii", &fd, &interval, &memory, &lines,
&native, &real_time)) {
return NULL;
}
@@ -251,6 +292,13 @@
return NULL;
}
+#ifndef VMPROF_UNIX
+ if (real_time) {
+ PyErr_SetString(PyExc_ValueError, "real time profiling is only
supported on Linux and MacOS");
+ return NULL;
+ }
+#endif
+
vmp_profile_lines(lines);
if (!Original_code_dealloc) {
@@ -258,21 +306,20 @@
PyCode_Type.tp_dealloc = &cpyprof_code_dealloc;
}
- p_error = vmprof_init(fd, interval, memory, lines, "cpython", native);
+ p_error = vmprof_init(fd, interval, memory, lines, "cpython", native,
real_time);
if (p_error) {
PyErr_SetString(PyExc_ValueError, p_error);
return NULL;
}
- if (vmprof_enable(memory, native) < 0) {
+ if (vmprof_enable(memory, native, real_time) < 0) {
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
is_enabled = 1;
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
static PyObject * vmp_is_enabled(PyObject *module, PyObject *noargs) {
@@ -285,66 +332,40 @@
static PyObject *
disable_vmprof(PyObject *module, PyObject *args)
{
- int fd = vmp_profile_fileno();
int only_needed = 0;
- if (!PyArg_ParseTuple(args, "|i", &only_needed)) {
+ if (!PyArg_ParseTuple(args, "|i", &only_needed))
return NULL;
- }
-#if VMPROF_UNIX
- if ((read(fd, NULL, 0) != 0) && (only_needed != 0)) {
- PyErr_SetString(PyExc_ValueError,
- "file descriptor must be readable to save only needed
code objects");
+ if (emit_all_code_objects_helper(only_needed, 1))
return NULL;
- }
-#else
- if (only_needed) {
- PyErr_SetString(PyExc_ValueError,
- "saving only needed code objects is not supported for
windows");
- return NULL;
- }
-#endif
-
- if (!is_enabled) {
- PyErr_SetString(PyExc_ValueError, "vmprof is not enabled");
- return NULL;
- }
-
- is_enabled = 0;
- vmprof_ignore_signals(1);
-
-#if VMPROF_UNIX
- if (only_needed)
- emit_all_code_objects_seen(fd);
- else
- emit_all_code_objects();
-#else
- emit_all_code_objects();
-#endif
if (vmprof_disable() < 0) {
PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
+
if (PyErr_Occurred())
return NULL;
- Py_INCREF(Py_None);
- return Py_None;
+
+ Py_RETURN_NONE;
}
static PyObject *
-write_all_code_objects(PyObject *module, PyObject *noargs)
+write_all_code_objects(PyObject *module, PyObject *args)
{
- if (!is_enabled) {
- PyErr_SetString(PyExc_ValueError, "vmprof is not enabled");
+ int only_needed = 0;
+
+ if (!PyArg_ParseTuple(args, "|i", &only_needed))
return NULL;
- }
- emit_all_code_objects();
+
+ if (emit_all_code_objects_helper(only_needed, 0))
+ return NULL;
+
if (PyErr_Occurred())
return NULL;
- Py_INCREF(Py_None);
- return Py_None;
+
+ Py_RETURN_NONE;
}
@@ -378,7 +399,7 @@
vmprof_ignore_signals(0);
return NULL;
}
- entry_count = vmp_walk_and_record_stack(tstate->frame, m,
MAX_STACK_DEPTH-1, skip, 0);
+ entry_count = vmp_walk_and_record_stack(tstate->frame, m,
SINGLE_BUF_SIZE/sizeof(void*)-1, (int)skip, 0);
for (i = 0; i < entry_count; i++) {
routine_ip = m[i];
@@ -387,16 +408,14 @@
free(m);
+ vmprof_ignore_signals(0);
Py_INCREF(list);
+ return list;
+error:
vmprof_ignore_signals(0);
- return list;
-error:
Py_DECREF(list);
- Py_INCREF(Py_None);
-
- vmprof_ignore_signals(0);
- return Py_None;
+ Py_RETURN_NONE;
}
#ifdef VMP_SUPPORTS_NATIVE_PROFILING
@@ -428,13 +447,13 @@
if (o_srcfile == NULL) goto error;
//
return PyTuple_Pack(3, o_name, o_lineno, o_srcfile);
+
error:
Py_XDECREF(o_name);
Py_XDECREF(o_lineno);
Py_XDECREF(o_srcfile);
- Py_INCREF(Py_None);
- return Py_None;
+ Py_RETURN_NONE;
}
#endif
@@ -455,18 +474,72 @@
}
#endif
+
+#ifdef VMPROF_UNIX
+static PyObject *
+insert_real_time_thread(PyObject *module, PyObject * noargs) {
+ ssize_t thread_count;
+
+ if (!is_enabled) {
+ PyErr_SetString(PyExc_ValueError, "vmprof is not enabled");
+ return NULL;
+ }
+
+ if (signal_type != SIGALRM) {
+ PyErr_SetString(PyExc_ValueError, "vmprof is not in real time mode");
+ return NULL;
+ }
+
+ while (__sync_lock_test_and_set(&spinlock, 1)) {
+ }
+
+ thread_count = insert_thread(pthread_self(), -1);
+ __sync_lock_release(&spinlock);
+
+ return PyLong_FromSsize_t(thread_count);
+}
+
+static PyObject *
+remove_real_time_thread(PyObject *module, PyObject * noargs) {
+ ssize_t thread_count;
+
+ if (!is_enabled) {
+ PyErr_SetString(PyExc_ValueError, "vmprof is not enabled");
+ return NULL;
+ }
+
+ if (signal_type != SIGALRM) {
+ PyErr_SetString(PyExc_ValueError, "vmprof is not in real time mode");
+ return NULL;
+ }
+
+ while (__sync_lock_test_and_set(&spinlock, 1)) {
+ }
+
+ thread_count = remove_thread(pthread_self(), -1);
+ __sync_lock_release(&spinlock);
+
+ return PyLong_FromSsize_t(thread_count);
+}
+#endif
+
+
static PyMethodDef VMProfMethods[] = {
{"enable", enable_vmprof, METH_VARARGS, "Enable profiling."},
{"disable", disable_vmprof, METH_VARARGS, "Disable profiling."},
- {"write_all_code_objects", write_all_code_objects, METH_NOARGS,
+ {"write_all_code_objects", write_all_code_objects, METH_VARARGS,
"Write eagerly all the IDs of code objects"},
{"sample_stack_now", sample_stack_now, METH_VARARGS, "Sample the stack
now"},
+ {"is_enabled", vmp_is_enabled, METH_NOARGS, "Indicates if vmprof is
currently sampling."},
#ifdef VMP_SUPPORTS_NATIVE_PROFILING
{"resolve_addr", resolve_addr, METH_VARARGS, "Return the name of the
addr"},
#endif
- {"is_enabled", vmp_is_enabled, METH_NOARGS, "Indicates if vmprof is
currently sampling."},
#ifdef VMPROF_UNIX
{"get_profile_path", vmp_get_profile_path, METH_NOARGS, "Profile path the
profiler logs to."},
+ {"insert_real_time_thread", insert_real_time_thread, METH_NOARGS,
+ "Insert a thread into the real time profiling list."},
+ {"remove_real_time_thread", remove_real_time_thread, METH_NOARGS,
+ "Remove a thread from the real time profiling list."},
#endif
{NULL, NULL, 0, NULL} /* Sentinel */
};
diff --git a/rpython/rlib/rvmprof/src/shared/machine.c
b/rpython/rlib/rvmprof/src/shared/machine.c
--- a/rpython/rlib/rvmprof/src/shared/machine.c
+++ b/rpython/rlib/rvmprof/src/shared/machine.c
@@ -4,7 +4,6 @@
#include <stdio.h>
#ifdef VMPROF_UNIX
-#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#endif
diff --git a/rpython/rlib/rvmprof/src/shared/rss_darwin.h
b/rpython/rlib/rvmprof/src/shared/rss_darwin.h
--- a/rpython/rlib/rvmprof/src/shared/rss_darwin.h
+++ b/rpython/rlib/rvmprof/src/shared/rss_darwin.h
@@ -24,7 +24,7 @@
kern_return_t error = task_info(mach_task, MACH_TASK_BASIC_INFO,
(task_info_t)&taskinfo, &out_count);
if (error == KERN_SUCCESS) {
- return taskinfo.resident_size / 1024;
+ return (long)(taskinfo.resident_size / 1024);
} else {
return -1;
}
diff --git a/rpython/rlib/rvmprof/src/shared/symboltable.c
b/rpython/rlib/rvmprof/src/shared/symboltable.c
--- a/rpython/rlib/rvmprof/src/shared/symboltable.c
+++ b/rpython/rlib/rvmprof/src/shared/symboltable.c
@@ -306,7 +306,7 @@
int _skip_string(int fileno)
{
long chars;
- int count = read(fileno, &chars, sizeof(long));
+ ssize_t count = read(fileno, &chars, sizeof(long));
//LOG("reading string of %d chars\n", chars);
if (count <= 0) {
return 1;
@@ -359,7 +359,7 @@
return 0;
}
-KHASH_MAP_INIT_INT(ptr, intptr_t)
+KHASH_MAP_INIT_INT64(ptr, int)
void vmp_scan_profile(int fileno, int dump_nat_sym, void *all_code_uids)
{
@@ -442,7 +442,7 @@
// if the address has already been dumped,
// do not log it again!
- it = kh_get(ptr, nat_syms, (intptr_t)addr);
+ it = kh_get(ptr, nat_syms, (khint64_t)addr);
if (it == kh_end(nat_syms)) {
char name[MAXLEN];
char srcfile[MAXLEN];
@@ -453,7 +453,7 @@
LOG("dumping add %p, name %s, %s:%d\n",
addr, name, srcfile, lineno);
_dump_native_symbol(fileno, addr, name,
lineno, srcfile);
int ret;
- it = kh_put(ptr, nat_syms, (intptr_t)addr,
&ret);
+ it = kh_put(ptr, nat_syms,
(khint64_t)addr, &ret);
kh_value(nat_syms, it) = 1;
}
}
diff --git a/rpython/rlib/rvmprof/src/shared/vmp_stack.c
b/rpython/rlib/rvmprof/src/shared/vmp_stack.c
--- a/rpython/rlib/rvmprof/src/shared/vmp_stack.c
+++ b/rpython/rlib/rvmprof/src/shared/vmp_stack.c
@@ -15,12 +15,12 @@
#include "compat.h"
#ifdef VMP_SUPPORTS_NATIVE_PROFILING
+
+#ifdef VMPROF_LINUX
#include "unwind/vmprof_unwind.h"
-
typedef mcontext_t unw_context_t;
// functions copied from libunwind using dlopen
-
static int (*unw_get_reg)(unw_cursor_t*, int, unw_word_t*) = NULL;
static int (*unw_step)(unw_cursor_t*) = NULL;
static int (*unw_init_local)(unw_cursor_t *, unw_context_t *) = NULL;
@@ -28,6 +28,9 @@
static int (*unw_get_proc_name)(unw_cursor_t *, char *, size_t, unw_word_t*) =
NULL;
static int (*unw_is_signal_frame)(unw_cursor_t *) = NULL;
static int (*unw_getcontext)(unw_context_t *) = NULL;
+#else
+#include <libunwind.h>
+#endif
#endif
@@ -44,6 +47,19 @@
#include <dlfcn.h>
#endif
+int _per_loop(void) {
+ // how many void* are written to the stack trace per loop iterations?
+#ifdef RPYTHON_VMPROF
+ return 2;
+#else
+ if (vmp_profiles_python_lines()) {
+ return 2;
+ }
+ return 1;
+#endif
+}
+
+
#ifdef PY_TEST
// for testing only!
PY_EVAL_RETURN_T * vmprof_eval(PY_STACK_FRAME_T *f, int throwflag) { return
NULL; }
@@ -130,19 +146,27 @@
int vmp_walk_and_record_python_stack_only(PY_STACK_FRAME_T *frame, void **
result,
int max_depth, int depth, intptr_t
pc)
{
- while (depth < max_depth && frame) {
+ while ((depth + _per_loop()) <= max_depth && frame) {
frame = _write_python_stack_entry(frame, result, &depth, max_depth);
}
return depth;
}
#ifdef VMP_SUPPORTS_NATIVE_PROFILING
-int _write_native_stack(void* addr, void ** result, int depth) {
+int _write_native_stack(void* addr, void ** result, int depth, int max_depth) {
#ifdef RPYTHON_VMPROF
+ if (depth + 2 >= max_depth) {
+ // bail, do not write to unknown memory
+ return depth;
+ }
result[depth++] = (void*)VMPROF_NATIVE_TAG;
#else
if (vmp_profiles_python_lines()) {
- // even if we do not log a python stack frame,
+ if (depth + 2 >= max_depth) {
+ // bail, do not write to unknown memory
+ return depth;
+ }
+ // even if we do not log a python line number,
// we must keep the profile readable
result[depth++] = 0;
}
@@ -176,26 +200,36 @@
// is saved in CPython. _write_python_stack_entry for details.
//
#ifdef VMP_SUPPORTS_NATIVE_PROFILING
- intptr_t func_addr;
+ void * func_addr;
unw_cursor_t cursor;
unw_context_t uc;
unw_proc_info_t pip;
+ int ret;
- if (!vmp_native_enabled()) {
+ if (vmp_native_enabled() == 0) {
return vmp_walk_and_record_python_stack_only(frame, result, max_depth,
0, pc);
}
- unw_getcontext(&uc);
- int ret = unw_init_local(&cursor, &uc);
+ ret = unw_getcontext(&uc);
if (ret < 0) {
// could not initialize lib unwind cursor and context
- return 0;
+ fprintf(stderr, "WARNING: unw_getcontext did not retreive context,
switching to python profiling mode \n");
+ vmp_native_disable();
+ return vmp_walk_and_record_python_stack_only(frame, result, max_depth,
0, pc);
+ }
+ ret = unw_init_local(&cursor, &uc);
+ if (ret < 0) {
+ // could not initialize lib unwind cursor and context
+ fprintf(stderr, "WARNING: unw_init_local did not succeed, switching to
python profiling mode \n");
+ vmp_native_disable();
+ return vmp_walk_and_record_python_stack_only(frame, result, max_depth,
0, pc);
}
if (signal < 0) {
while (signal < 0) {
int err = unw_step(&cursor);
if (err <= 0) {
+ fprintf(stderr, "WARNING: did not find signal frame, skipping
sample\n");
return 0;
}
signal++;
@@ -210,6 +244,7 @@
}
int err = unw_step(&cursor);
if (err <= 0) {
+ fprintf(stderr,"WARNING: did not find signal frame, skipping
sample\n");
return 0;
}
}
@@ -227,10 +262,10 @@
int depth = 0;
PY_STACK_FRAME_T * top_most_frame = frame;
- while (depth < max_depth) {
+ while ((depth + _per_loop()) <= max_depth) {
unw_get_proc_info(&cursor, &pip);
- func_addr = pip.start_ip;
+ func_addr = (void*)pip.start_ip;
//{
// char name[64];
@@ -269,7 +304,7 @@
// this is possible because compiler align to 8 bytes.
//
if (func_addr != 0x0) {
- depth = _write_native_stack((void*)(func_addr | 0x1), result,
depth);
+ depth = _write_native_stack((void*)(((uint64_t)func_addr) |
0x1), result, depth, max_depth);
}
}
@@ -277,14 +312,14 @@
if (err == 0) {
break;
} else if (err < 0) {
- return 0; // this sample is broken, cannot walk it fully
+ // this sample is broken, cannot walk native level... record
python level (at least)
+ return vmp_walk_and_record_python_stack_only(frame, result,
max_depth, 0, pc);
}
}
- return 0; // kill this sample, no python level was found
-#else
+ // if we come here, the found stack trace is removed and only python
stacks are recorded
+#endif
return vmp_walk_and_record_python_stack_only(frame, result, max_depth, 0,
pc);
-#endif
}
int vmp_native_enabled(void) {
@@ -442,7 +477,7 @@
kr = mach_vm_region(task, &addr, &vmsize, VM_REGION_TOP_INFO,
(vm_region_info_t)&topinfo, &count, &obj);
if (kr == KERN_SUCCESS) {
- vm_address_t start = addr, end = addr + vmsize;
+ vm_address_t start = (vm_address_t)addr, end = (vm_address_t)(addr
+ vmsize);
// dladdr now gives the path of the shared object
Dl_info info;
if (dladdr((const void*)start, &info) == 0) {
@@ -484,53 +519,48 @@
#endif
#define U_PREFIX "_U"
#define UL_PREFIX "_UL"
-#else
-#define LIBUNWIND "/usr/lib/system/libunwind.dylib"
-#define PREFIX "unw"
-#define U_PREFIX ""
-#define UL_PREFIX ""
#endif
int vmp_native_enable(void) {
- vmp_native_traces_enabled = 1;
-
+#ifdef VMPROF_LINUX
if (!unw_get_reg) {
- if (!(libhandle = dlopen(LIBUNWIND, RTLD_LAZY | RTLD_LOCAL))) {
+ if ((libhandle = dlopen(LIBUNWIND, RTLD_LAZY | RTLD_LOCAL)) == NULL) {
goto bail_out;
}
- if (!(unw_get_reg = dlsym(libhandle, UL_PREFIX PREFIX "_get_reg"))) {
+ if ((unw_get_reg = dlsym(libhandle, UL_PREFIX PREFIX "_get_reg")) ==
NULL) {
goto bail_out;
}
- if (!(unw_get_proc_info = dlsym(libhandle, UL_PREFIX PREFIX
"_get_proc_info"))){
+ if ((unw_get_proc_info = dlsym(libhandle, UL_PREFIX PREFIX
"_get_proc_info")) == NULL){
goto bail_out;
}
- if (!(unw_get_proc_name = dlsym(libhandle, UL_PREFIX PREFIX
"_get_proc_name"))){
+ if ((unw_get_proc_name = dlsym(libhandle, UL_PREFIX PREFIX
"_get_proc_name")) == NULL){
goto bail_out;
}
- if (!(unw_init_local = dlsym(libhandle, UL_PREFIX PREFIX
"_init_local"))) {
+ if ((unw_init_local = dlsym(libhandle, UL_PREFIX PREFIX
"_init_local")) == NULL) {
goto bail_out;
}
- if (!(unw_step = dlsym(libhandle, UL_PREFIX PREFIX "_step"))) {
+ if ((unw_step = dlsym(libhandle, UL_PREFIX PREFIX "_step")) == NULL) {
goto bail_out;
}
- if (!(unw_is_signal_frame = dlsym(libhandle, UL_PREFIX PREFIX
"_is_signal_frame"))) {
+ if ((unw_is_signal_frame = dlsym(libhandle, UL_PREFIX PREFIX
"_is_signal_frame")) == NULL) {
goto bail_out;
}
- if (!(unw_getcontext = dlsym(libhandle, U_PREFIX PREFIX
"_getcontext"))) {
+ if ((unw_getcontext = dlsym(libhandle, U_PREFIX PREFIX "_getcontext"))
== NULL) {
goto bail_out;
}
}
+#endif
-#if defined(__unix__)
- return vmp_read_vmaps("/proc/self/maps");
-#elif defined(__APPLE__)
- return vmp_read_vmaps(NULL);
-#endif
+ vmp_native_traces_enabled = 1;
+ return 1;
+
+#ifdef VMPROF_LINUX
bail_out:
vmprof_error = dlerror();
fprintf(stderr, "could not load libunwind at runtime. error: %s\n",
vmprof_error);
vmp_native_traces_enabled = 0;
return 0;
+#endif
}
void vmp_native_disable(void) {
@@ -554,7 +584,7 @@
if (vmp_range_count == 0) {
return 0;
}
- int i = vmp_binary_search_ranges(ip, vmp_ranges, vmp_range_count);
+ int i = vmp_binary_search_ranges(ip, vmp_ranges, (int)vmp_range_count);
if (i == -1) {
return 0;
}
@@ -583,9 +613,9 @@
// we found the lower bound
i = l - ol;
if ((i & 1) == 1) {
- return i-1;
+ return (int)i-1;
}
- return i;
+ return (int)i;
}
}
intptr_t * m = l + i;
@@ -599,7 +629,7 @@
}
int vmp_ignore_symbol_count(void) {
- return vmp_range_count;
+ return (int)vmp_range_count;
}
intptr_t * vmp_ignore_symbols(void) {
diff --git a/rpython/rlib/rvmprof/src/shared/vmprof.h
b/rpython/rlib/rvmprof/src/shared/vmprof.h
--- a/rpython/rlib/rvmprof/src/shared/vmprof.h
+++ b/rpython/rlib/rvmprof/src/shared/vmprof.h
@@ -26,6 +26,7 @@
#define PROFILE_LINES '\x02'
#define PROFILE_NATIVE '\x04'
#define PROFILE_RPYTHON '\x08'
+#define PROFILE_REAL_TIME '\x10'
#define DYN_JIT_FLAG 0xbeefbeef
diff --git a/rpython/rlib/rvmprof/src/shared/vmprof_common.h
b/rpython/rlib/rvmprof/src/shared/vmprof_common.h
--- a/rpython/rlib/rvmprof/src/shared/vmprof_common.h
+++ b/rpython/rlib/rvmprof/src/shared/vmprof_common.h
@@ -12,17 +12,82 @@
#include "vmprof_mt.h"
#endif
+#ifdef VMPROF_LINUX
+#include <syscall.h>
+#endif
+
#define MAX_FUNC_NAME 1024
static long prepare_interval_usec = 0;
static long profile_interval_usec = 0;
-static int opened_profile(const char *interp_name, int memory, int proflines,
int native);
+static int opened_profile(const char *interp_name, int memory, int proflines,
int native, int real_time);
#ifdef VMPROF_UNIX
+static int signal_type = SIGPROF;
+static int itimer_type = ITIMER_PROF;
+static pthread_t *threads = NULL;
+static size_t threads_size = 0;
+static size_t thread_count = 0;
+static size_t threads_size_step = 8;
static struct profbuf_s *volatile current_codes;
#endif
+#ifdef VMPROF_UNIX
+
+static inline ssize_t search_thread(pthread_t tid, ssize_t i) {
+ if (i < 0)
+ i = 0;
+ while (i < thread_count) {
+ if (pthread_equal(threads[i], tid))
+ return i;
+ i++;
+ }
+ return -1;
+}
+
+ssize_t insert_thread(pthread_t tid, ssize_t i) {
+ assert(signal_type == SIGALRM);
+ i = search_thread(tid, i);
+ if (i > 0)
+ return -1;
+ if (thread_count == threads_size) {
+ threads_size += threads_size_step;
+ threads = realloc(threads, sizeof(pid_t) * threads_size);
+ assert(threads != NULL);
+ memset(threads + thread_count, 0, sizeof(pid_t) * threads_size_step);
+ }
+ threads[thread_count++] = tid;
+ return thread_count;
+}
+
+ssize_t remove_thread(pthread_t tid, ssize_t i) {
+ assert(signal_type == SIGALRM);
+ if (thread_count == 0)
+ return -1;
+ if (threads == NULL)
+ return -1;
+ i = search_thread(tid, i);
+ if (i < 0)
+ return -1;
+ threads[i] = threads[--thread_count];
+ threads[thread_count] = 0;
+ return thread_count;
+}
+
+ssize_t remove_threads(void) {
+ assert(signal_type == SIGALRM);
+ if (threads != NULL) {
+ free(threads);
+ threads = NULL;
+ }
+ thread_count = 0;
+ threads_size = 0;
+ return 0;
+}
+
+#endif
+
#define MAX_STACK_DEPTH \
((SINGLE_BUF_SIZE - sizeof(struct prof_stacktrace_s)) / sizeof(void *))
@@ -64,7 +129,7 @@
RPY_EXTERN
char *vmprof_init(int fd, double interval, int memory,
- int proflines, const char *interp_name, int native)
+ int proflines, const char *interp_name, int native, int
real_time)
{
if (!(interval >= 1e-6 && interval < 1.0)) { /* also if it is NaN */
return "bad value for 'interval'";
@@ -74,6 +139,13 @@
if (prepare_concurrent_bufs() < 0)
return "out of memory";
#if VMPROF_UNIX
+ if (real_time) {
+ signal_type = SIGALRM;
+ itimer_type = ITIMER_REAL;
+ } else {
+ signal_type = SIGPROF;
+ itimer_type = ITIMER_PROF;
+ }
current_codes = NULL;
assert(fd >= 0);
#else
@@ -85,14 +157,14 @@
}
#endif
vmp_set_profile_fileno(fd);
- if (opened_profile(interp_name, memory, proflines, native) < 0) {
+ if (opened_profile(interp_name, memory, proflines, native, real_time) < 0)
{
vmp_set_profile_fileno(0);
return strerror(errno);
}
return NULL;
}
-static int opened_profile(const char *interp_name, int memory, int proflines,
int native)
+static int opened_profile(const char *interp_name, int memory, int proflines,
int native, int real_time)
{
int success;
int bits;
@@ -119,7 +191,7 @@
header.interp_name[1] = '\x00';
header.interp_name[2] = VERSION_TIMESTAMP;
header.interp_name[3] = memory*PROFILE_MEMORY + proflines*PROFILE_LINES + \
- native*PROFILE_NATIVE;
+ native*PROFILE_NATIVE +
real_time*PROFILE_REAL_TIME;
#ifdef RPYTHON_VMPROF
header.interp_name[3] += PROFILE_RPYTHON;
#endif
diff --git a/rpython/rlib/rvmprof/src/shared/vmprof_main.h
b/rpython/rlib/rvmprof/src/shared/vmprof_main.h
--- a/rpython/rlib/rvmprof/src/shared/vmprof_main.h
+++ b/rpython/rlib/rvmprof/src/shared/vmprof_main.h
@@ -48,11 +48,14 @@
#include "rss_darwin.h"
#endif
+#if VMPROF_LINUX
+#include <syscall.h>
+#endif
/************************************************************/
static void *(*mainloop_get_virtual_ip)(char *) = 0;
-static int opened_profile(const char *interp_name, int memory, int proflines,
int native);
+static int opened_profile(const char *interp_name, int memory, int proflines,
int native, int real_time);
static void flush_codes(void);
/************************************************************/
@@ -94,14 +97,19 @@
{
PY_STACK_FRAME_T * frame;
#ifdef RPYTHON_VMPROF
- // do nothing here,
+ // do nothing here,
frame = (PY_STACK_FRAME_T*)current;
#else
- if (!current) {
+ if (current == NULL) {
+ fprintf(stderr, "WARNING: get_stack_trace, current is NULL\n");
return 0;
}
frame = current->frame;
#endif
+ if (frame == NULL) {
+ fprintf(stderr, "WARNING: get_stack_trace, frame is NULL\n");
+ return 0;
+ }
return vmp_walk_and_record_stack(frame, result, max_depth, 1, pc);
}
@@ -155,11 +163,12 @@
PyThreadState * state;
long mythread_id;
+ mythread_id = PyThread_get_thread_ident();
istate = PyInterpreterState_Head();
if (istate == NULL) {
+ fprintf(stderr, "WARNING: interp state head is null (for thread id
%d)\n", mythread_id);
return NULL;
}
- mythread_id = PyThread_get_thread_ident();
// fish fish fish, it will NOT lock the keymutex in pythread
do {
state = PyInterpreterState_ThreadHead(istate);
@@ -171,16 +180,61 @@
} while ((istate = PyInterpreterState_Next(istate)) != NULL);
// uh? not found?
+ fprintf(stderr, "WARNING: cannot find thread state (for thread id %d),
sample will be thrown away\n", mythread_id);
return NULL;
}
#endif
+#ifdef VMPROF_UNIX
+static int broadcast_signal_for_threads(void)
+{
+ int done = 1;
+ size_t i = 0;
+ pthread_t self = pthread_self();
+ pthread_t tid;
+ while (i < thread_count) {
+ tid = threads[i];
+ if (pthread_equal(tid, self)) {
+ done = 0;
+ } else if (pthread_kill(tid, SIGALRM)) {
+ remove_thread(tid, i);
+ }
+ i++;
+ }
+ return done;
+}
+#endif
+
+#ifdef VMPROF_LINUX
+static inline int is_main_thread(void)
+{
+ pid_t pid = getpid();
+ pid_t tid = (pid_t) syscall(SYS_gettid);
+ return (pid == tid);
+}
+#endif
+
+#ifdef VMPROF_APPLE
+static inline int is_main_thread(void)
+{
+ return pthread_main_np();
+}
+#endif
+
static void sigprof_handler(int sig_nr, siginfo_t* info, void *ucontext)
{
int commit;
PY_THREAD_STATE_T * tstate = NULL;
void (*prevhandler)(int);
+
#ifndef RPYTHON_VMPROF
+
+ // Even though the docs say that this function call is for 'esoteric use'
+ // it seems to be correctly set when the interpreter is teared down!
+ if (!Py_IsInitialized()) {
+ return;
+ }
+
// TERRIBLE HACK AHEAD
// on OS X, the thread local storage is sometimes uninitialized
// when the signal handler runs - it means it's impossible to read errno
@@ -193,6 +247,21 @@
// get_current_thread_state returns a sane result
while (__sync_lock_test_and_set(&spinlock, 1)) {
}
+
+#ifdef VMPROF_UNIX
+ // SIGNAL ABUSE AHEAD
+ // On linux, the prof timer will deliver the signal to the thread which
triggered the timer,
+ // because these timers are based on process and system time, and as such,
are thread-aware.
+ // For the real timer, the signal gets delivered to the main thread,
seemingly always.
+ // Consequently if we want to sample multiple threads, we need to forward
this signal.
+ if (signal_type == SIGALRM) {
+ if (is_main_thread() && broadcast_signal_for_threads()) {
+ __sync_lock_release(&spinlock);
+ return;
+ }
+ }
+#endif
+
prevhandler = signal(SIGSEGV, &segfault_handler);
int fault_code = setjmp(restore_point);
if (fault_code == 0) {
@@ -226,6 +295,7 @@
if (commit) {
commit_buffer(fd, p);
} else {
+ fprintf(stderr, "WARNING: canceled buffer, no stack trace was
written %d\n", is_enabled);
cancel_buffer(p);
}
}
@@ -250,7 +320,7 @@
sa.sa_sigaction = sigprof_handler;
sa.sa_flags = SA_RESTART | SA_SIGINFO;
if (sigemptyset(&sa.sa_mask) == -1 ||
- sigaction(SIGPROF, &sa, NULL) == -1)
+ sigaction(signal_type, &sa, NULL) == -1)
return -1;
return 0;
}
@@ -262,7 +332,7 @@
ign_sigint.sa_flags = 0;
sigemptyset(&ign_sigint.sa_mask);
- if (sigaction(SIGPROF, &ign_sigint, NULL) < 0) {
+ if (sigaction(signal_type, &ign_sigint, NULL) < 0) {
fprintf(stderr, "Could not remove the signal handler (for
profiling)\n");
return -1;
}
@@ -273,9 +343,9 @@
{
static struct itimerval timer;
timer.it_interval.tv_sec = 0;
- timer.it_interval.tv_usec = profile_interval_usec;
+ timer.it_interval.tv_usec = (int)profile_interval_usec;
timer.it_value = timer.it_interval;
- if (setitimer(ITIMER_PROF, &timer, NULL) != 0)
+ if (setitimer(itimer_type, &timer, NULL) != 0)
return -1;
return 0;
}
@@ -284,7 +354,7 @@
static struct itimerval timer;
timerclear(&(timer.it_interval));
timerclear(&(timer.it_value));
- if (setitimer(ITIMER_PROF, &timer, NULL) != 0) {
+ if (setitimer(itimer_type, &timer, NULL) != 0) {
fprintf(stderr, "Could not disable the signal handler (for
profiling)\n");
return -1;
}
@@ -354,7 +424,7 @@
#endif
RPY_EXTERN
-int vmprof_enable(int memory, int native)
+int vmprof_enable(int memory, int native, int real_time)
{
#ifdef VMP_SUPPORTS_NATIVE_PROFILING
init_cpyprof(native);
@@ -364,6 +434,10 @@
profile_interval_usec = prepare_interval_usec;
if (memory && setup_rss() == -1)
goto error;
+#if VMPROF_UNIX
+ if (real_time && insert_thread(pthread_self(), -1) == -1)
+ goto error;
+#endif
if (install_pthread_atfork_hooks() == -1)
goto error;
if (install_sigprof_handler() == -1)
@@ -409,6 +483,10 @@
return -1;
if (remove_sigprof_handler() == -1)
return -1;
+#ifdef VMPROF_UNIX
+ if ((signal_type == SIGALRM) && remove_threads() == -1)
+ return -1;
+#endif
flush_codes();
if (shutdown_concurrent_bufs(vmp_profile_fileno()) < 0)
return -1;
diff --git a/rpython/rlib/rvmprof/src/shared/vmprof_main_win32.h
b/rpython/rlib/rvmprof/src/shared/vmprof_main_win32.h
--- a/rpython/rlib/rvmprof/src/shared/vmprof_main_win32.h
+++ b/rpython/rlib/rvmprof/src/shared/vmprof_main_win32.h
@@ -160,7 +160,7 @@
}
RPY_EXTERN
-int vmprof_enable(int memory, int native)
+int vmprof_enable(int memory, int native, int real_time)
{
if (!thread_started) {
if (!CreateThread(NULL, 0, vmprof_mainloop, NULL, 0, NULL)) {
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit