diff --git a/.gitignore b/.gitignore deleted file mode 100644 index a0dbbae..0000000 --- a/.gitignore +++ /dev/null @@ -1,14 +0,0 @@ -core -MANIFEST -.make.state.Debug -.dep.inc -Makefile -nbproject/ -build/ -dist/ -out.txt -*.pyc -dtrace/dtrace.c -.tox -.idea/ -/dtrace_cython/consumer.c diff --git a/ACKNOWLEDGEMENTS b/ACKNOWLEDGEMENTS deleted file mode 100644 index 86f4514..0000000 --- a/ACKNOWLEDGEMENTS +++ /dev/null @@ -1,15 +0,0 @@ - -Acknowledgements ----------------- - -Thanks to Marc Abramowitz (https://github.com/msabramo) for testing and porting -this to Mac OS X. - -Thanks to Jessica Clarke (https://github.com/jrtc27) for looking into leaking -of the DTrace handles. - -Thanks to Alexander Richardson (https://github.com/arichardson) for doing a -huge refactoring and looking into properly supporting python3. - -Thanks to Konrad Witaszczyk (https://github.com/kwitaszczyk) for looking into -properly freeing up DTrace resources. diff --git a/LICENSE b/LICENSE deleted file mode 100644 index 647a029..0000000 --- a/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (C) 2011-2015 by Thijs Metsch - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index ee4d029..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,6 +0,0 @@ -include setup.py -include LICENSE -include README.md -recursive-include dtrace_cython *.pyx *.pxd *.py *.h -recursive-include examples *.py - diff --git a/README.md b/README.md index 80d0ff7..bbb52b4 100644 --- a/README.md +++ b/README.md @@ -1,106 +1,5 @@ -Python as a DTrace consumer -=========================== -This is a first shot into looking at getting something up and running like -https://github.com/bcantrill/node-libdtrace for Python. +Github pages +============ -**_Note_**: This is far from ready - documentation on writing own DTrace consumers -is very rare :-/ - -The codes in the **examples** folder should give an overview of how to use -Python as a DTrace consumer. - -Currently, this package provides two modules: one wraps libdtrace using ctypes; -The other one uses cython. Should you not have cython installed, the ctypes -wrapper can still be used. - -The Python DTrace consumer can be installed via source here on GitHub, or using -[pypi](http://pypi.python.org/pypi/python-dtrace "python-dtrace on pypi"). - -Cython based wrapper --------------------- - -The Cython wrapper is more sophisticated and generally easier to use. -Initializing the Python based DTrace consumer is as simple as: - - import dtrace - - SCRIPT = 'dtrace:::BEGIN {trace("Hello World");}' - - consumer = dtrace.DTraceConsumer(walk_func=my_walk [...]) - consumer.run(SCRIPT, runtime=3) - -The simple DTraceConsumer can be initialized with self written callbacks which -will allow you to get the output from DTrace, the chewing of the probes as well -as walking the aggregate at the end of the DTrace run. The signatures looks -like this: - - def simple_chew(cpu): - pass - - def simple_chewrec(action): - pass - - def simple_out(value): - pass - - def simple_walk(action, identifier, keys, value): - pass - -Those functions can also be part of a class. Simply add the self parameter as -the first one: - - def simple_chew(self, cpu): - pass - -The DTraceConsumer has a *run_script* function which will run a provided DTrace -script for some time or till it is finished. The time on how long it is run can -be provided as parameter just like the script. During the DTrace chew, chewrec -and the out callbacks are called. When the run is finished the aggregation will -be walked - Thus you can aggregate for 3 seconds and then see the results. - -There also exists a DTraceConsumerThread which can be used to continuously -run DTrace in the background. This might come in handy when you want to -continuously aggregate data for e.g. an GUI. The chew, chewrec, out and walk -callbacks are now called as the snapshot function of DTrace is called. - -The DTraceConsumerThread has a parameter sleep which defaults to 0. This means -that the Thread will wait for DTrace for new aggregation data to arrive. This -has a major drawback since during the wait the Python GIL is acquired and your -program will stop if it needs to wait for DTrace to get new data. Setting the -parameter to 1 (or higher) will let the Thread snapshot the DTrace aggregate -every second instead of waiting for new data. Both usages might make sense - -set the sleep parameter if you know data will arrive sporadically or simply -let it default to 0 if you know data comes in all the time - so nothing will -be blocked. - - thr = DTraceConsumerThread(SCRIPT) - thr.start() - - # we will stop the thread after 5 sec... - time.sleep(5) - - # stop and wait for join... - thr.stop() - thr.join() - -Examples for both ways of handling the Python DTrace consumer can be found in -the *examples* folder. - -Ctypes based wrapper --------------------- - -Both the DTraceConsumer and the DTraceConsumerThread can be initialized with -self written chew, chewrec, buffered out and walk functions just like in the -cython approach. See the examples on how to do this. Also note that the out, -walk, chew and chewrec are currently limited in their functionality and require -you to understand how ctypes work. For examples on how to implement those -functions please review the consumer module in the dtrace_ctypes package. The -functions are named: **simple_chew**, **simple_chewrec**, **simple_walk** and -**simple_buffered_out_writer function**. - -The examples for using this libdtrace wrapper can be found in the -*examples/ctypes* folder. - -(c) 2011-2015 Thijs Metsch -(c) 2013 engjoy UG (haftungsbeschraenkt) +See: http://pages.github.com/ diff --git a/attic/dtrace.py b/attic/dtrace.py deleted file mode 100755 index d52c050..0000000 --- a/attic/dtrace.py +++ /dev/null @@ -1,204 +0,0 @@ -#!/usr/bin/env python2.7 - -""" -Simple Python based DTrace consumers which executes a Hello World D script -using libdtrace and ctypes to access it. -""" -from __future__ import print_function -from ctypes import cdll, CDLL, Structure, c_void_p, c_char_p, c_uint, c_int, \ - c_char, CFUNCTYPE, POINTER, byref, cast -import time - -SCRIPT_COUNT = 'dtrace:::BEGIN {trace("Hello World");} \ - syscall:::entry { @num[execname] = count(); }' - - -class dtrace_aggdesc(Structure): - """ - TODO - use offset - """ - _fields_ = [("dtagd_flags", c_int)] - - -class dtrace_aggdata(Structure): - """ - As defined in dtrace.h:351 - """ - _fields_ = [("dtada_handle", c_void_p), - ("dtada_desc", dtrace_aggdesc), - ("dtada_edesc", c_void_p), - ("dtada_pdesc", c_void_p), - ("dtada_data", c_void_p), - ("dtada_normal", c_void_p), - ("dtada_size", c_int), - ("dtada_delta", c_void_p), - ("dtada_percpu", c_void_p), - ("dtada_percpu_delta", c_void_p)] - - -class dtrace_bufdata(Structure): - """ - As defined in dtrace.h:310 - """ - _fields_ = [("dtbda_handle", c_void_p), - ("dtbda_buffered", c_char_p), # works - ("dtbda_probe", c_void_p), - ("dtbda_recdesc", c_void_p), - ("dtbda_aggdata", c_void_p), - ("dtbda_flags", c_uint)] - - -class dtrace_probedesc(Structure): - """ - As defined in sys/dtrace.h:884 - """ - _fields_ = [("dtrace_id_t", c_uint), - ("dtpd_provider", c_char), - ("dtpd_mod", c_char), - ("dtpd_func", c_char_p), - ("dtpd_name", c_char_p)] - - -class dtrace_probedata(Structure): - """ - As defined in dtrace.h:186 - """ - _fields_ = [("dtpda_handle", c_void_p), - ("dtpda_edesc", c_void_p), - ("dtpda_pdesc", dtrace_probedesc), - ("dtpda_cpu", c_int), # works - ("dtpda_data", c_void_p), - ("dtpda_flow", c_void_p), - ("dtpda_prefix", c_void_p), - ("dtpda_indent", c_int)] - - -class dtrace_recdesc(Structure): - """ - As defined in sys/dtrace.h:931 - """ - pass - - -def deref(addr, typ): - """ - Deref a pointer. - """ - return cast(addr, POINTER(typ)).contents - -CHEW_FUNC = CFUNCTYPE(c_int, - POINTER(dtrace_probedata), - POINTER(c_void_p)) -CHEWREC_FUNC = CFUNCTYPE(c_int, - POINTER(dtrace_probedata), - POINTER(dtrace_recdesc), - POINTER(c_void_p)) -BUFFERED_FUNC = CFUNCTYPE(c_int, - POINTER(dtrace_bufdata), - POINTER(c_void_p)) -WALK_FUNC = CFUNCTYPE(c_int, - POINTER(dtrace_aggdata), - POINTER(c_void_p)) - -cdll.LoadLibrary("libdtrace.so") - -LIBRARY = CDLL("libdtrace.so") - - -def chew_func(data, arg): - """ - Callback for chew. - """ - print('+--> In chew: cpu :', c_int(data.contents.dtpda_cpu).value) - return 0 - - -def chewrec_func(data, rec, arg): - """ - Callback for record chewing. - """ - if rec is None: - return 1 - return 0 - - -def buffered_stdout_writer(bufdata, arg): - """ - In case dtrace_work is given None as filename - this one is called. - """ - tmp = c_char_p(bufdata.contents.dtbda_buffered).value.strip() - print(' +--> In buffered_stdout_writer: ', tmp) - return 0 - - -def walk(data, arg): - """ - Aggregate walker. - """ - - # TODO: pickup the 16 and 272 from offset in desc... - - tmp = data.contents.dtada_data - name = cast(tmp + 16, c_char_p).value - instance = deref(tmp + 272, c_int).value - - print('+--> walking', name, instance) - - return 0 - - -def run_dtrace(): - """ - Go for it... - """ - # get dtrace handle - handle = LIBRARY.dtrace_open(3, 0, byref(c_int(0))) - - # options - if LIBRARY.dtrace_setopt(handle, "bufsize", "4m") != 0: - txt = LIBRARY.dtrace_errmsg(handle, LIBRARY.dtrace_errno(handle)) - raise Exception(c_char_p(txt).value) - if LIBRARY.dtrace_setopt(handle, "aggsize", "4m") != 0: - txt = LIBRARY.dtrace_errmsg(handle, LIBRARY.dtrace_errno(handle)) - raise Exception(c_char_p(txt).value) - - # callbacks - buf_func = BUFFERED_FUNC(buffered_stdout_writer) - LIBRARY.dtrace_handle_buffered(handle, buf_func, None) - - # compile - prg = LIBRARY.dtrace_program_strcompile(handle, - SCRIPT_COUNT, 3, 4, 0, None) - - # run - LIBRARY.dtrace_program_exec(handle, prg, None) - LIBRARY.dtrace_go(handle) - - # aggregate data for a few sec... - i = 0 - chew = CHEW_FUNC(chew_func) - chew_rec = CHEWREC_FUNC(chewrec_func) - while i < 2: - LIBRARY.dtrace_sleep(handle) - LIBRARY.dtrace_work(handle, None, chew, chew_rec, None) - - time.sleep(1) - i += 1 - - LIBRARY.dtrace_stop(handle) - - walk_func = WALK_FUNC(walk) - # sorting instead of dtrace_aggregate_walk - if LIBRARY.dtrace_aggregate_walk_valsorted(handle, walk_func, None) != 0: - txt = LIBRARY.dtrace_errmsg(handle, LIBRARY.dtrace_errno(handle)) - raise Exception(c_char_p(txt).value) - - # Get errors if any... - txt = LIBRARY.dtrace_errmsg(handle, LIBRARY.dtrace_errno(handle)) - print(c_char_p(txt).value) - - # Last: close handle! - LIBRARY.dtrace_close(handle) - -if __name__ == '__main__': - run_dtrace() diff --git a/attic/main.c b/attic/main.c deleted file mode 100644 index b571d6a..0000000 --- a/attic/main.c +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Simple test to see how libdtrace works - will be used to create the Python - * code later on... - */ - -#include -#include -#include -#include - -static int -chew(const dtrace_probedata_t *data, void *arg) -{ - dtrace_probedesc_t *probedesc = data->dtpda_pdesc; - processorid_t cpu = data->dtpda_cpu; - fprintf(stdout, "+--> in chew: %2d %2d %10s %10s", cpu, probedesc->dtpd_id, probedesc->dtpd_provider, probedesc->dtpd_name); - return (DTRACE_CONSUME_THIS); -} - -static int -chewrec(const dtrace_probedata_t *data, const dtrace_recdesc_t *rec, void *arg) -{ - if (rec == NULL) { - fprintf(stdout, "\n"); - return (DTRACE_CONSUME_NEXT); - } - return (DTRACE_CONSUME_THIS); -} - -static int -buffered(const dtrace_bufdata_t *bufdata, void *arg) -{ - fprintf(stdout, " +--> In buffered: %s", bufdata->dtbda_buffered); - return (DTRACE_HANDLE_OK); -} - -/* - * DTrace aggregate walker use this instead of chew, chewrec and buffered (which just output printf)... - */ -static int -walk(const dtrace_aggdata_t *data, void *arg) -{ - dtrace_aggdesc_t *aggdesc = data->dtada_desc; - dtrace_recdesc_t *nrec, *irec; - char *name; - int32_t *instance; - static const dtrace_aggdata_t *count; - - if (count == NULL) { - count = data; - return (DTRACE_AGGWALK_NEXT); - } - - nrec = &aggdesc->dtagd_rec[1]; - irec = &aggdesc->dtagd_rec[2]; - - name = data->dtada_data + nrec->dtrd_offset; - instance = (int32_t *) (data->dtada_data + irec->dtrd_offset); - - fprintf(stderr, "+--> In walk: %-20s %-10d\n", name, *instance); - - return (DTRACE_AGGWALK_NEXT); -} - -/* - * there we go... - */ -int -main(int argc, char** argv) -{ - int err = 0; - - // Get DTrace handle. - dtrace_hdl_t *handle; - handle = dtrace_open(DTRACE_VERSION, 0, &err); - if (handle == NULL) { - fprintf(stderr, "Unable to get hold of an dtrace handle: %s\n", dtrace_errmsg(NULL, err)); - } - - // set options - if (dtrace_setopt(handle, "bufsize", "4m") != 0) { - fprintf(stderr, "Unable to set bufsize option: %s\n", dtrace_errmsg(NULL, err)); - } - if (dtrace_setopt(handle, "aggsize", "4m") != 0) { - fprintf(stderr, "Unable to set bufsize option: %s\n", dtrace_errmsg(NULL, err)); - } - - // set buffer - if (dtrace_handle_buffered(handle, buffered, NULL) == -1) { - fprintf(stderr, "Unable to add buffered output: %s\n", dtrace_errmsg(NULL, err)); - } - - // compile from string. - dtrace_prog_t *prog; - char *script2 = "dtrace:::BEGIN {trace(\"Hello World\");} syscall:::entry { @num[execname] = count(); }"; - - prog = dtrace_program_strcompile(handle, script2, DTRACE_PROBESPEC_NAME, DTRACE_C_ZDEFS, 0, NULL); - if (prog == NULL) { - fprintf(stderr, "Unable to compile d script: %s\n", dtrace_errmsg(NULL, err)); - } - - // run - dtrace_proginfo_t info; - dtrace_program_exec(handle, prog, &info); - dtrace_go(handle); - - // aggregate for a few secs - int i = 0; - do { - dtrace_sleep(handle); - dtrace_work(handle, NULL, chew, chewrec, NULL); - sleep(1); - i += 1; - } while (i < 10); - dtrace_stop(handle); - - if (dtrace_aggregate_snap(handle) != 0) - printf("failed to add to aggregate\n"); - - // Instead of print -> we'll walk...dtrace_aggregate_print(handle, stdout, NULL); - if (dtrace_aggregate_walk_valsorted(handle, walk, NULL) != 0) { - fprintf(stderr, "Unable to append walker: %s\n", dtrace_errmsg(NULL, err)); - } - - // Print errors if any... - fprintf(stderr, dtrace_errmsg(handle, dtrace_errno(handle))); - - // Close the DTrace handle. - dtrace_close(handle); - - return (EXIT_SUCCESS); -} diff --git a/constraints.txt b/constraints.txt deleted file mode 100644 index 25e9025..0000000 --- a/constraints.txt +++ /dev/null @@ -1 +0,0 @@ -Cython~=3.0a6 diff --git a/dtrace_ctypes/__init__.py b/dtrace_ctypes/__init__.py deleted file mode 100644 index f400fd8..0000000 --- a/dtrace_ctypes/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -""" -Package containing the DTrace consumer based on ctypes. - -Created on Oct 10, 2011 - -@author: tmetsch -""" diff --git a/dtrace_ctypes/consumer.py b/dtrace_ctypes/consumer.py deleted file mode 100644 index d879436..0000000 --- a/dtrace_ctypes/consumer.py +++ /dev/null @@ -1,450 +0,0 @@ -""" -The implementation of the consumer. - -Created on Oct 10, 2011 - -@author: tmetsch -""" -from __future__ import print_function -import platform -import threading -import time -from ctypes import (byref, c_char_p, c_int, c_size_t, c_uint, c_void_p, cast, - cdll, CDLL, CFUNCTYPE, POINTER, Structure) -from ctypes.util import find_library -from threading import Thread - -from dtrace_ctypes.dtrace_structs import (dtrace_aggdata, dtrace_bufdata, - dtrace_hdl_t, dtrace_probedata, - dtrace_prog_t, dtrace_recdesc, - DTRACE_WORKSTATUS_OKAY, - DTRACE_WORKSTATUS_DONE, - DTRACE_WORKSTATUS_ERROR) - -_LIBRARY = cdll.LoadLibrary(find_library("dtrace")) -_IS_MACOS = platform.system().startswith("Darwin") - -# ============================================================================= -# chewing and output walkers -# ============================================================================= - - -CHEW_FUNC = CFUNCTYPE(c_int, - POINTER(dtrace_probedata), - c_void_p) -CHEWREC_FUNC = CFUNCTYPE(c_int, - POINTER(dtrace_probedata), - POINTER(dtrace_recdesc), - c_void_p) -BUFFERED_FUNC = CFUNCTYPE(c_int, - POINTER(dtrace_bufdata), - c_void_p) -WALK_FUNC = CFUNCTYPE(c_int, - POINTER(dtrace_aggdata), - c_void_p) - - -def simple_chew_func(data, _arg): - """ - Callback for chew. - """ - print('CPU :', c_int(data.contents.dtpda_cpu).value) - return 0 - - -def noop_chew_func(_data, _arg): - """ - No-op chew function. - """ - return 0 - - -def simple_chewrec_func(_data, rec, _arg): - """ - Callback for record chewing. - """ - if rec is None: - return 1 - return 0 - - -noop_chewrec_func = simple_chewrec_func - - -def simple_buffered_out_writer(bufdata, _arg): - """ - In case dtrace_work is given None as filename - this one is called. - """ - tmp = c_char_p(bufdata.contents.dtbda_buffered).value.strip() - print('out >', tmp) - return 0 - - -def noop_buffered_out_writer(_bufdata, _arg): - """ - No-op buffered out writer. - """ - return 0 - - -def simple_walk(data, _arg): - """ - Aggregate walker capable of reading a name and one value. - """ - # TODO: pickup the 16 and 272 from offset in desc... - - tmp = data.contents.dtada_data - name = cast(tmp + 16, c_char_p).value - instance = deref(tmp + 272, c_int).value - - print('{0:60s} :{1:10d}'.format(name.decode(), instance)) - - return 0 - - -def noop_walk(_data, _arg): - """ - No-op walker. - """ - return 0 - -# ============================================================================= -# LibDTrace function wrapper class -# ============================================================================= - - -def _get_dtrace_fn(name, restype, argtypes): - tmp = getattr(_LIBRARY, name) - tmp.restype = restype - tmp.argtypes = argtypes - return tmp - - -class LIBRARY: - """ - TODO: add comment. - """ - # Types - dtrace_proginfo_t = c_void_p - dtrace_probespec_t = c_int # actually an enum - dtrace_workstatus_t = c_int # actually an enum - FILE_p = c_void_p # FILE* - # Functions - dtrace_open = _get_dtrace_fn( - "dtrace_open", dtrace_hdl_t, [c_int, c_int, POINTER(c_int)]) - dtrace_close = _get_dtrace_fn( - "dtrace_close", None, [dtrace_hdl_t]) - dtrace_go = _get_dtrace_fn("dtrace_go", c_int, [dtrace_hdl_t]) - dtrace_stop = _get_dtrace_fn("dtrace_stop", c_int, [dtrace_hdl_t]) - dtrace_sleep = _get_dtrace_fn("dtrace_sleep", None, [dtrace_hdl_t]) - dtrace_work = _get_dtrace_fn( - "dtrace_work", dtrace_workstatus_t, - [dtrace_hdl_t, FILE_p, CHEW_FUNC, CHEWREC_FUNC, c_void_p]) - dtrace_errno = _get_dtrace_fn( - "dtrace_errno", c_int, [dtrace_hdl_t]) - dtrace_errmsg = _get_dtrace_fn( - "dtrace_errmsg", c_char_p, [dtrace_hdl_t, c_int]) - dtrace_setopt = _get_dtrace_fn( - "dtrace_setopt", c_int, [dtrace_hdl_t, c_char_p, c_char_p]) - dtrace_handle_buffered = _get_dtrace_fn( - "dtrace_handle_buffered", c_int, - [dtrace_hdl_t, BUFFERED_FUNC, c_void_p]) - dtrace_aggregate_walk = _get_dtrace_fn( - "dtrace_aggregate_walk", c_int, - [dtrace_hdl_t, WALK_FUNC, c_void_p]) - dtrace_aggregate_walk_valsorted = _get_dtrace_fn( - "dtrace_aggregate_walk_valsorted", c_int, - [dtrace_hdl_t, WALK_FUNC, c_void_p]) - dtrace_aggregate_snap = _get_dtrace_fn( - "dtrace_aggregate_snap", c_int, [dtrace_hdl_t]) - dtrace_program_strcompile = _get_dtrace_fn( - "dtrace_program_strcompile", dtrace_prog_t, - [dtrace_hdl_t, c_char_p, dtrace_probespec_t, c_uint, c_int, - POINTER(c_char_p)]) - dtrace_program_exec = _get_dtrace_fn( - "dtrace_program_exec", c_int, - [dtrace_hdl_t, dtrace_prog_t, dtrace_proginfo_t]) - - -# ============================================================================= -# Convenience stuff -# ============================================================================= - - -def deref(addr, typ): - """ - Deref a pointer. - """ - return cast(addr, POINTER(typ)).contents - - -def get_error_msg(handle, err=None): - """ - Get the latest and greatest DTrace error. - """ - if err is None: - err = LIBRARY.dtrace_errno(handle) - txt = LIBRARY.dtrace_errmsg(handle, err) - return c_char_p(txt).value.decode("utf-8") - -# ============================================================================= -# Consumers -# ============================================================================= - - -def _dtrace_open(): - err = c_int(0) - handle = LIBRARY.dtrace_open(3, 0, byref(err)) - if handle is None or handle == 0: - raise Exception('Unable to get a DTrace handle. Error=' + - get_error_msg(handle, err)) - assert isinstance(handle, dtrace_hdl_t) - # set buffer options - if LIBRARY.dtrace_setopt(handle, b'bufsize', b'4m') != 0: - raise Exception(get_error_msg(handle)) - - if LIBRARY.dtrace_setopt(handle, b'aggsize', b'4m') != 0: - raise Exception(get_error_msg(handle)) - return handle - - -class FILE(Structure): - """ - Basic dummy structure. - """ - - -if _IS_MACOS: - # These are needed to work around a broken libdtrace on macOS. - libc_open_memstream = CDLL('libc.dylib').open_memstream - libc_open_memstream.restype = POINTER(FILE) - libc_open_memstream.argtypes = [POINTER(c_void_p), POINTER(c_size_t)] - libc_fclose = CDLL('libc.dylib').fclose - libc_fclose.argtypes = [POINTER(FILE)] - libc_free = CDLL('libc.dylib').free - libc_free.argtypes = [c_void_p] - - -def _get_dtrace_work_fp(): - if _IS_MACOS: - # Note: macOS crashes if we pass NULL for fp (FreeBSD works fine) - # Use open_memstream() as a workaround for this bug. - memstream = c_void_p(None) - size = c_size_t(0) - f_p = libc_open_memstream(byref(memstream), byref(size)) - assert cast(f_p, c_void_p).value != c_void_p(None).value - return f_p, memstream - return None, None - - -def _dtrace_sleep_and_work(consumer): - LIBRARY.dtrace_sleep(consumer.handle) - f_p, memstream = _get_dtrace_work_fp() - status = LIBRARY.dtrace_work(consumer.handle, f_p, consumer.chew, - consumer.chew_rec, None) - if f_p is not None: - assert memstream.value != 0, memstream - libc_fclose(f_p) # buffer is valid after fclose(). - tmp = dtrace_bufdata() - tmp.dtbda_buffered = cast(memstream, c_char_p) - consumer.buf_out(byref(tmp), None) - if status == DTRACE_WORKSTATUS_ERROR: - raise Exception('dtrace_work failed: ', - get_error_msg(consumer.handle)) - return status - - -class DTraceConsumer: - """ - A Pyton based DTrace consumer. - """ - def __init__(self, - chew_func=simple_chew_func, - chew_rec_func=simple_chewrec_func, - walk_func=simple_walk, - out_func=simple_buffered_out_writer): - """ - Constructor. will get the DTrace handle - """ - if chew_func is not None: - self.chew = CHEW_FUNC(chew_func) - else: - self.chew = CHEW_FUNC(noop_chew_func) - - if chew_rec_func is not None: - self.chew_rec = CHEWREC_FUNC(chew_rec_func) - else: - self.chew_rec = CHEWREC_FUNC(noop_chewrec_func) - - if walk_func is not None: - self.walk = WALK_FUNC(walk_func) - else: - self.walk = WALK_FUNC(noop_walk) - - if out_func is not None: - self.buf_out = BUFFERED_FUNC(out_func) - else: - self.buf_out = BUFFERED_FUNC(noop_buffered_out_writer) - - # get dtrace handle - self.handle = _dtrace_open() - - def __del__(self): - """ - Always close the DTrace handle :-) - """ - if hasattr(self, "handle"): # Don't close if __init__ failed. - LIBRARY.dtrace_close(self.handle) - - def run(self, script, runtime=1): - """ - Run a DTrace script for a number of seconds defined by the runtime. - - After the run is complete the aggregate is walked. During execution the - stdout of DTrace is redirected to the chew, chewrec and buffered output - writer. - - script -- The script to run. - runtime -- The time the script should run in second (Default: 1s). - """ - # set simple output callbacks - assert self.handle is not None - if LIBRARY.dtrace_handle_buffered(self.handle, self.buf_out, - None) == -1: - raise Exception('Unable to set the stdout buffered writer.') - - # compile - prg = LIBRARY.dtrace_program_strcompile( - self.handle, c_char_p(script.encode("utf-8")), 3, 4, 0, None) - if prg is None: - raise Exception('Unable to compile the script: ', - get_error_msg(self.handle)) - - # run - if LIBRARY.dtrace_program_exec(self.handle, prg, None) == -1: - raise Exception('Failed to execute: ', - get_error_msg(self.handle)) - if LIBRARY.dtrace_go(self.handle) != 0: - raise Exception('Failed to run_script: ', - get_error_msg(self.handle)) - - # aggregate data for a few sec... - i = 0 - while i < runtime: - status = _dtrace_sleep_and_work(self) - if status == DTRACE_WORKSTATUS_DONE: - break # No more work - assert status == DTRACE_WORKSTATUS_OKAY, status - time.sleep(1) - i += 1 - - LIBRARY.dtrace_stop(self.handle) - - # sorting instead of dtrace_aggregate_walk - - if LIBRARY.dtrace_aggregate_walk_valsorted(self.handle, self.walk, - None) != 0: - raise Exception('Failed to walk the aggregate: ', - get_error_msg(self.handle)) - - -class DTraceConsumerThread(Thread): - """ - Thread class with a stop() method. The thread itself has to check - regularly for the stopped() condition. - """ - - def __init__(self, - script, - chew_func=simple_chew_func, - chew_rec_func=simple_chewrec_func, - walk_func=simple_walk, - out_func=simple_buffered_out_writer): - """ - Constructor. will get the DTrace handle - """ - super(DTraceConsumerThread, self).__init__() - self._stop = threading.Event() - self.script = script - - if chew_func is not None: - self.chew = CHEW_FUNC(chew_func) - else: - self.chew = CHEW_FUNC(noop_chew_func) - - if chew_rec_func is not None: - self.chew_rec = CHEWREC_FUNC(chew_rec_func) - else: - self.chew_rec = CHEWREC_FUNC(noop_chewrec_func) - - if walk_func is not None: - self.walk = WALK_FUNC(walk_func) - else: - self.walk = WALK_FUNC(noop_walk) - - if out_func is not None: - self.buf_out = BUFFERED_FUNC(out_func) - else: - self.buf_out = BUFFERED_FUNC(noop_buffered_out_writer) - - # get dtrace handle - self.handle = _dtrace_open() - - def __del__(self): - """ - Always close the DTrace handle :-) - """ - if hasattr(self, "handle"): # Don't close if __init__ failed. - LIBRARY.dtrace_close(self.handle) - - def run(self): - Thread.run(self) - assert self.handle is not None - # set simple output callbacks - if LIBRARY.dtrace_handle_buffered(self.handle, self.buf_out, - None) == -1: - raise Exception('Unable to set the stdout buffered writer.') - - # compile - prg = LIBRARY.dtrace_program_strcompile( - self.handle, c_char_p(self.script.encode("utf-8")), 3, 4, 0, - None) - if prg is None: - raise Exception('Unable to compile the script: ', - get_error_msg(self.handle)) - - # run - if LIBRARY.dtrace_program_exec(self.handle, prg, None) == -1: - raise Exception('Failed to execute: ', - get_error_msg(self.handle)) - if LIBRARY.dtrace_go(self.handle) != 0: - raise Exception('Failed to run_script: ', - get_error_msg(self.handle)) - - # aggregate data for a few sec... - while not self.stopped(): - status = _dtrace_sleep_and_work(self) - if status == DTRACE_WORKSTATUS_DONE: - break # No more work - assert status == DTRACE_WORKSTATUS_OKAY, status - if LIBRARY.dtrace_aggregate_snap(self.handle) != 0: - raise Exception('Failed to snapshot the aggregate: ', - get_error_msg(self.handle)) - if LIBRARY.dtrace_aggregate_walk(self.handle, self.walk, - None) != 0: - raise Exception('Failed to walk the aggregate: ', - get_error_msg(self.handle)) - - LIBRARY.dtrace_stop(self.handle) - - def stop(self): - """ - Stop DTrace. - """ - self._stop.set() - - def stopped(self): - """ - Used to check the status. - """ - return self._stop.isSet() diff --git a/dtrace_ctypes/dtrace_structs.py b/dtrace_ctypes/dtrace_structs.py deleted file mode 100644 index 0cd144e..0000000 --- a/dtrace_ctypes/dtrace_structs.py +++ /dev/null @@ -1,122 +0,0 @@ -""" -The DTrace data structures. - -Created on Oct 10, 2011 - -@author: tmetsch -""" - -# disabling 'too few public meth' pylint check (R0903) -# disabling 'Invalid name' pylint check (C0103) -# pylint: disable=R0903,C0103 - -from ctypes import (c_int64, c_size_t, c_uint16, c_uint32, c_uint64, POINTER, - Structure, c_int, c_void_p, c_char_p, c_char, c_uint) - - -class dtrace_hdl(Structure): - """ - DTrace handle. - """ - - -class dtrace_prog(Structure): - """ - DTrace prog. - """ - - -dtrace_hdl_t = POINTER(dtrace_hdl) -dtrace_prog_t = POINTER(dtrace_prog) - - -class dtrace_recdesc(Structure): - """ - sys/dtrace.h:931 - """ - _fields_ = [ - ("dtrd_action", c_uint16), # dtrace_actkind_t - ("dtrd_size", c_uint32), - ("dtrd_offset", c_uint32), - ("dtrd_alignment", c_uint16), - ("dtrd_format", c_uint16), - ("dtrd_arg", c_uint64), - ("dtrd_uarg", c_uint64)] - - -class dtrace_aggdesc(Structure): - """ - sys/dtrace.h:950 - """ - _fields_ = [("dtagd_name", c_char_p), - ("dtagd_varid", c_int64), # dtrace_aggvarid_t - ("dtagd_flags", c_int), - ("dtagd_id", c_void_p), # dtrace_aggid_t - ("dtagd_epid", c_void_p), # dtrace_epid_t - ("dtagd_size", c_uint32), - ("dtagd_nrecs", c_int), - ("dtagd_pad", c_uint32), - ("dtagd_rec", dtrace_recdesc), # variable-length - ] - - -class dtrace_probedesc(Structure): - """ - As defined in sys/dtrace.h:884 - """ - _fields_ = [("dtrace_id_t", c_uint), - ("dtpd_provider", c_char), - ("dtpd_mod", c_char), - ("dtpd_func", c_char_p), - ("dtpd_name", c_char_p)] - - -class dtrace_aggdata(Structure): - """ - As defined in dtrace.h:351 - """ - _fields_ = [("dtada_handle", dtrace_hdl_t), - ("dtada_desc", POINTER(dtrace_aggdesc)), - ("dtada_edesc", c_void_p), - ("dtada_pdesc", POINTER(dtrace_probedesc)), - ("dtada_data", c_void_p), - ("dtada_normal", c_uint64), - ("dtada_size", c_size_t), - ("dtada_delta", c_void_p), - ("dtada_percpu", c_void_p), - ("dtada_percpu_delta", c_void_p), - ("dtada_total", c_int64), - ("dtada_minbin", c_uint16), - ("dtada_maxbin", c_uint16), - ("dtada_flags", c_uint32)] - - -class dtrace_probedata(Structure): - """ - As defined in dtrace.h:186 - """ - _fields_ = [("dtpda_handle", dtrace_hdl_t), - ("dtpda_edesc", c_void_p), - ("dtpda_pdesc", POINTER(dtrace_probedesc)), - ("dtpda_cpu", c_int), - ("dtpda_data", c_void_p), - ("dtpda_flow", c_int), # dtrace_flowkind_t - ("dtpda_prefix", c_char_p), - ("dtpda_indent", c_int)] - - -class dtrace_bufdata(Structure): - """ - As defined in dtrace.h:310 - """ - _fields_ = [("dtbda_handle", dtrace_hdl_t), - ("dtbda_buffered", c_char_p), - ("dtbda_probe", POINTER(dtrace_probedata)), - ("dtbda_recdesc", POINTER(dtrace_recdesc)), - ("dtbda_aggdata", POINTER(dtrace_aggdata)), - ("dtbda_flags", c_uint)] - - -DTRACE_WORKSTATUS_ERROR = -1 -DTRACE_WORKSTATUS_OKAY = 0 -DTRACE_WORKSTATUS_DONE = 1 diff --git a/dtrace_cython/__init__.py b/dtrace_cython/__init__.py deleted file mode 100644 index 99a33c0..0000000 --- a/dtrace_cython/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -""" -Package containing the DTrace consumer. - -Created on Oct 10, 2011 - -@author: tmetsch -""" diff --git a/dtrace_cython/consumer.pyx b/dtrace_cython/consumer.pyx deleted file mode 100644 index 0401000..0000000 --- a/dtrace_cython/consumer.pyx +++ /dev/null @@ -1,579 +0,0 @@ -from __future__ import print_function, division -import time -import threading -from threading import Thread - -from dtrace_cython.dtrace_h cimport * -from libc.stdint cimport INT64_MAX, INT64_MIN -from libc.stdio cimport fclose -from libc.stdlib cimport free -from posix.stdio cimport open_memstream - -from cython.operator cimport dereference - -# ---------------------------------------------------------------------------- -# The DTrace callbacks -# ---------------------------------------------------------------------------- - - -cdef int chew(const dtrace_probedata_t * data, void * arg) with gil: - """ - Callback defined by DTrace - will call the Python callback. - - Called once per fired probe... - """ - - tmp = arg - function = tmp[0] - - cpu = data.dtpda_cpu - - function(cpu) - - return 0 - - -cdef int chewrec(const dtrace_probedata_t * data, const dtrace_recdesc_t * rec, - void * arg) with gil: - """ - Callback defined by DTrace - will call the Python callback. - - Called once per action. - """ - - if rec == NULL: - return 0 - - tmp = arg - function = tmp[1] - - action = rec.dtrd_action - function(action) - - return 0 - - -cdef int buf_out(const dtrace_bufdata_t * buf_data, void * arg) with gil: - """ - Callback defined by DTrace - will call the Python callback. - """ - - value = buf_data.dtbda_buffered.strip() - - function = arg - function(value) - - return 0 - - -cdef int walk(const dtrace_aggdata_t * data, void * arg) with gil: - """ - Callback defined by DTrace - will call the Python callback. - """ - - keys = [] - value = None - - desc = data.dtada_desc - iden = desc.dtagd_varid - cdef dtrace_recdesc_t *rec - cdef int64_t *tmp - - cdef dtrace_syminfo_t foo - cdef GElf_Sym sym - - aggrec = &desc.dtagd_rec[desc.dtagd_nrecs - 1] - action = aggrec.dtrd_action - - for i in range(1, desc.dtagd_nrecs - 1): - rec = &desc.dtagd_rec[i] - address = data.dtada_data + rec.dtrd_offset - - # TODO: need to extend this. - if rec.dtrd_size == sizeof(uint32_t): - keys.append((address)[0]) - elif rec.dtrd_size == sizeof(uint64_t): - keys.append(( address)[0]) - elif rec.dtrd_size == 20 * sizeof(uint64_t): - # case stack() has been used --> need to lookup symbols. - for j in range(rec.dtrd_arg): - pc = dereference(address) - ret = dtrace_lookup_by_addr(data.dtada_handle, pc, &sym, &foo) - if ret == 0: - keys.append((foo.dts_object,foo.dts_name)) - address += sizeof(uint64_t) - else: - keys.append(address) - - if action in [DTRACEAGG_SUM, DTRACEAGG_MAX, DTRACEAGG_MIN, - DTRACEAGG_COUNT]: - value = ((data.dtada_data + aggrec.dtrd_offset))[0] - elif action == DTRACEAGG_AVG: - tmp = (data.dtada_data + aggrec.dtrd_offset) - value = tmp[1] // tmp[0] - elif action == DTRACEAGG_QUANTIZE: - tmp = (data.dtada_data + aggrec.dtrd_offset) - ranges = get_quantize_ranges() - quantize = [] - for i in range(0, len(ranges)): - quantize.append((ranges[i], tmp[i])) - - value = quantize - elif action == DTRACEAGG_LQUANTIZE: - tmp = (data.dtada_data + aggrec.dtrd_offset) - quantize = [] - tmp_arg = tmp[0] - - ranges = get_lquantize_ranges(tmp_arg) - levels = (aggrec.dtrd_size // sizeof (uint64_t)) - 1 - for i in range(0, levels): - # i + 1 since tmp[0] is already 'used' - quantize.append((ranges[i], tmp[i + 1])) - - value = quantize - else: - raise Exception('Unsupported DTrace aggregation function!') - - function = arg - function(action, iden, keys, value) - - # DTRACE_AGGWALK_REMOVE - return 5 - -# ---------------------------------------------------------------------------- -# Helper functions for the walk... -# ---------------------------------------------------------------------------- - - -cdef get_quantize_ranges(): - ranges = [] - - for i in range(0, DTRACE_QUANTIZE_NBUCKETS): - if i < DTRACE_QUANTIZE_ZEROBUCKET: - if i > 0: - mini = DTRACE_QUANTIZE_BUCKETVAL(i -1) + 1 - else: - # INT64_MIN - mini = INT64_MIN - maxi = DTRACE_QUANTIZE_BUCKETVAL(i) - elif i == DTRACE_QUANTIZE_ZEROBUCKET: - mini = 0 - maxi = 0 - else: - mini = DTRACE_QUANTIZE_BUCKETVAL(i) - if i < DTRACE_QUANTIZE_NBUCKETS - 1: - maxi = DTRACE_QUANTIZE_BUCKETVAL(i + 1) -1 - else: - # INT64_MAX - maxi = INT64_MAX - ranges.append((mini, maxi)) - - return ranges - - -cdef get_lquantize_ranges(uint64_t arg): - ranges = [] - - base = DTRACE_LQUANTIZE_BASE(arg) - step = DTRACE_LQUANTIZE_STEP(arg) - levels = DTRACE_LQUANTIZE_LEVELS(arg) - - for i in range(0, levels + 2): - if i == 0: - mini = INT64_MIN - else: - mini = base + ((i - 1) * step) - if i > levels: - maxi = INT64_MAX - else: - maxi = base + (i * step) - 1 - ranges.append((mini, maxi)) - - return ranges - -# ---------------------------------------------------------------------------- -# Default Python callbacks -# ---------------------------------------------------------------------------- - - -def simple_chew(cpu): - """ - Simple chew function. - - cpu -- CPU id. - """ - print('Running on CPU:', cpu) - - -def noop_chew(_cpu): - """ - No-op chew function. - """ - pass - - -def simple_chewrec(action): - """ - Simple chewrec callback. - - action -- id of the action which was called. - """ - print('Called action was:', action) - - -def noop_chewrec(_action): - """ - No-op chewrec function. - """ - pass - - -def simple_out(value): - """ - A buffered output handler for all those prints. - - value -- Line by line string of the DTrace output. - """ - print('Value is:', value) - - -def noop_out(_value): - """ - No-op out function. - """ - pass - - -def simple_walk(action, identifier, keys, value): - """ - Simple aggregation walker. - - action -- the aggregation action. - identifier -- the id. - action -- type of action (sum, avg, ...) - keys -- list of keys. - value -- the value. - """ - print(action, identifier, keys, value) - - -def noop_walk(_action, _identifier, _keys, _value): - """ - No-op walker. - """ - pass - -# ---------------------------------------------------------------------------- -# The consumers -# ---------------------------------------------------------------------------- - -cdef int _dtrace_sleep_and_work(dtrace_hdl_t * handle, void * args, out_func): - cdef FILE *fp = NULL - IF UNAME_SYSNAME == "Darwin": - cdef char *memstream - cdef size_t size - # Note: macOS crashes if we pass NULL for fp (FreeBSD works fine) - # As a workaround we use open_memstream to write to memory. - fp = open_memstream(&memstream, &size) - assert fp != NULL - status = dtrace_work(handle, fp, &chew, &chewrec, args) - IF UNAME_SYSNAME == "Darwin": - fclose(fp) - out_func(( memstream).strip()) - free(memstream) - if status == DTRACE_WORKSTATUS_ERROR: - raise Exception('dtrace_work failed: ', - dtrace_errmsg(handle, dtrace_errno(handle))) - return status - - -cdef class DTraceConsumer: - """ - A Python based DTrace consumer. - """ - - cdef dtrace_hdl_t * handle - cdef object out_func - cdef object walk_func - cdef object chew_func - cdef object chewrec_func - - def __init__(self, chew_func=simple_chew, chewrec_func=simple_chewrec, - out_func=simple_out, walk_func=simple_walk): - """ - Constructor. Gets a DTrace handle and sets some options. - Passing None as one of the callback arguments installs a no-op - callback. - """ - self.chew_func = noop_chew if chew_func is None else chew_func - self.chewrec_func = noop_chewrec if chewrec_func is None \ - else chewrec_func - self.out_func = noop_out if out_func is None else out_func - self.walk_func = noop_walk if walk_func is None else walk_func - - cdef int err - if self.handle == NULL: - # ensure we only grab 1 - cython might call init twice, of more. - self.handle = dtrace_open(3, 0, &err) - if self.handle == NULL: - raise Exception(dtrace_errmsg(NULL, err)) - - # set buffer options - if dtrace_setopt(self.handle, 'bufsize', '4m') != 0: - raise Exception(dtrace_errmsg(self.handle, - dtrace_errno(self.handle))) - - if dtrace_setopt(self.handle, 'aggsize', '4m') != 0: - raise Exception(dtrace_errmsg(self.handle, - dtrace_errno(self.handle))) - - def __dealloc__(self): - """ - Release DTrace handle. - """ - if self.handle != NULL: - dtrace_close(self.handle) - self.handle = NULL - - cpdef compile(self, str script): - """ - Compile a DTrace script and return errors if any. - - script -- The script to compile. - """ - cdef dtrace_prog_t * prg - prg = dtrace_program_strcompile(self.handle, script.encode("utf-8"), - DTRACE_PROBESPEC_NAME, 0, 0, NULL) - if prg == NULL: - raise Exception('Unable to compile the script: ', - dtrace_errmsg(self.handle, - dtrace_errno(self.handle))) - - cpdef run(self, str script, runtime=1): - """ - Run a DTrace script for a number of seconds defined by the runtime. - - After the run is complete the aggregate is walked. During execution the - stdout of DTrace is redirected to the chew, chewrec and buffered output - writer. - - script -- The script to run. - runtime -- The time the script should run in second (Default: 1s). - """ - # set simple output callbacks - if dtrace_handle_buffered(self.handle, & buf_out, - self.out_func) == -1: - raise Exception('Unable to set the stdout buffered writer.') - - # compile - cdef dtrace_prog_t * prg - prg = dtrace_program_strcompile(self.handle, script.encode("utf-8"), - DTRACE_PROBESPEC_NAME, 0, 0, NULL) - if prg == NULL: - raise Exception('Unable to compile the script: ', - dtrace_errmsg(self.handle, - dtrace_errno(self.handle))) - - # run - if dtrace_program_exec(self.handle, prg, NULL) == -1: - raise Exception('Failed to execute: ', - dtrace_errmsg(self.handle, - dtrace_errno(self.handle))) - if dtrace_go(self.handle) != 0: - raise Exception('Failed to run_script: ', - dtrace_errmsg(self.handle, - dtrace_errno(self.handle))) - - i = 0 - args = (self.chew_func, self.chewrec_func) - while i < runtime: - dtrace_sleep(self.handle) - status = _dtrace_sleep_and_work(self.handle, args, - self.out_func) - if status == DTRACE_WORKSTATUS_DONE: - break - elif status == DTRACE_WORKSTATUS_ERROR: - raise Exception('dtrace_work failed: ', - dtrace_errmsg(self.handle, - dtrace_errno(self.handle))) - else: - assert status == DTRACE_WORKSTATUS_OKAY, status - time.sleep(1) - i += 1 - - dtrace_stop(self.handle) - - # walk the aggregate - # sorting instead of dtrace_aggregate_walk - if dtrace_aggregate_walk_valsorted(self.handle, & walk, - self.walk_func) != 0: - raise Exception('Failed to walk the aggregate: ', - dtrace_errmsg(self.handle, - dtrace_errno(self.handle))) - - -cdef class DTraceContinuousConsumer: - """ - Continuously consuming DTrace consumer - """ - - cdef dtrace_hdl_t * handle - cdef object out_func - cdef object walk_func - cdef object chew_func - cdef object chewrec_func - cdef object script - - def __init__(self, str script, chew_func=simple_chew, - chewrec_func=simple_chewrec, out_func=simple_out, - walk_func=simple_walk): - """ - Constructor. will get the DTrace handle - """ - self.chew_func = noop_chew if chew_func is None else chew_func - self.chewrec_func = noop_chewrec if chewrec_func is None \ - else chewrec_func - self.out_func = noop_out if out_func is None else out_func - self.walk_func = noop_walk if walk_func is None else walk_func - self.script = script.encode("utf-8") - - cdef int err - if self.handle == NULL: - # ensure we only grab 1 - cython might call init twice, of more. - self.handle = dtrace_open(3, 0, &err) - if self.handle == NULL: - raise Exception(dtrace_errmsg(NULL, err)) - - # set buffer options - if dtrace_setopt(self.handle, 'bufsize', '4m') != 0: - raise Exception(dtrace_errmsg(self.handle, - dtrace_errno(self.handle))) - - if dtrace_setopt(self.handle, 'aggsize', '4m') != 0: - raise Exception(dtrace_errmsg(self.handle, - dtrace_errno(self.handle))) - - def __dealloc__(self): - """ - Release DTrace handle. - """ - if self.handle != NULL: - dtrace_stop(self.handle) - dtrace_close(self.handle) - self.handle = NULL - - cpdef go(self): - """ - Compile DTrace program. - """ - # set simple output callbacks - if dtrace_handle_buffered(self.handle, & buf_out, - self.out_func) == -1: - raise Exception('Unable to set the stdout buffered writer.') - - # compile - cdef dtrace_prog_t * prg - prg = dtrace_program_strcompile(self.handle, self.script, - DTRACE_PROBESPEC_NAME, 0, 0, NULL) - if prg == NULL: - raise Exception('Unable to compile the script: ', - dtrace_errmsg(self.handle, - dtrace_errno(self.handle))) - - # run - if dtrace_program_exec(self.handle, prg, NULL) == -1: - raise Exception('Failed to execute: ', - dtrace_errmsg(self.handle, - dtrace_errno(self.handle))) - if dtrace_go(self.handle) != 0: - raise Exception('Failed to run_script: ', - dtrace_errmsg(self.handle, - dtrace_errno(self.handle))) - - def sleep(self): - """ - Wait for new data to arrive. - - WARN: This method will acquire the Python GIL! - """ - dtrace_sleep(self.handle) - - def snapshot(self): - """ - Snapshot the data and walk the aggregate. - """ - args = (self.chew_func, self.chewrec_func) - status = _dtrace_sleep_and_work(self.handle, args, - self.out_func) - if dtrace_aggregate_snap(self.handle) != 0: - raise Exception('Failed to get the aggregate: ', - dtrace_errmsg(self.handle, - dtrace_errno(self.handle))) - if dtrace_aggregate_walk_valsorted(self.handle, & walk, - self.walk_func) != 0: - raise Exception('Failed to walk aggregate: ', - dtrace_errmsg(self.handle, - dtrace_errno(self.handle))) - - return status - - -class DTraceConsumerThread(Thread): - """ - Helper Thread which can be used to continuously aggregate. - """ - - def __init__(self, script, consume=True, chew_func=simple_chew, - chewrec_func=simple_chewrec, out_func=simple_out, - walk_func=simple_walk, sleep=0): - """ - Initilizes the Thread. - """ - Thread.__init__(self) - self._should_stop = threading.Event() - self.sleep_time = sleep - self.consume = consume - self.consumer = DTraceContinuousConsumer(script, chew_func, - chewrec_func, out_func, - walk_func) - - def __del__(self): - """ - Make sure DTrace stops. - """ - # This is called even if __init__ raises, so we have to check whether - # consumer was initialized. - if hasattr(self, "consumer"): - if not self.consume: - self.consumer.snapshot() - del self.consumer - - def run(self): - """ - Aggregate data... - """ - Thread.run(self) - - self.consumer.go() - while not self.stopped(): - if self.sleep_time == 0: - self.consumer.sleep() - else: - time.sleep(self.sleep_time) - - if self.consume: - status = self.consumer.snapshot() - if status == DTRACE_WORKSTATUS_DONE: - self.stop() - - def stop(self): - """ - Stop DTrace. - """ - self._should_stop.set() - - def stopped(self): - """ - Used to check the status. - """ - return self._should_stop.isSet() diff --git a/dtrace_cython/dtrace_h.pxd b/dtrace_cython/dtrace_h.pxd deleted file mode 100644 index cad75e1..0000000 --- a/dtrace_cython/dtrace_h.pxd +++ /dev/null @@ -1,147 +0,0 @@ - -from libc.stdint cimport uint16_t, int32_t, uint32_t, int64_t, uint64_t -from libc.stdio cimport FILE - -cdef extern from "gelf.h": - # used for symbol lookup while using stack() - - ctypedef struct GElf_Sym: - pass - - -cdef extern from "libelf_workaround.h": - # lib elf workaround :-/ - pass - - -cdef extern from "sys/dtrace.h": - uint64_t DTRACE_QUANTIZE_NBUCKETS - uint64_t DTRACE_QUANTIZE_ZEROBUCKET - - int DTRACEACT_AGGREGATION - int DTRACEAGG_COUNT - int DTRACEAGG_MIN - int DTRACEAGG_MAX - int DTRACEAGG_AVG - int DTRACEAGG_SUM - int DTRACEAGG_STDDEV # (unsupported) - int DTRACEAGG_QUANTIZE - int DTRACEAGG_LQUANTIZE - - ctypedef struct dtrace_recdesc_t: - # Taken from sys/dtrace.h:931 - uint16_t dtrd_action - uint32_t dtrd_offset - uint32_t dtrd_size - uint64_t dtrd_arg - - ctypedef struct dtrace_aggdesc_t: - # Taken from sys/dtrace.h:950 - int dtagd_nrecs - int64_t dtagd_varid - dtrace_recdesc_t dtagd_rec[1] - - cdef uint16_t DTRACE_LQUANTIZE_STEP(uint64_t x) - cdef uint16_t DTRACE_LQUANTIZE_LEVELS(uint64_t x) - cdef int32_t DTRACE_LQUANTIZE_BASE(uint64_t x) - cdef int64_t DTRACE_QUANTIZE_BUCKETVAL(uint64_t buck) - - -cdef extern from "dtrace.h": - - ctypedef enum dtrace_probespec_t: - # Taken from dtrace.h:186 - DTRACE_PROBESPEC_NONE - DTRACE_PROBESPEC_PROVIDER - DTRACE_PROBESPEC_MOD - DTRACE_PROBESPEC_FUNC - DTRACE_PROBESPEC_NAME - - ctypedef enum dtrace_workstatus_t: - # Taken from dtrace.h:247 - DTRACE_WORKSTATUS_ERROR - DTRACE_WORKSTATUS_OKAY - DTRACE_WORKSTATUS_DONE - - ctypedef struct dtrace_hdl_t: - # Taken from dtrace.h:54 - pass - - ctypedef struct dtrace_prog_t: - # Taken from dtrace.h:58 - pass - - ctypedef struct dtrace_proginfo_t: - # Taken from dtrace.h:97 - pass - - ctypedef struct dtrace_probedesc_t: - uint32_t id - char dtpd_provider[64] # DTRACE_PROVNAMELEN - char dtpd_mod[64] # DTRACE_MODNAMELEN - char dtpd_func[192] # DTRACE_FUNCNAMELEN - char dtpd_name[64] # DTRACE_NAMELEN - - ctypedef struct dtrace_probedata_t: - # Taken from dtrace.h:186 - dtrace_hdl_t * dtbda_handle - dtrace_probedesc_t * dtpda_pdesc - int dtpda_cpu - - ctypedef struct dtrace_bufdata_t: - # Taken from dtrace.h:310 - dtrace_hdl_t * dtbda_handle - const char * dtbda_buffered - dtrace_probedata_t * dtbda_probe - - ctypedef struct dtrace_aggdata_t: - # Taken from dtrace.h:351 - dtrace_hdl_t * dtada_handle - dtrace_aggdesc_t * dtada_desc - char * dtada_data - - ctypedef struct dtrace_syminfo_t: - const char * dts_object - const char * dts_name - long dts_id - - # from dtrace.h - ctypedef int dtrace_handle_buffered_f(const dtrace_bufdata_t * buf_data, void * arg) - ctypedef int dtrace_consume_probe_f(const dtrace_probedata_t *, void *) - ctypedef int dtrace_consume_rec_f(const dtrace_probedata_t *, const dtrace_recdesc_t * , void *) - ctypedef int dtrace_aggregate_f(const dtrace_aggdata_t *, void *) - - # open and close handle - dtrace_hdl_t * dtrace_open(int, int, int *) - void dtrace_close(dtrace_hdl_t * handle) - - # options - int dtrace_setopt(dtrace_hdl_t * , char * , char *) - - # output handling - int dtrace_handle_buffered(dtrace_hdl_t * , dtrace_handle_buffered_f * , void *) - int dtrace_consume_probe_f(const dtrace_probedata_t * , void *) - int dtrace_consume_rec_f(const dtrace_probedata_t * , dtrace_recdesc_t * , void *) - int dtrace_aggregate_f(const dtrace_aggdata_t * , void *) - - # compile - dtrace_prog_t * dtrace_program_strcompile(dtrace_hdl_t * , char * , dtrace_probespec_t, int, int, char * const []) - - # running - int dtrace_program_exec(dtrace_hdl_t * , dtrace_prog_t * , dtrace_proginfo_t *) - int dtrace_go(dtrace_hdl_t *) - int dtrace_stop(dtrace_hdl_t *) - void dtrace_sleep(dtrace_hdl_t *) - dtrace_workstatus_t dtrace_work(dtrace_hdl_t * , FILE * , dtrace_consume_probe_f * , dtrace_consume_rec_f * , void *) - - # walking aggregate - int dtrace_aggregate_walk_valsorted(dtrace_hdl_t * , dtrace_aggregate_f * , void *) - int dtrace_aggregate_snap(dtrace_hdl_t *) - int dtrace_aggregate_walk(dtrace_hdl_t *, dtrace_aggregate_f *, void *) - - # Dealing with stack() - int dtrace_lookup_by_addr(dtrace_hdl_t *, uint64_t addr, GElf_Sym *, dtrace_syminfo_t *) - - # error handling... - int dtrace_errno(dtrace_hdl_t * handle) - char * dtrace_errmsg(dtrace_hdl_t * handle, int error) diff --git a/dtrace_cython/libelf_workaround.h b/dtrace_cython/libelf_workaround.h deleted file mode 100644 index 27b12fb..0000000 --- a/dtrace_cython/libelf_workaround.h +++ /dev/null @@ -1,7 +0,0 @@ -/* - * Needed to resolve: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=39019 - */ - -#if defined(_ILP32) && (_FILE_OFFSET_BITS != 32) -#define _FILE_OFFSET_BITS 32 -#endif diff --git a/examples/amqp/README.md b/examples/amqp/README.md deleted file mode 100644 index 5972dd0..0000000 --- a/examples/amqp/README.md +++ /dev/null @@ -1,21 +0,0 @@ -Python DTrace consumer and AMQP -=============================== - -Very simple example to show how you can use the Python DTrace consumer and AMQP. - -The scenario is pretty simple - let's say you want to trace data on one machine -and display it on another. Still the data should be up to date - so basically -whenever a DTrace probe fires the data should be transmitted to the other -hosts. This can be done with AMQP. - -The examples here assume you have a RabbitMQ server running and have pika -installed. - -Start the **receive.py** Python script first. Than start the **send.py** Python -script. You can even start multiple **send.py** scripts on multiple hosts to get -an overall view of the system calls made by processes on all machines :-) - -The **send.py** script counts system calls and will send AMQP messages as new -data arrives. You will see in the output of the **receive.py** script that data -arrives almost instantly. Now you can build life updating visualizations of the -data gathered by DTrace. diff --git a/examples/amqp/receive.py b/examples/amqp/receive.py deleted file mode 100755 index 3104f27..0000000 --- a/examples/amqp/receive.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python - -""" -Receives up to date data from the DTrace consumer. - -Created on Mar 31, 2012 - -@author: tmetsch -""" -from __future__ import print_function -import ast -import pika - -# Connection to the broker. -connection = pika.BlockingConnection(pika.ConnectionParameters( - host='localhost')) -channel = connection.channel() -channel.queue_declare(queue='dtrace') - -# The aggregated data :-) -data = {} - - -def callback(_ch, _method, _properties, body): - """ - Callback which picks up the DTrace messages. - """ - tmp = ast.literal_eval(body) - key = tmp.keys()[0] - val = tmp.values()[0] - if key in data: - data[key] += val - else: - data[key] = val - # Print instead of doing print you could life update a chart...msg will - # come as DTrace fires them - print('Received: ', key, val) - - -if __name__ == '__main__': - channel.basic_consume(on_message_callback=callback, - queue='dtrace', - auto_ack=False) - - try: - channel.start_consuming() - except KeyboardInterrupt: - connection.close() - # You could use Google chart to create a plot aferwards... - # from GChartWrapper import Pie - # G = Pie(data.values()) - # G.label(*data.keys()).size(400,300).title('Syscalls counter chart') - # G.save() diff --git a/examples/amqp/send.py b/examples/amqp/send.py deleted file mode 100755 index ded1dbf..0000000 --- a/examples/amqp/send.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python - -""" -Use the Python DTrace consumer and push the data through AMQP as the DTrace -probes fire. - -Created on Mar 31, 2012 - -@author: tmetsch -""" - -import time - -import pika - -import dtrace - -# pretty num D script counting syscalls... -SCRIPT = 'syscall:::entry { @num[execname] = count(); }' - -# Connection to the broker -connection = pika.BlockingConnection(pika.ConnectionParameters( - host='localhost')) -channel = connection.channel() -channel.queue_declare(queue='dtrace') - - -def walk(_iden, key, value): - """ - Walker which sends on the data to RabbitMQ as the DTrace probes fire. - """ - channel.basic_publish(exchange='', routing_key='dtrace', - body=bytes({key[0]: value})) - - -def run_dtrace(): - """ - Runs a DTrace script for 5 seconds... - """ - thr = dtrace.DTraceConsumerThread(SCRIPT, walk_func=walk) - thr.start() - - # we will stop the thread after some time... - time.sleep(5) - - # stop and wait for join... - thr.stop() - thr.join() - - -if __name__ == '__main__': - run_dtrace() - connection.close() diff --git a/examples/cli.py b/examples/cli.py deleted file mode 100755 index 6adfd43..0000000 --- a/examples/cli.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/usr/bin/env python - -""" -A CLI for Dtrace which acts almost like the normal dtrace command on your -shell. - -Created on Apr 11, 2012 - -@author: tmetsch -""" - -from __future__ import print_function - -import sys - -import dtrace - - -def print_lquantize(values): - """ - Print a lquantize. - """ - # find max - maxi = 0 - for item in values: - if item[1] > maxi: - maxi = item[1] - - for item in values: - if item[0][0] > 0: - print('%10s ' % item[0][0], end=' ') - for _ in range(0, ((40 * int(item[1])) / maxi)): - sys.stdout.write('*') - for _ in range(((40 * int(item[1])) / maxi), 40): - sys.stdout.write(' ') - print(' %5s' % item[1]) - - -def pretty_print(_iden, action, keys, values): - """ - Pretty print aggregation walker. - """ - if action in [1799]: - print(keys, values) - elif action == 1800: - # lquantize - print('\n ', keys[0], '\n') - print('{0:>10s} {1:-^40} {2}'.format('value', ' Distribution ', - 'count')) - print_lquantize(values) - else: - pass - - -def brendan(): - """ - DTrace fans will understand this :-D - """ - print('Tracing... Hit Ctrl-C to end') - - -def run_dtrace(script): - """ - Run DTrace till Ctrl+C is pressed... - """ - thr = dtrace.DTraceConsumerThread(script, False, walk_func=pretty_print) - thr.start() - brendan() - try: - while True: - pass - except (KeyboardInterrupt, SystemExit): - thr.stop() - thr.join() - - -if __name__ == '__main__': - # run_dtrace('dtrace:::BEGIN {trace("Hello World"); exit(0);}') - # run_dtrace('syscall:::entry { @num[pid,execname] = count(); }') - TMP = 'syscall::read:entry { @dist[execname] = lquantize(arg0, 0, 12, 2);}' - run_dtrace(TMP) - # run_dtrace('sysinfo:::readch { @dist[execname] = quantize(arg0); }') diff --git a/examples/ctypes/hello_world.py b/examples/ctypes/hello_world.py deleted file mode 100755 index 4851c2c..0000000 --- a/examples/ctypes/hello_world.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Use the Python DTrace consumer and run a Hello World DTrace script. - -Created on Oct 10, 2011 - -@author: tmetsch -""" - -from dtrace_ctypes import consumer - -SCRIPT = 'dtrace:::BEGIN {trace("Hello World");}' - - -def main(): - """ - Run DTrace... - """ - dtrace = consumer.DTraceConsumer() - dtrace.run(SCRIPT) - - -if __name__ == '__main__': - main() diff --git a/examples/ctypes/read_bytes.py b/examples/ctypes/read_bytes.py deleted file mode 100755 index 6992629..0000000 --- a/examples/ctypes/read_bytes.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python - -""" -Use the Python DTrace consumer and sum the num of bytes read. - -Created on Oct 10, 2011 - -@author: tmetsch -""" -from __future__ import print_function - -from dtrace_ctypes import consumer - -SCRIPT = 'sysinfo:::readch { @bytes[execname] = sum(arg0); }' - - -def main(): - """ - Run DTrace... - """ - print('Hint: if you don\'t get any output try running it with pfexec...') - - dtrace = consumer.DTraceConsumer() - dtrace.run(SCRIPT, 4) - - -if __name__ == '__main__': - main() diff --git a/examples/ctypes/syscall_by_zone.py b/examples/ctypes/syscall_by_zone.py deleted file mode 100755 index 1e2079e..0000000 --- a/examples/ctypes/syscall_by_zone.py +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env python - -""" -Use the Python DTrace consumer and count syscalls by zone. - -Created on Oct 10, 2011 - -@author: tmetsch -""" - -from __future__ import print_function - -import time - -from ctypes import cast, c_char_p, c_int -from dtrace_ctypes import consumer - -SCRIPT = 'syscall:::entry { @num[zonename] = count(); }' - - -def walk(data, _): - """ - Nice formatted aggregate walker. - """ - tmp = data.contents.dtada_data - - name = cast(tmp + 16, c_char_p).value - count = consumer.deref(tmp + 272, c_int).value - - print('Zone "{0:s}" made {1:d} syscalls.'.format(name.decode(), count)) - - return 0 - - -def main(): - """ - Run DTrace... - """ - dtrace = consumer.DTraceConsumerThread(SCRIPT, walk_func=walk) - dtrace.start() - - # we will stop the thread after some time... - time.sleep(5) - - # stop and wait for join... - dtrace.stop() - dtrace.join() - - -if __name__ == '__main__': - main() diff --git a/examples/ctypes/syscall_count.py b/examples/ctypes/syscall_count.py deleted file mode 100755 index 294a121..0000000 --- a/examples/ctypes/syscall_count.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Use the Python DTrace consumer and run a syscall counter DTrace script. - -Created on Oct 10, 2011 - -@author: tmetsch -""" - -from dtrace_ctypes import consumer - -SCRIPT = 'syscall:::entry { @num[execname] = count(); }' - - -def main(): - """ - Run DTrace... - """ - dtrace = consumer.DTraceConsumer() - dtrace.run(SCRIPT, 4) - - -if __name__ == '__main__': - main() diff --git a/examples/ctypes/syscall_count_continuous.py b/examples/ctypes/syscall_count_continuous.py deleted file mode 100755 index dc72f00..0000000 --- a/examples/ctypes/syscall_count_continuous.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python - -""" -Use the Python DTrace consumer as a Thread and run a syscall counter DTrace -script. - -Created on Oct 10, 2011 - -@author: tmetsch -""" - -import time - -from dtrace_ctypes import consumer - -SCRIPT = 'syscall:::entry { @num[execname] = count(); }' - - -def main(): - """ - Run DTrace... - """ - dtrace = consumer.DTraceConsumerThread(SCRIPT) - dtrace.start() - - # we will stop the thread after some time... - time.sleep(2) - - # stop and wait for join... - dtrace.stop() - dtrace.join() - - -if __name__ == '__main__': - main() diff --git a/examples/ctypes/syscall_count_own_walk.py b/examples/ctypes/syscall_count_own_walk.py deleted file mode 100755 index b5e1436..0000000 --- a/examples/ctypes/syscall_count_own_walk.py +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/env python - -""" -Use the Python DTrace consumer and run a syscall counter DTrace script with an -own aggregate walk function. - -Created on Oct 10, 2011 - -@author: tmetsch -""" - -from __future__ import print_function -from ctypes import cast, c_char_p, c_int -from dtrace_ctypes import consumer - -SCRIPT = 'syscall:::entry { @num[execname] = count(); }' - - -def my_walk(data, _): - """ - Aggregate walker. - - Have a look at the simple walk func in the consumer source code... - """ - tmp = data.contents.dtada_data - name = cast(tmp + 16, c_char_p).value - instance = consumer.deref(tmp + 272, c_int).value - - print('{0:4d} > {1:60s}'.format(instance, name.decode())) - - return 0 - - -def main(): - """ - Run DTrace... - """ - dtrace = consumer.DTraceConsumer(walk_func=my_walk) - dtrace.run(SCRIPT, 4) - - -if __name__ == '__main__': - main() diff --git a/examples/hello_world.py b/examples/hello_world.py deleted file mode 100755 index c345bee..0000000 --- a/examples/hello_world.py +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env python - -""" -Use the Python DTrace consumer and run a Hello World DTrace script. - -Created on Oct 10, 2011 - -@author: tmetsch -""" - -import dtrace - -SCRIPT = 'dtrace:::BEGIN {trace("Hello World"); exit(0);}' - - -def main(): - """ - Run DTrace... - """ - consumer = dtrace.DTraceConsumer() - # Even when the runtime is set to run 10sec this will terminate immediately - # because of the exit statement in the D script. - consumer.run(SCRIPT, runtime=10) - - -if __name__ == '__main__': - main() diff --git a/examples/read_bytes.py b/examples/read_bytes.py deleted file mode 100755 index fa1d06d..0000000 --- a/examples/read_bytes.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python - -""" -Use the Python DTrace consumer and sum the num of bytes read. - -Created on Oct 10, 2011 - -@author: tmetsch -""" -from __future__ import print_function - -import dtrace - -SCRIPT = 'sysinfo:::readch { @bytes[execname] = sum(arg0); }' - - -def main(): - """ - Run DTrace... - """ - print('Hint: if you don\'t get any output try running it with pfexec...') - - consumer = dtrace.DTraceConsumer() - consumer.run(SCRIPT, 4) - - -if __name__ == '__main__': - main() diff --git a/examples/read_distribution_quantize.py b/examples/read_distribution_quantize.py deleted file mode 100755 index 1ce625b..0000000 --- a/examples/read_distribution_quantize.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python - -""" -Use the Python DTrace consumer with a script using quantize. - -Created on Mar 28, 2012 - -@author: tmetsch -""" -from __future__ import print_function - -import dtrace - -# SCRIPT = 'io:::start { @bytes = quantize(args[0]->b_bcount); }' -SCRIPT = 'sysinfo:::readch { @dist[execname] = quantize(arg0); }' - - -def my_walk(_action, _identifier, key, values): - """ - Walk the aggregrate. - """ - print(key) - for item in values: - if item[0][0] > 0 and item[1] > 0: - print('%8s %20s' % (item[0][0], item[1])) - - -def main(): - """ - Run DTrace... - """ - consumer = dtrace.DTraceConsumer(walk_func=my_walk) - consumer.run(SCRIPT, 10) - - -if __name__ == '__main__': - main() diff --git a/examples/stack_example.py b/examples/stack_example.py deleted file mode 100644 index 2d4cd9a..0000000 --- a/examples/stack_example.py +++ /dev/null @@ -1,23 +0,0 @@ -#!/usr/bin/env python - -""" -Use the Python DTrace consumer and use stack(). -""" - -import dtrace - -SCRIPT = "profile-99 /arg0/ { @foo[stack()] = count();}" - - -def main(): - """ - Run DTrace... - """ - print('Hint: if you don\'t get any output try running it with pfexec...') - - consumer = dtrace.DTraceConsumer() - consumer.run(SCRIPT, 4) - - -if __name__ == '__main__': - main() diff --git a/examples/syscall_by_zone.py b/examples/syscall_by_zone.py deleted file mode 100755 index ca73ae1..0000000 --- a/examples/syscall_by_zone.py +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env python - -""" -Use the Python DTrace consumer and count syscalls by zone. - -Created on Oct 10, 2011 - -@author: tmetsch -""" -from __future__ import print_function - -import time - -import dtrace - -SCRIPT = 'syscall:::entry { @num[zonename] = count(); }' - - -def my_walk(_action, _identifier, key, value): - """ - A simple aggregate walker. - """ - print('Zone "{0:s}" made {1:d} syscalls.'.format(key[0].decode(), value)) - - -def main(): - """ - Run DTrace... - """ - thr = dtrace.DTraceConsumerThread(SCRIPT, walk_func=my_walk, sleep=1) - thr.start() - - # we will stop the thread after some time... - time.sleep(5) - - # stop and wait for join... - thr.stop() - thr.join() - - -if __name__ == '__main__': - main() diff --git a/examples/syscall_count.py b/examples/syscall_count.py deleted file mode 100755 index f4fe339..0000000 --- a/examples/syscall_count.py +++ /dev/null @@ -1,25 +0,0 @@ -#!/usr/bin/env python - -""" -Use the Python DTrace consumer and run a syscall counter DTrace script. - -Created on Oct 10, 2011 - -@author: tmetsch -""" - -import dtrace - -SCRIPT = 'syscall:::entry { @num[pid,execname] = count(); }' - - -def main(): - """ - Run DTrace... - """ - consumer = dtrace.DTraceConsumer() - consumer.run(SCRIPT, 2) - - -if __name__ == '__main__': - main() diff --git a/examples/syscall_count_continuous.py b/examples/syscall_count_continuous.py deleted file mode 100755 index 08604a8..0000000 --- a/examples/syscall_count_continuous.py +++ /dev/null @@ -1,35 +0,0 @@ -#!/usr/bin/env python - -""" -Use the Python DTrace consumer as a Thread and run a syscall counter DTrace -script. - -Created on Oct 10, 2011 - -@author: tmetsch -""" - -import time - -import dtrace - -SCRIPT = 'syscall:::entry { @num[execname] = count(); }' - - -def main(): - """ - Run DTrace... - """ - thr = dtrace.DTraceConsumerThread(SCRIPT) - thr.start() - - # we will stop the thread after some time... - time.sleep(5) - - # stop and wait for join... - thr.stop() - thr.join() - - -if __name__ == '__main__': - main() diff --git a/examples/syscall_count_own_walk.py b/examples/syscall_count_own_walk.py deleted file mode 100755 index 6a9c47f..0000000 --- a/examples/syscall_count_own_walk.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python - -""" -Use the Python DTrace consumer and run a syscall counter DTrace script with an -own aggregate walk function. - -Created on Oct 10, 2011 - -@author: tmetsch -""" -from __future__ import print_function - -import dtrace - -SCRIPT = 'syscall:::entry { @num[execname] = count(); }' - - -def my_walk(_action, identifier, key, value): - """ - Aggregate walker. - """ - print('>', identifier, key, value) - - -def main(): - """ - Run DTrace... - """ - consumer = dtrace.DTraceConsumer(walk_func=my_walk) - consumer.run(SCRIPT, 4) - - -if __name__ == '__main__': - main() diff --git a/examples/syscall_lquantize.py b/examples/syscall_lquantize.py deleted file mode 100755 index 3d429c9..0000000 --- a/examples/syscall_lquantize.py +++ /dev/null @@ -1,36 +0,0 @@ -#!/usr/bin/env python - -""" -Use the Python DTrace consumer with a script using lquantize. - -Created on Mar 28, 2012 - -@author: tmetsch -""" -from __future__ import print_function - -import dtrace - -SCRIPT = 'syscall::read:entry { @dist[execname] = lquantize(arg0, 0, 12, 2); }' - - -def my_walk(_action, _identifier, key, values): - """ - Walk the aggregate. - """ - print(key) - for item in values: - if item[0][0] > 0: - print('%8s %20s' % (item[0][0], item[1])) - - -def main(): - """ - Run DTrace... - """ - consumer = dtrace.DTraceConsumer(walk_func=my_walk) - consumer.run(SCRIPT, 5) - - -if __name__ == '__main__': - main() diff --git a/index.html b/index.html new file mode 100644 index 0000000..3dc4291 --- /dev/null +++ b/index.html @@ -0,0 +1,90 @@ + + + + + + tmetsch/python-dtrace @ GitHub + + + + + + Fork me on GitHub +
+
+ github github +
+

+ python-dtrace +

+
+ A Python DTrace consumer using libdtrace - Now Python can be used as DTrace Provider and Consumer... +
+

+ Download +

+

+ You can download this project in either zip or tar formats. +

+

+ You can also clone the project with Git by running: +

+
+$ git clone git://github.com/tmetsch/python-dtrace
+
+

+ The project is also hosted on pypi and can be installed using pip or easy_install 'python-dtrace'. +

+

+ Documentation +

+

+ Please review the README file on GitHub and have a look at the examples in the examples folder. +

+

+ Screenshots +

+

+ Several examples are demoed in little scripts in the /examples folder. But you can do way more with it: Click on the Screenshots to see larger views. +

+
+ Screenshot 1 +
+ Trace the flow of a program provided by a pid (The language the program is written in needs to have a DTrace provider - like Python). By double-clicking the entries the matching Source Code is shown. +
+
+
+ Screenshot 2 +
+ Life updated & animated diagram written in Python with Tk. The size of the bubbles reflects the number of syscalls the process made. So the bubbles appear, grow and shrink while the system runs. +
+
+
+ Screenshot 3 +
+ Basic usage of the DTrace Python Consumer. +
+
+
+ Screenshot 4 +
+ Life updating call graph of a program which DTrace attached to using the pid. Again any (running) programm written in a language which has a DTrace provider can be traced - and therefore callgraphs created. +
+
+
+ + diff --git a/python-dtrace-callgraph.png b/python-dtrace-callgraph.png new file mode 100644 index 0000000..94e48cc Binary files /dev/null and b/python-dtrace-callgraph.png differ diff --git a/python-dtrace-syscalls.png b/python-dtrace-syscalls.png new file mode 100644 index 0000000..2784b76 Binary files /dev/null and b/python-dtrace-syscalls.png differ diff --git a/python-dtrace-terminal.png b/python-dtrace-terminal.png new file mode 100644 index 0000000..d102c74 Binary files /dev/null and b/python-dtrace-terminal.png differ diff --git a/python-dtrace-trace-flow.png b/python-dtrace-trace-flow.png new file mode 100644 index 0000000..cf913e2 Binary files /dev/null and b/python-dtrace-trace-flow.png differ diff --git a/setup.py b/setup.py deleted file mode 100644 index 6f573ea..0000000 --- a/setup.py +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/env python - -""" -Setup script. - -Created on Oct 10, 2011 - -@author: tmetsch -""" - -from distutils.core import setup -from distutils.extension import Extension -import os -import platform -import sys -try: - from Cython.Build import build_ext, cythonize - extra_args = {} - if platform.system().lower().startswith("freebsd"): - # On older FreeBSD versions the dtrace headers are not - # installed by default, so we need to find the full sources. - if not os.path.exists("/usr/include/dtrace.h"): - src_dir = os.getenv("FREEBSD_SRC_DIR", "/usr/src") - if not os.path.exists(os.path.join(src_dir, "sys/cddl")): - raise ImportError("Cannot find FreeBSD DTrace headers") - extra_args["include_dirs"] = [ - os.path.join(src_dir, - "sys/cddl/compat/opensolaris"), - os.path.join(src_dir, - "sys/cddl/contrib/opensolaris/uts/common"), - os.path.join(src_dir, - "cddl/contrib/opensolaris/lib/libdtrace/common"), - ] - if os.getenv("ENABLE_ASAN", None) is not None: - extra_args["extra_compile_args"] = ["-fsanitize=address"] - extra_args["extra_link_args"] = ["-fsanitize=address"] - BUILD_EXTENSION = {'build_ext': build_ext} - EXT_MODULES = cythonize( - [ - Extension("dtrace", ["dtrace_cython/dtrace_h.pxd", - "dtrace_cython/consumer.pyx"], - libraries=["dtrace"], - **extra_args) - ], - language_level=sys.version_info.major - ) - -except ImportError: - BUILD_EXTENSION = {} - EXT_MODULES = None - print('WARNING: Cython seems not to be present. Currently you will only' - ' be able to use the ctypes wrapper. Or you can install cython and' - ' try again.') - - -setup(name='python-dtrace', - version='0.0.15', - description='DTrace consumer for Python based on libdtrace. Use Python' - ' as DTrace Consumer and Provider! See the homepage for' - ' more information.', - license='MIT', - keywords='DTrace', - url='http://tmetsch.github.io/python-dtrace/', - packages=['dtrace_ctypes'], - cmdclass=BUILD_EXTENSION, - ext_modules=EXT_MODULES, - classifiers=["Development Status :: 2 - Pre-Alpha", - "Operating System :: OS Independent", - "Programming Language :: Python" - ]) diff --git a/style.css b/style.css new file mode 100644 index 0000000..fb2a29a --- /dev/null +++ b/style.css @@ -0,0 +1,56 @@ +body { + margin-top: 1.0em; + background-color: #fff; + font-family: Helvetica, Arial, FreeSans, san-serif; + color: #000000; + } +#container { + margin: 0 auto; + width: 600px; + } +h1 { + font-size: 3.8em; + color: #26331a; + margin-bottom: 3px; + } +h1 a { + text-decoration: none +} +h2 { + font-size: 1.5em; + color: #26331a; + } +a { + color: #26331a; + } +.description { + font-size: 1.2em; + margin-bottom: 30px; + margin-top: 30px; + font-style: italic; + } +.download { + float: right; + } + +pre { + background: #000; + color: #fff; + padding: 15px; + } + +.image { + width: 280px; + float: left; + margin: 10px; + } +.image .caption { + font-size: 80%; + font-family: Verdana, Arial, sans-serif; + text-align: justify; + } + +.image img { + width: 280px; + border: 0px; + } diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index 6aa8002..0000000 --- a/tests/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -""" -Unit tests. -""" -__author__ = 'tmetsch' diff --git a/tests/dtrace_ctypes_test.py b/tests/dtrace_ctypes_test.py deleted file mode 100644 index 8cf729f..0000000 --- a/tests/dtrace_ctypes_test.py +++ /dev/null @@ -1,42 +0,0 @@ -""" -Unittest for ctypes based consumer. -""" - -__author__ = 'tmetsch' - - -import unittest - -from ctypes import c_char_p - -from dtrace_ctypes import consumer - -SCRIPT = 'dtrace:::BEGIN {trace("Hello World");}' - - -class TestDTraceConsumer(unittest.TestCase): - """ - Tests ctypes based DTrace consumer. - """ - - def setUp(self): - self.out = b'' - self.consumer = consumer.DTraceConsumer(out_func=self._get_output) - - def test_run_for_success(self): - """ - Test for success. - """ - self.consumer.run(SCRIPT) - - def test_run_for_sanity(self): - """ - Test for sanity. - """ - self.consumer.run(SCRIPT) - self.assertEqual(self.out, b'Hello World') - - def _get_output(self, data, _arg): - tmp = c_char_p(data.contents.dtbda_buffered).value.strip() - self.out += tmp - return 0 diff --git a/tests/dtrace_cython_test.py b/tests/dtrace_cython_test.py deleted file mode 100644 index cc47cc3..0000000 --- a/tests/dtrace_cython_test.py +++ /dev/null @@ -1,64 +0,0 @@ -""" -Unittest for cython based consumer. -""" - -__author__ = 'tmetsch' - -import unittest - -import dtrace - -SCRIPT = 'dtrace:::BEGIN {trace("Hello World");}' - - -class TestDTraceConsumer(unittest.TestCase): - """ - Test DTrace consumer. - """ - - def setUp(self): - self.out = '' - self.consumer = dtrace.DTraceConsumer(out_func=self._get_output) - - # Test fo success. - - def test_compile_for_success(self): - """ - Test for success. - """ - self.consumer.compile(SCRIPT) - - def test_run_for_success(self): - """ - Test for success. - """ - self.consumer.run(SCRIPT) - - # Test fo failure. - - def test_compile_for_failure(self): - """ - Test for failure. - """ - self.assertRaises(Exception, self.consumer.compile, 'foo') - - def test_run_for_failure(self): - """ - Test for failure. - """ - self.assertRaises(Exception, self.consumer.run, 'sadf') - - # Test fo sanity. - - def test_run_for_sanity(self): - """ - Test for sanity. - """ - self.consumer.run(SCRIPT) - self.assertEqual(b'Hello World', self.out) - - def _get_output(self, tmp): - """ - Test for sanity. - """ - self.out = tmp diff --git a/tox.ini b/tox.ini deleted file mode 100644 index 304f74a..0000000 --- a/tox.ini +++ /dev/null @@ -1,13 +0,0 @@ -# Tox (http://tox.testrun.org/) is a tool for running tests -# in multiple virtualenvs. This configuration file will run the -# test suite on all supported python versions. To use it, "pip install tox" -# and then run "tox" from this directory. - -[tox] -envlist = py26, py27 - -[testenv] -# If you are on a Mac try: -# commands = sudo python examples/syscall_count.py -commands = python examples/syscall_count.py -deps = Cython