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

Reply via email to