Skip to content

Commit

Permalink
Merge pull request #134 from bawr/real-time
Browse files Browse the repository at this point in the history
Optional real time profiling support.
  • Loading branch information
planrich committed May 1, 2017
2 parents 2692520 + a53d732 commit ccae9f7
Show file tree
Hide file tree
Showing 8 changed files with 296 additions and 25 deletions.
70 changes: 66 additions & 4 deletions src/_vmprof.c
Original file line number Diff line number Diff line change
Expand Up @@ -269,10 +269,11 @@ static PyObject *enable_vmprof(PyObject* self, PyObject *args)
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;
}

Expand All @@ -291,20 +292,27 @@ static PyObject *enable_vmprof(PyObject* self, PyObject *args)
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) {
Original_code_dealloc = PyCode_Type.tp_dealloc;
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;
}
Expand Down Expand Up @@ -466,18 +474,72 @@ static PyObject * vmp_get_profile_path(PyObject *module, PyObject *noargs) {
}
#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_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 */
};
Expand Down
1 change: 1 addition & 0 deletions src/vmprof.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
82 changes: 77 additions & 5 deletions src/vmprof_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 *))

Expand Down Expand Up @@ -64,7 +129,7 @@ typedef struct prof_stacktrace_s {

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'";
Expand All @@ -74,6 +139,13 @@ char *vmprof_init(int fd, double interval, int memory,
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
Expand All @@ -85,14 +157,14 @@ char *vmprof_init(int fd, double interval, int memory,
}
#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;
Expand All @@ -119,7 +191,7 @@ static int opened_profile(const char *interp_name, int memory, int proflines, in
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
Expand Down

0 comments on commit ccae9f7

Please sign in to comment.