Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

add support for sym()/func()/mod()/uaddr()/usym()/umod()/ufunc()

  • Loading branch information...
commit cdea01b6fdeb951b92b052fa336b19825013217b 1 parent 658b724
@bcantrill bcantrill authored
Showing with 262 additions and 47 deletions.
  1. +105 −47 libdtrace.cc
  2. +157 −0 tests/test-sym.js
View
152 libdtrace.cc
@@ -1,3 +1,12 @@
+/*
+ * For whatever reason, g++ on Solaris defines _XOPEN_SOURCE -- which in
+ * turn will prevent us from pulling in our desired definition for boolean_t.
+ * We don't need it, so explicitly undefine it.
+ */
+#ifdef _XOPEN_SOURCE
+#undef _XOPEN_SOURCE
+#endif
+
#include <v8.h>
#include <node.h>
#include <string.h>
@@ -33,7 +42,9 @@ class DTraceConsumer : node::ObjectWrap {
Handle<Value> error(const char *fmt, ...);
Handle<Value> badarg(const char *msg);
+ boolean_t valid(const dtrace_recdesc_t *);
const char *action(const dtrace_recdesc_t *, char *, int);
+ Local<Value> record(const dtrace_recdesc_t *, caddr_t);
Local<Array> *ranges_cached(dtrace_aggvarid_t);
Local<Array> *ranges_cache(dtrace_aggvarid_t, Local<Array> *);
@@ -156,6 +167,7 @@ DTraceConsumer::action(const dtrace_recdesc_t *rec, char *buf, int size)
{ DTRACEACT_LIBACT, "<library action>" },
{ DTRACEACT_USTACK, "ustack()" },
{ DTRACEACT_JSTACK, "jstack()" },
+ { DTRACEACT_USYM, "usym()" },
{ DTRACEACT_UMOD, "umod()" },
{ DTRACEACT_UADDR, "uaddr()" },
{ DTRACEACT_STOP, "stop()" },
@@ -219,6 +231,95 @@ DTraceConsumer::badarg(const char *msg)
return (ThrowException(Exception::TypeError(String::New(msg))));
}
+boolean_t
+DTraceConsumer::valid(const dtrace_recdesc_t *rec)
+{
+ dtrace_actkind_t action = rec->dtrd_action;
+
+ switch (action) {
+ case DTRACEACT_DIFEXPR:
+ case DTRACEACT_SYM:
+ case DTRACEACT_MOD:
+ case DTRACEACT_USYM:
+ case DTRACEACT_UMOD:
+ case DTRACEACT_UADDR:
+ return (B_TRUE);
+
+ default:
+ return (B_FALSE);
+ }
+}
+
+Local<Value>
+DTraceConsumer::record(const dtrace_recdesc_t *rec, caddr_t addr)
+{
+ switch (rec->dtrd_action) {
+ case DTRACEACT_DIFEXPR:
+ switch (rec->dtrd_size) {
+ case sizeof (uint64_t):
+ return (Number::New(*((int64_t *)addr)));
+
+ case sizeof (uint32_t):
+ return (Integer::New(*((int32_t *)addr)));
+
+ case sizeof (uint16_t):
+ return (Integer::New(*((uint16_t *)addr)));
+
+ case sizeof (uint8_t):
+ return (Integer::New(*((uint8_t *)addr)));
+
+ default:
+ return (String::New((const char *)addr));
+ }
+
+ case DTRACEACT_SYM:
+ case DTRACEACT_MOD:
+ case DTRACEACT_USYM:
+ case DTRACEACT_UMOD:
+ case DTRACEACT_UADDR:
+ dtrace_hdl_t *dtp = dtc_handle;
+ char buf[2048], *tick, *plus;
+
+ buf[0] = '\0';
+
+ if (DTRACEACT_CLASS(rec->dtrd_action) == DTRACEACT_KERNEL) {
+ uint64_t pc = ((uint64_t *)addr)[0];
+ dtrace_addr2str(dtp, pc, buf, sizeof (buf) - 1);
+ } else {
+ uint64_t pid = ((uint64_t *)addr)[0];
+ uint64_t pc = ((uint64_t *)addr)[1];
+ dtrace_uaddr2str(dtp, pid, pc, buf, sizeof (buf) - 1);
+ }
+
+ if (rec->dtrd_action == DTRACEACT_MOD ||
+ rec->dtrd_action == DTRACEACT_UMOD) {
+ /*
+ * If we're looking for the module name, we'll
+ * return everything to the left of the left-most
+ * tick -- or "<undefined>" if there is none.
+ */
+ if ((tick = strchr(buf, '`')) == NULL)
+ return (String::New("<unknown>"));
+
+ *tick = '\0';
+ } else if (rec->dtrd_action == DTRACEACT_SYM ||
+ rec->dtrd_action == DTRACEACT_USYM) {
+ /*
+ * If we're looking for the symbol name, we'll
+ * return everything to the left of the right-most
+ * plus sign (if there is one).
+ */
+ if ((plus = strrchr(buf, '+')) != NULL)
+ *plus = '\0';
+ }
+
+ return (String::New(buf));
+ }
+
+ assert(B_FALSE);
+ return (Integer::New(-1));
+}
+
Handle<Value>
DTraceConsumer::Strcompile(const Arguments& args)
{
@@ -339,7 +440,7 @@ DTraceConsumer::consume(const dtrace_probedata_t *data,
return (DTRACE_CONSUME_NEXT);
}
- if (rec->dtrd_action != DTRACEACT_DIFEXPR) {
+ if (!dtc->valid(rec)) {
char errbuf[256];
dtc->dtc_error = dtc->error("unsupported action %s "
@@ -351,30 +452,8 @@ DTraceConsumer::consume(const dtrace_probedata_t *data,
}
Local<Object> record = Object::New();
- void *addr = data->dtpda_data;
-
- switch (rec->dtrd_size) {
- case sizeof (uint64_t):
- datum = Number::New(*((int64_t *)addr));
- break;
-
- case sizeof (uint32_t):
- datum = Integer::New(*((int32_t *)addr));
- break;
-
- case sizeof (uint16_t):
- datum = Integer::New(*((uint16_t *)addr));
- break;
-
- case sizeof (uint8_t):
- datum = Integer::New(*((uint8_t *)addr));
- break;
-
- default:
- datum = String::New((const char *)addr);
- }
- record->Set(String::New("data"), datum);
+ record->Set(String::New("data"), dtc->record(rec, data->dtpda_data));
Local<Value> argv[2] = { probe, record };
@@ -528,7 +607,7 @@ DTraceConsumer::aggwalk(const dtrace_aggdata_t *agg, void *arg)
caddr_t addr = agg->dtada_data + rec->dtrd_offset;
Local<Value> datum;
- if (rec->dtrd_action != DTRACEACT_DIFEXPR) {
+ if (!dtc->valid(rec)) {
dtc->dtc_error = dtc->error("unsupported action %s "
"as key #%d in aggregation \"%s\"\n",
dtc->action(rec, errbuf, sizeof (errbuf)), i,
@@ -536,28 +615,7 @@ DTraceConsumer::aggwalk(const dtrace_aggdata_t *agg, void *arg)
return (DTRACE_AGGWALK_ERROR);
}
- switch (rec->dtrd_size) {
- case sizeof (uint64_t):
- datum = Number::New(*((int64_t *)addr));
- break;
-
- case sizeof (uint32_t):
- datum = Integer::New(*((int32_t *)addr));
- break;
-
- case sizeof (uint16_t):
- datum = Integer::New(*((uint16_t *)addr));
- break;
-
- case sizeof (uint8_t):
- datum = Integer::New(*((uint8_t *)addr));
- break;
-
- default:
- datum = String::New((const char *)addr);
- }
-
- key->Set(i - 1, datum);
+ key->Set(i - 1, dtc->record(rec, addr));
}
aggrec = &aggdesc->dtagd_rec[aggdesc->dtagd_nrecs - 1];
View
157 tests/test-sym.js
@@ -0,0 +1,157 @@
+var sys = require('sys');
+var libdtrace = require('libdtrace');
+var assert = require('assert');
+var fs = require('fs');
+
+var test = function (action, valid, work)
+{
+ /*
+ * First, verify that the action can be compiled and consumed.
+ */
+ var dtp = new libdtrace.Consumer();
+ sys.puts('>>>> basic compile for ' + action + '()');
+
+ dtp.strcompile('BEGIN { ' + action + '(0); }');
+
+ dtp.go();
+
+ var found = false, start, now;
+
+ if (!work)
+ work = function () {};
+
+ dtp.consume(function testbasic (probe, rec) {
+ assert.equal(probe.provider, 'dtrace');
+ assert.equal(probe.module, '');
+ assert.equal(probe.function, '');
+ assert.equal(probe.name, 'BEGIN');
+
+ if (!rec || !rec.hasOwnProperty('data'))
+ return;
+
+ found = true;
+ sys.puts(' ' + sys.inspect(rec));
+ });
+
+ assert.ok(found, 'did not find valid data record');
+
+ var argument = (action.indexOf('u') == 0 ? 'arg1' : 'arg0');
+
+ /*
+ * Now verify the straight consumption.
+ */
+ sys.puts('>>>> consumption for ' + action +
+ '() (using ' + argument + ' on profile probe)');
+
+ dtp = new libdtrace.Consumer();
+ dtp.strcompile('profile-97hz /' + argument + ' != 0/ ' +
+ '{ ' + action + '(' + argument + ') }');
+
+ dtp.go();
+
+ start = (new Date()).valueOf();
+
+ do {
+ now = (new Date()).valueOf();
+ work();
+ } while (now - start < 2000);
+
+ found = false;
+
+ dtp.consume(function testbasic (probe, rec) {
+ if (!rec)
+ return;
+
+ if (valid(rec.data))
+ found = true;
+
+ sys.puts(' ' + sys.inspect(rec));
+ });
+
+ dtp.stop();
+ assert.ok(found, 'did not find valid record in principal buffer');
+
+ sys.puts('>>>> aggregation on ' + action +
+ '() (using ' + argument + ' on profile probe)');
+
+ dtp = new libdtrace.Consumer();
+ dtp.strcompile('profile-97hz /' + argument + ' != 0/ ' +
+ '{ @[' + action + '(' + argument + ')] = count() }');
+
+ dtp.setopt('aggrate', '10ms');
+ dtp.go();
+
+ var start = new Date().valueOf();
+
+ do {
+ now = (new Date()).valueOf();
+ work();
+ } while (now - start < 2000);
+
+ found = false;
+
+ dtp.aggwalk(function (varid, key, val) {
+ if (valid(key[0]))
+ found = true;
+
+ sys.puts(' ' + sys.inspect(key) + ': ' + sys.inspect(val));
+ });
+};
+
+var validsym = function (str)
+{
+ assert.ok(str.indexOf('+0x') == -1,
+ 'expected symbol without offset; found "' + str + '"');
+
+ return (str.indexOf('`') != -1 && str.indexOf('+') == -1);
+}
+
+var validaddr = function (str)
+{
+ assert.ok(str.indexOf('0x') == 0 || str.indexOf('`') != -1,
+ 'expected address; found "' + str + '"');
+
+ return (str.indexOf('`') != -1);
+}
+
+var kernelwork = function ()
+{
+ fs.unlink('/tmp/does/not/exist');
+}
+
+var validkmod = function (str)
+{
+ if (str == 'unix' || 'genunix') /* Solaris */
+ return (true);
+
+ if (str == 'mach_kernel') /* OS X */
+ return (true);
+
+ return (false);
+};
+
+var validmod = function (str)
+{
+ if (str.indexOf('.so.') != -1) /* Solaris */
+ return (true);
+
+ if (str.indexOf('dylib') != -1) /* OS X */
+ return (true);
+};
+
+test('sym', validsym, kernelwork);
+test('func', validsym, kernelwork);
+test('mod', validkmod, kernelwork);
+
+test('uaddr', validaddr);
+test('usym', validsym);
+test('umod', validmod);
+
+/*
+ * Note that this test may induce a seg fault in node on at least some variants
+ * of Mac OS X. For more details, see the post to dtrace-discuss with the
+ * subject "Bug in dtrace_uaddr2str() on OS X?" (Dec. 1, 2010). (As of this
+ * writing, the bug had been root-caused within Apple, and a fix forthcoming.)
+ */
+test('ufunc', validsym);
+
Please sign in to comment.
Something went wrong with that request. Please try again.