Permalink
Browse files

Fixed avg(); fixed setopt() values; added more tests

  • Loading branch information...
1 parent d5bd50b commit 0cc75005f7c81126f2093d75981aee0ea1d4657c @bcantrill bcantrill committed Nov 5, 2010
Showing with 399 additions and 59 deletions.
  1. +0 −9 README
  2. +201 −0 README.md
  3. +0 −20 example.js
  4. +13 −0 examples/example1.js
  5. +45 −0 examples/example2.js
  6. +16 −4 libdtrace.cc
  7. +36 −26 tests/test-aggregation.js
  8. +27 −0 tests/test-options.js
  9. +45 −0 tests/test-quantize.js
  10. +16 −0 tests/test-stddev.js
View
9 README
@@ -1,9 +0,0 @@
-
-dtrace, a node.js addon for controlling DTrace enablings
---------------------------------------------------------
-
-This is a simple (and, for the moment, crude) addon to provide native
-libdtrace to node.js programs, supporting basic enablings (including
-aggregations). Currently, esoteric actions are not supported -- this is
-not designed to allow a drop-in replacement for dtrace(1).
-
View
201 README.md
@@ -0,0 +1,201 @@
+
+node-libdtrace
+==============
+
+Overview
+--------
+
+node-libdtrace is a Node.js addon that interfaces to libdtrace, allowing
+node programs to control DTrace enablings.
+
+Status
+------
+
+The primary objective is not to create a `dtrace(1M)` alternative in node, but
+rather to allow node programs to create and control programmatically useful
+DTrace enablings. That is, the goal is software-software interaction, and as
+such, DTrace actions related to controlling output (e.g., `printf()`,
+`printa()`) are not supported. Error handling is, for the moment, weak.
+
+Platforms
+---------
+
+This should work on any platform that supports DTrace, and is known to work on
+Mac OS X (tested on 10.6.4) and Solaris, OpenSolaris and derivatives (tested on
+Nevada 121 and later).
+
+Installation
+------------
+
+As an addon, nod-libdtrace is installed in the usual way:
+
+ % node-waf configure
+ % node-waf build
+ % node-waf
+
+API
+---
+
+### `new libdtrace.Consumer()`
+
+Create a new libdtrace consumer, which will correspond to a new `libdtrace`
+state. If DTrace cannot be initalized for any reason, this will throw an
+exception with the `message` member set to the more detailed reason from
+libdtrace. Note that one particularly common failure mode is attempting to
+initialize DTrace without the necessary level of privilege; in this case, for
+example, the `message` member will be:
+
+ DTrace requires additional privileges
+
+(The specifics of this particular message should obviously not be
+programmatically depended upon.) If encountering this error, you will
+need to be a user that has DTrace privileges.
+
+### `consumer.strcompile(str)`
+
+Compile the specified `str` as a D program. This is required before
+any call to `consumer.go()`.
+
+### `consumer.go()`
+
+Instruments the system using the specified enabling. Before `consumer.go()`
+is called, the specified D program has been compiled but not executed; once
+`consumer.go()` is called, no further D compilation is possible.
+
+### `consumer.setopt(option, value)`
+
+Sets the specified `option` (a string) to `value` (an integer, boolean,
+string, or string representation of an integer or boolean, as denoted by
+the option being set).
+
+### `consumer.consume(function func (probe, rec) {})`
+
+Consume any DTrace data traced to the principal buffer since the last call to
+`consumer.consume()` (or the call to `consumer.go()` if `consumer.consume()`
+has not been called). For each trace record, `func` will be called and
+passed two arguments:
+
+* `probe` is an object that specifies the probe that corresponds to the
+ trace record in terms of the probe tuple: provider, module, function
+ and name.
+
+* `rec` is an object that has a single member, `data`, that corresponds to
+ the datum within the trace record. If the trace record has been entirely
+ consumed, `rec` will be `undefined`.
+
+In terms of implementation, a call to `consumer.consume()` will result in a
+call to `dtrace_status()` and a principal buffer switch. Note that if the
+rate of consumption exceeds the specified `switchrate` (set via either
+`#pragma D option switchrate` or `consumer.setopt()`), this will result in no
+new data processing.
+
+### `consumer.aggwalk(function func (varid, key, value) {})`
+
+Snapshot and iterate over all aggregation data accumulated since the
+last call to `consumer.aggwalk()` (or the call to `consumer.go()` if
+`consumer.aggwalk()` has not been called). For each aggregate record,
+`func` will be called and passed three arguments:
+
+* `varid` is the identifier of the aggregation variable. These IDs are
+ assigned in program order, starting with 1.
+
+* `key` is an array of keys that, taken with the variable identifier,
+ uniquely specifies the aggregation record.
+
+* `value` is the value of the aggregation record, the meaning of which
+ depends on the aggregating action:
+
+ * For `count()`, `sum()`, `max()` and `min()`, the value is the
+ integer value of the aggregation action
+
+ * For `avg()`, the value is the numeric value of the aggregating action
+
+ * For `quantize()` and `lquantize()`, the value is an array of 2-tuples
+ denoting ranges and value: each element consists of a two element array
+ denoting the range (minimum followed by maximum, inclusive) and the
+ value for that range.
+
+Upon return from `consumer.aggwalk()`, the aggregation data for the specified
+variable and key(s) is removed.
+
+Note that the rate of `consumer.aggwalk()` actually consumes the aggregation
+buffer is clamed by the `aggrate` option; if `consumer.aggwalk()` is called
+more frequently than the specified rate, `consumer.aggwalk()` will not
+induce any additional data processing.
+
+`consumer.aggwalk()` does not iterate over aggregation data in any guaranteed
+order, and may interleave aggregation variables and/or keys.
+
+Examples
+--------
+
+### Hello world
+
+The obligatory "hello world":
+
+ var sys = require('sys');
+ var libdtrace = require('libdtrace');
+ var dtp = new libdtrace.Consumer();
+
+ var prog = 'BEGIN { trace("hello world"); }';
+
+ dtp.strcompile(prog);
+ dtp.go();
+
+ dtp.consume(function (probe, rec) {
+ if (rec)
+ sys.puts(rec.data);
+ });
+
+### Using aggregations
+
+A slightly more sophisticated example showing system calls aggregated and
+sorted by executable name:
+
+ var sys = require('sys');
+ var libdtrace = require('libdtrace');
+ var dtp = new libdtrace.Consumer();
+
+ var prog = 'syscall:::entry { @[execname] = count(); }'
+
+ dtp.strcompile(prog);
+ dtp.go();
+
+ var syscalls = {};
+ var keys = [];
+
+ var pad = function (val, len)
+ {
+ var rval = '', i, str = val + '';
+
+ for (i = 0; i < Math.abs(len) - str.length; i++)
+ rval += ' ';
+
+ rval = len < 0 ? str + rval : rval + str;
+
+ return (rval);
+ };
+
+ setInterval(function () {
+ var i;
+
+ sys.puts(pad('EXECNAME', -40) + pad('COUNT', -10));
+
+ dtp.aggwalk(function (id, key, val) {
+ if (!syscalls.hasOwnProperty(key[0]))
+ keys.push(key[0]);
+
+ syscalls[key[0]] = val;
+ });
+
+ keys.sort();
+
+ for (i = 0; i < keys.length; i++) {
+ sys.puts(pad(keys[i], -40) + pad(syscalls[keys[i]], -10));
+ syscalls[keys[i]] = 0;
+ }
+
+ sys.puts('');
+ }, 1000);
+
+
View
@@ -1,20 +0,0 @@
-var sys = require('sys');
-var libdtrace = require('libdtrace');
-var dtp = new libdtrace.Consumer();
-
-var prog = 'BEGIN { trace("hello world"); }\n'
-prog += 'BEGIN { @["hello"] = sum(1); @["world"] = sum(2); }'
-
-dtp.strcompile(prog);
-
-dtp.go();
-
-dtp.consume(function (probe, rec) {
- sys.puts(sys.inspect(probe));
- sys.puts(sys.inspect(rec));
-});
-
-dtp.aggwalk(function (varid, key, value) {
- sys.puts(sys.inspect(key));
- sys.puts(sys.inspect(value));
-});
View
@@ -0,0 +1,13 @@
+var sys = require('sys');
+var libdtrace = require('libdtrace');
+var dtp = new libdtrace.Consumer();
+
+var prog = 'BEGIN { trace("hello world"); }\n'
+
+dtp.strcompile(prog);
+dtp.go();
+
+dtp.consume(function (probe, rec) {
+ if (rec)
+ sys.puts(rec.data);
+});
View
@@ -0,0 +1,45 @@
+var sys = require('sys');
+var libdtrace = require('libdtrace');
+var dtp = new libdtrace.Consumer();
+
+var prog = 'syscall:::entry { @[execname] = count(); }'
+
+dtp.strcompile(prog);
+dtp.go();
+
+var syscalls = {};
+var keys = [];
+
+var pad = function (val, len)
+{
+ var rval = '', i, str = val + '';
+
+ for (i = 0; i < Math.abs(len) - str.length; i++)
+ rval += ' ';
+
+ rval = len < 0 ? str + rval : rval + str;
+
+ return (rval);
+};
+
+setInterval(function () {
+ var i;
+
+ sys.puts(pad('EXECNAME', -40) + pad('COUNT', -10));
+
+ dtp.aggwalk(function (id, key, val) {
+ if (!syscalls.hasOwnProperty(key[0]))
+ keys.push(key[0]);
+
+ syscalls[key[0]] = val;
+ });
+
+ keys.sort();
+
+ for (i = 0; i < keys.length; i++) {
+ sys.puts(pad(keys[i], -40) + pad(syscalls[keys[i]], -10));
+ syscalls[keys[i]] = 0;
+ }
+
+ sys.puts('');
+}, 1000);
View
@@ -261,8 +261,11 @@ DTraceConsumer::Setopt(const Arguments& args)
String::Utf8Value option(args[0]->ToString());
if (args.Length() >= 2) {
- if (!args[1]->IsString())
- return (dtc->badarg("expected value for option"));
+ if (args[1]->IsArray())
+ return (dtc->badarg("option value can't be an array"));
+
+ if (args[1]->IsObject())
+ return (dtc->badarg("option value can't be an object"));
String::Utf8Value optval(args[1]->ToString());
rval = dtrace_setopt(dtp, *option, *optval);
@@ -448,7 +451,8 @@ DTraceConsumer::ranges_quantize(dtrace_aggvarid_t varid)
if (i < DTRACE_QUANTIZE_ZEROBUCKET) {
/*
* If we're less than the zero bucket, our range
- * extends from
+ * extends from negative infinity through to the
+ * beginning of our zeroth bucket.
*/
min = i > 0 ? DTRACE_QUANTIZE_BUCKETVAL(i - 1) + 1 :
INT64_MIN;
@@ -562,7 +566,6 @@ DTraceConsumer::aggwalk(const dtrace_aggdata_t *agg, void *arg)
case DTRACEAGG_COUNT:
case DTRACEAGG_MIN:
case DTRACEAGG_MAX:
- case DTRACEAGG_AVG:
case DTRACEAGG_SUM: {
caddr_t addr = agg->dtada_data + aggrec->dtrd_offset;
@@ -571,6 +574,15 @@ DTraceConsumer::aggwalk(const dtrace_aggdata_t *agg, void *arg)
break;
}
+ case DTRACEAGG_AVG: {
+ const int64_t *data = (int64_t *)(agg->dtada_data +
+ aggrec->dtrd_offset);
+
+ assert(aggrec->dtrd_size == sizeof (uint64_t) * 2);
+ val = Number::New(data[1] / (double)data[0]);
+ break;
+ }
+
case DTRACEAGG_QUANTIZE: {
Local<Array> quantize = Array::New();
const int64_t *data = (int64_t *)(agg->dtada_data +
Oops, something went wrong.

0 comments on commit 0cc7500

Please sign in to comment.