├── .gitignore ├── LICENSE ├── README.md ├── binding.gyp ├── examples ├── example1.js └── example2.js ├── index.js ├── libdtrace.cc ├── package.json └── tests ├── test-aggregation.js ├── test-basic.js ├── test-footprint.js ├── test-llquantize.js ├── test-lquantize.js ├── test-options.js ├── test-printf.js ├── test-quantize.js ├── test-sched.js ├── test-stddev.js ├── test-sym.js └── test-version.js /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /node_modules 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2010 Bryan Cantrill. All rights reserved. 2 | Permission is hereby granted, free of charge, to any person obtaining a copy 3 | of this software and associated documentation files (the "Software"), to 4 | deal in the Software without restriction, including without limitation the 5 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 6 | sell copies of the Software, and to permit persons to whom the Software is 7 | furnished to do so, subject to the following conditions: 8 | 9 | The above copyright notice and this permission notice shall be included in 10 | all copies or substantial portions of the Software. 11 | 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 13 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 14 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 15 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 16 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 17 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 18 | IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | node-libdtrace 3 | ============== 4 | 5 | Overview 6 | -------- 7 | 8 | node-libdtrace is a Node.js addon that interfaces to libdtrace, allowing 9 | node programs to control DTrace enablings. 10 | 11 | Status 12 | ------ 13 | 14 | The primary objective is not to create a `dtrace(1M)` alternative in node, but 15 | rather to allow node programs to create and control programmatically useful 16 | DTrace enablings. That is, the goal is software-software interaction, and as 17 | such, DTrace actions related to controlling output (e.g., `printf()`, 18 | `printa()`) are not supported. Error handling is, for the moment, weak. 19 | 20 | Platforms 21 | --------- 22 | 23 | This should work on any platform that supports DTrace, and is known to work on 24 | Mac OS X (tested on 10.7.5) and illumos (tested on 25 | SmartOS). 26 | 27 | Installation 28 | ------------ 29 | 30 | As an addon, node-libdtrace is installed in the usual way: 31 | 32 | % npm install libdtrace 33 | 34 | API 35 | --- 36 | 37 | ### `new libdtrace.Consumer()` 38 | 39 | Create a new libdtrace consumer, which will correspond to a new `libdtrace` 40 | state. If DTrace cannot be initalized for any reason, this will throw an 41 | exception with the `message` member set to the more detailed reason from 42 | libdtrace. Note that one particularly common failure mode is attempting to 43 | initialize DTrace without the necessary level of privilege; in this case, for 44 | example, the `message` member will be: 45 | 46 | DTrace requires additional privileges 47 | 48 | (The specifics of this particular message should obviously not be 49 | programmatically depended upon.) If encountering this error, you will 50 | need to be a user that has DTrace privileges. 51 | 52 | ### `consumer.strcompile(str)` 53 | 54 | Compile the specified `str` as a D program. This is required before 55 | any call to `consumer.go()`. 56 | 57 | ### `consumer.go()` 58 | 59 | Instruments the system using the specified enabling. Before `consumer.go()` 60 | is called, the specified D program has been compiled but not executed; once 61 | `consumer.go()` is called, no further D compilation is possible. 62 | 63 | ### `consumer.setopt(option, value)` 64 | 65 | Sets the specified `option` (a string) to `value` (an integer, boolean, 66 | string, or string representation of an integer or boolean, as denoted by 67 | the option being set). 68 | 69 | ### `consumer.consume(function func (probe, rec) {})` 70 | 71 | Consume any DTrace data traced to the principal buffer since the last call to 72 | `consumer.consume()` (or the call to `consumer.go()` if `consumer.consume()` 73 | has not been called). For each trace record, `func` will be called and 74 | passed two arguments: 75 | 76 | * `probe` is an object that specifies the probe that corresponds to the 77 | trace record in terms of the probe tuple: provider, module, function 78 | and name. 79 | 80 | * `rec` is an object that has a single member, `data`, that corresponds to 81 | the datum within the trace record. If the trace record has been entirely 82 | consumed, `rec` will be `undefined`. 83 | 84 | In terms of implementation, a call to `consumer.consume()` will result in a 85 | call to `dtrace_status()` and a principal buffer switch. Note that if the 86 | rate of consumption exceeds the specified `switchrate` (set via either 87 | `#pragma D option switchrate` or `consumer.setopt()`), this will result in no 88 | new data processing. 89 | 90 | ### `consumer.aggwalk(function func (varid, key, value) {})` 91 | 92 | Snapshot and iterate over all aggregation data accumulated since the 93 | last call to `consumer.aggwalk()` (or the call to `consumer.go()` if 94 | `consumer.aggwalk()` has not been called). For each aggregate record, 95 | `func` will be called and passed three arguments: 96 | 97 | * `varid` is the identifier of the aggregation variable. These IDs are 98 | assigned in program order, starting with 1. 99 | 100 | * `key` is an array of keys that, taken with the variable identifier, 101 | uniquely specifies the aggregation record. 102 | 103 | * `value` is the value of the aggregation record, the meaning of which 104 | depends on the aggregating action: 105 | 106 | * For `count()`, `sum()`, `max()` and `min()`, the value is the 107 | integer value of the aggregation action 108 | 109 | * For `avg()`, the value is the numeric value of the aggregating action 110 | 111 | * For `quantize()` and `lquantize()`, the value is an array of 2-tuples 112 | denoting ranges and value: each element consists of a two element array 113 | denoting the range (minimum followed by maximum, inclusive) and the 114 | value for that range. 115 | 116 | Upon return from `consumer.aggwalk()`, the aggregation data for the specified 117 | variable and key(s) is removed. 118 | 119 | Note that the rate of `consumer.aggwalk()` actually consumes the aggregation 120 | buffer is clamed by the `aggrate` option; if `consumer.aggwalk()` is called 121 | more frequently than the specified rate, `consumer.aggwalk()` will not 122 | induce any additional data processing. 123 | 124 | `consumer.aggwalk()` does not iterate over aggregation data in any guaranteed 125 | order, and may interleave aggregation variables and/or keys. 126 | 127 | ### `consumer.version()` 128 | 129 | Returns the version string, as returned from `dtrace -V`. 130 | 131 | Examples 132 | -------- 133 | 134 | ### Hello world 135 | 136 | The obligatory "hello world": 137 | 138 | var sys = require('sys'); 139 | var libdtrace = require('libdtrace'); 140 | var dtp = new libdtrace.Consumer(); 141 | 142 | var prog = 'BEGIN { trace("hello world"); }'; 143 | 144 | dtp.strcompile(prog); 145 | dtp.go(); 146 | 147 | dtp.consume(function (probe, rec) { 148 | if (rec) 149 | sys.puts(rec.data); 150 | }); 151 | 152 | ### Using aggregations 153 | 154 | A slightly more sophisticated example showing system calls aggregated and 155 | sorted by executable name: 156 | 157 | var sys = require('sys'); 158 | var libdtrace = require('libdtrace'); 159 | var dtp = new libdtrace.Consumer(); 160 | 161 | var prog = 'syscall:::entry { @[execname] = count(); }' 162 | 163 | dtp.strcompile(prog); 164 | dtp.go(); 165 | 166 | var syscalls = {}; 167 | var keys = []; 168 | 169 | var pad = function (val, len) 170 | { 171 | var rval = '', i, str = val + ''; 172 | 173 | for (i = 0; i < Math.abs(len) - str.length; i++) 174 | rval += ' '; 175 | 176 | rval = len < 0 ? str + rval : rval + str; 177 | 178 | return (rval); 179 | }; 180 | 181 | setInterval(function () { 182 | var i; 183 | 184 | sys.puts(pad('EXECNAME', -40) + pad('COUNT', -10)); 185 | 186 | dtp.aggwalk(function (id, key, val) { 187 | if (!syscalls.hasOwnProperty(key[0])) 188 | keys.push(key[0]); 189 | 190 | syscalls[key[0]] = val; 191 | }); 192 | 193 | keys.sort(); 194 | 195 | for (i = 0; i < keys.length; i++) { 196 | sys.puts(pad(keys[i], -40) + pad(syscalls[keys[i]], -10)); 197 | syscalls[keys[i]] = 0; 198 | } 199 | 200 | sys.puts(''); 201 | }, 1000); 202 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | 'targets': [ 3 | { 4 | 'target_name': 'libdtrace', 5 | 'cflags_cc': ['-fexceptions'], 6 | 'ldflags': ['-ldtrace'], 7 | 'sources': [ 8 | 'libdtrace.cc' 9 | ], 10 | 'libraries': ['-ldtrace'], 11 | 'xcode_settings': { 12 | 'OTHER_CPLUSPLUSFLAGS': [ 13 | '-fexceptions', 14 | '-Wunused-variable', 15 | ], 16 | } 17 | }, 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /examples/example1.js: -------------------------------------------------------------------------------- 1 | var sys = require('sys'); 2 | var libdtrace = require('libdtrace'); 3 | var dtp = new libdtrace.Consumer(); 4 | 5 | var prog = 'BEGIN { trace("hello world"); }\n' 6 | 7 | dtp.strcompile(prog); 8 | dtp.go(); 9 | 10 | dtp.consume(function (probe, rec) { 11 | if (rec) 12 | sys.puts(rec.data); 13 | }); 14 | -------------------------------------------------------------------------------- /examples/example2.js: -------------------------------------------------------------------------------- 1 | var sys = require('sys'); 2 | var libdtrace = require('libdtrace'); 3 | var dtp = new libdtrace.Consumer(); 4 | 5 | var prog = 'syscall:::entry { @[execname] = count(); }' 6 | 7 | dtp.strcompile(prog); 8 | dtp.go(); 9 | 10 | var syscalls = {}; 11 | var keys = []; 12 | 13 | var pad = function (val, len) 14 | { 15 | var rval = '', i, str = val + ''; 16 | 17 | for (i = 0; i < Math.abs(len) - str.length; i++) 18 | rval += ' '; 19 | 20 | rval = len < 0 ? str + rval : rval + str; 21 | 22 | return (rval); 23 | }; 24 | 25 | setInterval(function () { 26 | var i; 27 | 28 | sys.puts(pad('EXECNAME', -40) + pad('COUNT', -10)); 29 | 30 | dtp.aggwalk(function (id, key, val) { 31 | if (!syscalls.hasOwnProperty(key[0])) 32 | keys.push(key[0]); 33 | 34 | syscalls[key[0]] = val; 35 | }); 36 | 37 | keys.sort(); 38 | 39 | for (i = 0; i < keys.length; i++) { 40 | sys.puts(pad(keys[i], -40) + pad(syscalls[keys[i]], -10)); 41 | syscalls[keys[i]] = 0; 42 | } 43 | 44 | sys.puts(''); 45 | }, 1000); 46 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var libdtrace = require('bindings')('dtrace.node'); 2 | 3 | module.exports=libdtrace; 4 | -------------------------------------------------------------------------------- /libdtrace.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * For whatever reason, g++ on Solaris defines _XOPEN_SOURCE -- which in 3 | * turn will prevent us from pulling in our desired definition for boolean_t. 4 | * We don't need it, so explicitly undefine it. 5 | */ 6 | #ifdef _XOPEN_SOURCE 7 | #undef _XOPEN_SOURCE 8 | #endif 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | /* 20 | * Sadly, libelf refuses to compile if _FILE_OFFSET_BITS has been manually 21 | * jacked to 64 on a 32-bit compile. In this case, we just manually set it 22 | * back to 32. 23 | */ 24 | #if defined(_ILP32) && (_FILE_OFFSET_BITS != 32) 25 | #undef _FILE_OFFSET_BITS 26 | #define _FILE_OFFSET_BITS 32 27 | #endif 28 | 29 | #include 30 | 31 | /* 32 | * This is a tad unsightly: if we didn't find the definition of the 33 | * llquantize() aggregating action, we're going to redefine it here (along 34 | * with its support cast of macros). This allows node-libdtrace to operate 35 | * on a machine that has llquantize(), even if it was compiled on a machine 36 | * without the support. 37 | */ 38 | #ifndef DTRACEAGG_LLQUANTIZE 39 | 40 | #define DTRACEAGG_LLQUANTIZE (DTRACEACT_AGGREGATION + 9) 41 | 42 | #define DTRACE_LLQUANTIZE_FACTORSHIFT 48 43 | #define DTRACE_LLQUANTIZE_FACTORMASK ((uint64_t)UINT16_MAX << 48) 44 | #define DTRACE_LLQUANTIZE_LOWSHIFT 32 45 | #define DTRACE_LLQUANTIZE_LOWMASK ((uint64_t)UINT16_MAX << 32) 46 | #define DTRACE_LLQUANTIZE_HIGHSHIFT 16 47 | #define DTRACE_LLQUANTIZE_HIGHMASK ((uint64_t)UINT16_MAX << 16) 48 | #define DTRACE_LLQUANTIZE_NSTEPSHIFT 0 49 | #define DTRACE_LLQUANTIZE_NSTEPMASK UINT16_MAX 50 | 51 | #define DTRACE_LLQUANTIZE_FACTOR(x) \ 52 | (uint16_t)(((x) & DTRACE_LLQUANTIZE_FACTORMASK) >> \ 53 | DTRACE_LLQUANTIZE_FACTORSHIFT) 54 | 55 | #define DTRACE_LLQUANTIZE_LOW(x) \ 56 | (uint16_t)(((x) & DTRACE_LLQUANTIZE_LOWMASK) >> \ 57 | DTRACE_LLQUANTIZE_LOWSHIFT) 58 | 59 | #define DTRACE_LLQUANTIZE_HIGH(x) \ 60 | (uint16_t)(((x) & DTRACE_LLQUANTIZE_HIGHMASK) >> \ 61 | DTRACE_LLQUANTIZE_HIGHSHIFT) 62 | 63 | #define DTRACE_LLQUANTIZE_NSTEP(x) \ 64 | (uint16_t)(((x) & DTRACE_LLQUANTIZE_NSTEPMASK) >> \ 65 | DTRACE_LLQUANTIZE_NSTEPSHIFT) 66 | #endif 67 | 68 | using namespace v8; 69 | using std::string; 70 | using std::vector; 71 | 72 | class DTraceConsumer : node::ObjectWrap { 73 | public: 74 | static void Initialize(Handle target); 75 | 76 | protected: 77 | DTraceConsumer(); 78 | ~DTraceConsumer(); 79 | 80 | Handle error(const char *fmt, ...); 81 | Handle badarg(const char *msg); 82 | boolean_t valid(const dtrace_recdesc_t *); 83 | const char *action(const dtrace_recdesc_t *, char *, int); 84 | Local record(const dtrace_recdesc_t *, caddr_t); 85 | Local probedesc(const dtrace_probedesc_t *); 86 | 87 | Local *ranges_cached(dtrace_aggvarid_t); 88 | Local *ranges_cache(dtrace_aggvarid_t, Local *); 89 | Local *ranges_quantize(dtrace_aggvarid_t); 90 | Local *ranges_lquantize(dtrace_aggvarid_t, uint64_t); 91 | Local *ranges_llquantize(dtrace_aggvarid_t, uint64_t, int); 92 | 93 | static int consume(const dtrace_probedata_t *data, 94 | const dtrace_recdesc_t *rec, void *arg); 95 | static int aggwalk(const dtrace_aggdata_t *agg, void *arg); 96 | static int bufhandler(const dtrace_bufdata_t *bufdata, void *arg); 97 | 98 | static Handle New(const Arguments& args); 99 | static Handle Consume(const Arguments& args); 100 | static Handle Aggclear(const Arguments& args); 101 | static Handle Aggwalk(const Arguments& args); 102 | static Handle Aggmin(const Arguments& args); 103 | static Handle Aggmax(const Arguments& args); 104 | static Handle Strcompile(const Arguments& args); 105 | static Handle Setopt(const Arguments& args); 106 | static Handle Go(const Arguments& args); 107 | static Handle Stop(const Arguments& args); 108 | static Handle Version(const Arguments& args); 109 | 110 | private: 111 | dtrace_hdl_t *dtc_handle; 112 | static Persistent dtc_templ; 113 | const Arguments *dtc_args; 114 | Local dtc_callback; 115 | Handle dtc_error; 116 | Local *dtc_ranges; 117 | dtrace_aggvarid_t dtc_ranges_varid; 118 | }; 119 | 120 | Persistent DTraceConsumer::dtc_templ; 121 | 122 | DTraceConsumer::DTraceConsumer() : node::ObjectWrap() 123 | { 124 | int err; 125 | dtrace_hdl_t *dtp; 126 | 127 | if ((dtc_handle = dtp = dtrace_open(DTRACE_VERSION, 0, &err)) == NULL) 128 | throw (dtrace_errmsg(NULL, err)); 129 | 130 | /* 131 | * Set our buffer size and aggregation buffer size to the de facto 132 | * standard of 4M. 133 | */ 134 | (void) dtrace_setopt(dtp, "bufsize", "4m"); 135 | (void) dtrace_setopt(dtp, "aggsize", "4m"); 136 | 137 | if (dtrace_handle_buffered(dtp, DTraceConsumer::bufhandler, this) == -1) 138 | throw (dtrace_errmsg(dtp, dtrace_errno(dtp))); 139 | 140 | dtc_ranges = NULL; 141 | }; 142 | 143 | DTraceConsumer::~DTraceConsumer() 144 | { 145 | if (dtc_ranges != NULL) 146 | delete [] dtc_ranges; 147 | 148 | dtrace_close(dtc_handle); 149 | } 150 | 151 | void 152 | DTraceConsumer::Initialize(Handle target) 153 | { 154 | HandleScope scope; 155 | Local dtc = 156 | FunctionTemplate::New(DTraceConsumer::New); 157 | 158 | dtc_templ = Persistent::New(dtc); 159 | dtc_templ->InstanceTemplate()->SetInternalFieldCount(1); 160 | dtc_templ->SetClassName(String::NewSymbol("Consumer")); 161 | 162 | NODE_SET_PROTOTYPE_METHOD(dtc_templ, "strcompile", 163 | DTraceConsumer::Strcompile); 164 | NODE_SET_PROTOTYPE_METHOD(dtc_templ, "setopt", DTraceConsumer::Setopt); 165 | NODE_SET_PROTOTYPE_METHOD(dtc_templ, "go", DTraceConsumer::Go); 166 | NODE_SET_PROTOTYPE_METHOD(dtc_templ, "consume", 167 | DTraceConsumer::Consume); 168 | NODE_SET_PROTOTYPE_METHOD(dtc_templ, "aggwalk", 169 | DTraceConsumer::Aggwalk); 170 | NODE_SET_PROTOTYPE_METHOD(dtc_templ, "aggclear", 171 | DTraceConsumer::Aggclear); 172 | NODE_SET_PROTOTYPE_METHOD(dtc_templ, "aggmin", DTraceConsumer::Aggmin); 173 | NODE_SET_PROTOTYPE_METHOD(dtc_templ, "aggmax", DTraceConsumer::Aggmax); 174 | NODE_SET_PROTOTYPE_METHOD(dtc_templ, "stop", DTraceConsumer::Stop); 175 | NODE_SET_PROTOTYPE_METHOD(dtc_templ, "version", 176 | DTraceConsumer::Version); 177 | 178 | target->Set(String::NewSymbol("Consumer"), dtc_templ->GetFunction()); 179 | } 180 | 181 | Handle 182 | DTraceConsumer::New(const Arguments& args) 183 | { 184 | HandleScope scope; 185 | DTraceConsumer *dtc; 186 | 187 | try { 188 | dtc = new DTraceConsumer(); 189 | } catch (char const *msg) { 190 | return (ThrowException(Exception::Error(String::New(msg)))); 191 | } 192 | 193 | dtc->Wrap(args.Holder()); 194 | 195 | return (args.This()); 196 | } 197 | 198 | const char * 199 | DTraceConsumer::action(const dtrace_recdesc_t *rec, char *buf, int size) 200 | { 201 | static struct { 202 | dtrace_actkind_t action; 203 | const char *name; 204 | } act[] = { 205 | { DTRACEACT_NONE, "" }, 206 | { DTRACEACT_DIFEXPR, "" }, 207 | { DTRACEACT_EXIT, "exit()" }, 208 | { DTRACEACT_PRINTF, "printf()" }, 209 | { DTRACEACT_PRINTA, "printa()" }, 210 | { DTRACEACT_LIBACT, "" }, 211 | { DTRACEACT_USTACK, "ustack()" }, 212 | { DTRACEACT_JSTACK, "jstack()" }, 213 | { DTRACEACT_USYM, "usym()" }, 214 | { DTRACEACT_UMOD, "umod()" }, 215 | { DTRACEACT_UADDR, "uaddr()" }, 216 | { DTRACEACT_STOP, "stop()" }, 217 | { DTRACEACT_RAISE, "raise()" }, 218 | { DTRACEACT_SYSTEM, "system()" }, 219 | { DTRACEACT_FREOPEN, "freopen()" }, 220 | { DTRACEACT_STACK, "stack()" }, 221 | { DTRACEACT_SYM, "sym()" }, 222 | { DTRACEACT_MOD, "mod()" }, 223 | { DTRACEAGG_COUNT, "count()" }, 224 | { DTRACEAGG_MIN, "min()" }, 225 | { DTRACEAGG_MAX, "max()" }, 226 | { DTRACEAGG_AVG, "avg()" }, 227 | { DTRACEAGG_SUM, "sum()" }, 228 | { DTRACEAGG_STDDEV, "stddev()" }, 229 | { DTRACEAGG_QUANTIZE, "quantize()" }, 230 | { DTRACEAGG_LQUANTIZE, "lquantize()" }, 231 | { DTRACEAGG_LLQUANTIZE, "llquantize()" }, 232 | { DTRACEACT_NONE, NULL }, 233 | }; 234 | 235 | dtrace_actkind_t action = rec->dtrd_action; 236 | int i; 237 | 238 | for (i = 0; act[i].name != NULL; i++) { 239 | if (act[i].action == action) 240 | return (act[i].name); 241 | } 242 | 243 | (void) snprintf(buf, size, "", action); 244 | 245 | return (buf); 246 | } 247 | 248 | Handle 249 | DTraceConsumer::error(const char *fmt, ...) 250 | { 251 | char buf[1024], buf2[1024]; 252 | char *err = buf; 253 | va_list ap; 254 | 255 | va_start(ap, fmt); 256 | (void) vsnprintf(buf, sizeof (buf), fmt, ap); 257 | 258 | if (buf[strlen(buf) - 1] != '\n') { 259 | /* 260 | * If our error doesn't end in a new-line, we'll append the 261 | * strerror of errno. 262 | */ 263 | (void) snprintf(err = buf2, sizeof (buf2), 264 | "%s: %s", buf, strerror(errno)); 265 | } else { 266 | buf[strlen(buf) - 1] = '\0'; 267 | } 268 | 269 | return (ThrowException(Exception::Error(String::New(err)))); 270 | } 271 | 272 | Handle 273 | DTraceConsumer::badarg(const char *msg) 274 | { 275 | return (ThrowException(Exception::TypeError(String::New(msg)))); 276 | } 277 | 278 | boolean_t 279 | DTraceConsumer::valid(const dtrace_recdesc_t *rec) 280 | { 281 | dtrace_actkind_t action = rec->dtrd_action; 282 | 283 | switch (action) { 284 | case DTRACEACT_DIFEXPR: 285 | case DTRACEACT_SYM: 286 | case DTRACEACT_MOD: 287 | case DTRACEACT_USYM: 288 | case DTRACEACT_UMOD: 289 | case DTRACEACT_UADDR: 290 | return (B_TRUE); 291 | 292 | default: 293 | return (B_FALSE); 294 | } 295 | } 296 | 297 | Local 298 | DTraceConsumer::record(const dtrace_recdesc_t *rec, caddr_t addr) 299 | { 300 | switch (rec->dtrd_action) { 301 | case DTRACEACT_DIFEXPR: 302 | switch (rec->dtrd_size) { 303 | case sizeof (uint64_t): 304 | return (Number::New(*((int64_t *)addr))); 305 | 306 | case sizeof (uint32_t): 307 | return (Integer::New(*((int32_t *)addr))); 308 | 309 | case sizeof (uint16_t): 310 | return (Integer::New(*((uint16_t *)addr))); 311 | 312 | case sizeof (uint8_t): 313 | return (Integer::New(*((uint8_t *)addr))); 314 | 315 | default: 316 | return (String::New((const char *)addr)); 317 | } 318 | 319 | case DTRACEACT_SYM: 320 | case DTRACEACT_MOD: 321 | case DTRACEACT_USYM: 322 | case DTRACEACT_UMOD: 323 | case DTRACEACT_UADDR: 324 | dtrace_hdl_t *dtp = dtc_handle; 325 | char buf[2048], *tick, *plus; 326 | 327 | buf[0] = '\0'; 328 | 329 | if (DTRACEACT_CLASS(rec->dtrd_action) == DTRACEACT_KERNEL) { 330 | uint64_t pc = ((uint64_t *)addr)[0]; 331 | dtrace_addr2str(dtp, pc, buf, sizeof (buf) - 1); 332 | } else { 333 | uint64_t pid = ((uint64_t *)addr)[0]; 334 | uint64_t pc = ((uint64_t *)addr)[1]; 335 | dtrace_uaddr2str(dtp, pid, pc, buf, sizeof (buf) - 1); 336 | } 337 | 338 | if (rec->dtrd_action == DTRACEACT_MOD || 339 | rec->dtrd_action == DTRACEACT_UMOD) { 340 | /* 341 | * If we're looking for the module name, we'll 342 | * return everything to the left of the left-most 343 | * tick -- or "" if there is none. 344 | */ 345 | if ((tick = strchr(buf, '`')) == NULL) 346 | return (String::New("")); 347 | 348 | *tick = '\0'; 349 | } else if (rec->dtrd_action == DTRACEACT_SYM || 350 | rec->dtrd_action == DTRACEACT_USYM) { 351 | /* 352 | * If we're looking for the symbol name, we'll 353 | * return everything to the left of the right-most 354 | * plus sign (if there is one). 355 | */ 356 | if ((plus = strrchr(buf, '+')) != NULL) 357 | *plus = '\0'; 358 | } 359 | 360 | return (String::New(buf)); 361 | } 362 | 363 | assert(B_FALSE); 364 | return (Integer::New(-1)); 365 | } 366 | 367 | Handle 368 | DTraceConsumer::Strcompile(const Arguments& args) 369 | { 370 | DTraceConsumer *dtc = ObjectWrap::Unwrap(args.Holder()); 371 | dtrace_hdl_t *dtp = dtc->dtc_handle; 372 | dtrace_prog_t *dp; 373 | dtrace_proginfo_t info; 374 | 375 | if (args.Length() < 1 || !args[0]->IsString()) 376 | return (dtc->badarg("expected program")); 377 | 378 | String::Utf8Value program(args[0]->ToString()); 379 | 380 | if ((dp = dtrace_program_strcompile(dtp, *program, 381 | DTRACE_PROBESPEC_NAME, 0, 0, NULL)) == NULL) { 382 | return (dtc->error("couldn't compile '%s': %s\n", *program, 383 | dtrace_errmsg(dtp, dtrace_errno(dtp)))); 384 | } 385 | 386 | if (dtrace_program_exec(dtp, dp, &info) == -1) { 387 | return (dtc->error("couldn't execute '%s': %s\n", *program, 388 | dtrace_errmsg(dtp, dtrace_errno(dtp)))); 389 | } 390 | 391 | return (Undefined()); 392 | } 393 | 394 | Handle 395 | DTraceConsumer::Setopt(const Arguments& args) 396 | { 397 | DTraceConsumer *dtc = ObjectWrap::Unwrap(args.Holder()); 398 | dtrace_hdl_t *dtp = dtc->dtc_handle; 399 | dtrace_prog_t *dp; 400 | dtrace_proginfo_t info; 401 | int rval; 402 | 403 | if (args.Length() < 1 || !args[0]->IsString()) 404 | return (dtc->badarg("expected an option to set")); 405 | 406 | String::Utf8Value option(args[0]->ToString()); 407 | 408 | if (args.Length() >= 2) { 409 | if (args[1]->IsArray()) 410 | return (dtc->badarg("option value can't be an array")); 411 | 412 | if (args[1]->IsObject()) 413 | return (dtc->badarg("option value can't be an object")); 414 | 415 | String::Utf8Value optval(args[1]->ToString()); 416 | rval = dtrace_setopt(dtp, *option, *optval); 417 | } else { 418 | rval = dtrace_setopt(dtp, *option, NULL); 419 | } 420 | 421 | if (rval != 0) { 422 | return (dtc->error("couldn't set option '%s': %s\n", *option, 423 | dtrace_errmsg(dtp, dtrace_errno(dtp)))); 424 | } 425 | 426 | return (Undefined()); 427 | } 428 | 429 | Handle 430 | DTraceConsumer::Go(const Arguments& args) 431 | { 432 | DTraceConsumer *dtc = ObjectWrap::Unwrap(args.Holder()); 433 | dtrace_hdl_t *dtp = dtc->dtc_handle; 434 | 435 | if (dtrace_go(dtp) == -1) { 436 | return (dtc->error("couldn't enable tracing: %s\n", 437 | dtrace_errmsg(dtp, dtrace_errno(dtp)))); 438 | } 439 | 440 | return (Undefined()); 441 | } 442 | 443 | Handle 444 | DTraceConsumer::Stop(const Arguments& args) 445 | { 446 | DTraceConsumer *dtc = ObjectWrap::Unwrap(args.Holder()); 447 | dtrace_hdl_t *dtp = dtc->dtc_handle; 448 | 449 | if (dtrace_stop(dtp) == -1) { 450 | return (dtc->error("couldn't disable tracing: %s\n", 451 | dtrace_errmsg(dtp, dtrace_errno(dtp)))); 452 | } 453 | 454 | return (Undefined()); 455 | } 456 | 457 | Local 458 | DTraceConsumer::probedesc(const dtrace_probedesc_t *pd) 459 | { 460 | Local probe = Object::New(); 461 | probe->Set(String::New("provider"), String::New(pd->dtpd_provider)); 462 | probe->Set(String::New("module"), String::New(pd->dtpd_mod)); 463 | probe->Set(String::New("function"), String::New(pd->dtpd_func)); 464 | probe->Set(String::New("name"), String::New(pd->dtpd_name)); 465 | 466 | return (probe); 467 | } 468 | 469 | int 470 | DTraceConsumer::bufhandler(const dtrace_bufdata_t *bufdata, void *arg) 471 | { 472 | dtrace_probedata_t *data = bufdata->dtbda_probe; 473 | const dtrace_recdesc_t *rec = bufdata->dtbda_recdesc; 474 | DTraceConsumer *dtc = (DTraceConsumer *)arg; 475 | 476 | if (rec == NULL || rec->dtrd_action != DTRACEACT_PRINTF) 477 | return (DTRACE_HANDLE_OK); 478 | 479 | Local probe = dtc->probedesc(data->dtpda_pdesc); 480 | Local record = Object::New(); 481 | record->Set(String::New("data"), String::New(bufdata->dtbda_buffered)); 482 | Local argv[2] = { probe, record }; 483 | 484 | dtc->dtc_callback->Call(dtc->dtc_args->This(), 2, argv); 485 | 486 | return (DTRACE_HANDLE_OK); 487 | } 488 | 489 | int 490 | DTraceConsumer::consume(const dtrace_probedata_t *data, 491 | const dtrace_recdesc_t *rec, void *arg) 492 | { 493 | DTraceConsumer *dtc = (DTraceConsumer *)arg; 494 | dtrace_probedesc_t *pd = data->dtpda_pdesc; 495 | Local datum; 496 | 497 | Local probe = dtc->probedesc(data->dtpda_pdesc); 498 | 499 | if (rec == NULL) { 500 | Local argv[1] = { probe }; 501 | dtc->dtc_callback->Call(dtc->dtc_args->This(), 1, argv); 502 | return (DTRACE_CONSUME_NEXT); 503 | } 504 | 505 | if (!dtc->valid(rec)) { 506 | char errbuf[256]; 507 | 508 | /* 509 | * If this is a printf(), we'll defer to the bufhandler. 510 | */ 511 | if (rec->dtrd_action == DTRACEACT_PRINTF) 512 | return (DTRACE_CONSUME_THIS); 513 | 514 | dtc->dtc_error = dtc->error("unsupported action %s " 515 | "in record for %s:%s:%s:%s\n", 516 | dtc->action(rec, errbuf, sizeof (errbuf)), 517 | pd->dtpd_provider, pd->dtpd_mod, 518 | pd->dtpd_func, pd->dtpd_name); 519 | return (DTRACE_CONSUME_ABORT); 520 | } 521 | 522 | Local record = Object::New(); 523 | record->Set(String::New("data"), dtc->record(rec, data->dtpda_data)); 524 | Local argv[2] = { probe, record }; 525 | 526 | dtc->dtc_callback->Call(dtc->dtc_args->This(), 2, argv); 527 | 528 | return (DTRACE_CONSUME_THIS); 529 | } 530 | 531 | Handle 532 | DTraceConsumer::Consume(const Arguments& args) 533 | { 534 | DTraceConsumer *dtc = ObjectWrap::Unwrap(args.Holder()); 535 | dtrace_hdl_t *dtp = dtc->dtc_handle; 536 | dtrace_workstatus_t status; 537 | 538 | if (!args[0]->IsFunction()) 539 | return (dtc->badarg("expected function as argument")); 540 | 541 | dtc->dtc_callback = Local::Cast(args[0]); 542 | dtc->dtc_args = &args; 543 | dtc->dtc_error = Null(); 544 | 545 | status = dtrace_work(dtp, NULL, NULL, DTraceConsumer::consume, dtc); 546 | 547 | if (status == -1 && !dtc->dtc_error->IsNull()) 548 | return (dtc->dtc_error); 549 | 550 | return (Undefined()); 551 | } 552 | 553 | /* 554 | * Caching the quantized ranges improves performance substantially if the 555 | * aggregations have many disjoing keys. Note that we only cache a single 556 | * aggregation variable; programs that have more than one aggregation variable 557 | * may see significant degradations in performance. (If this is a common 558 | * case, this cache should clearly be expanded.) 559 | */ 560 | Local * 561 | DTraceConsumer::ranges_cached(dtrace_aggvarid_t varid) 562 | { 563 | if (varid == dtc_ranges_varid) 564 | return (dtc_ranges); 565 | 566 | return (NULL); 567 | } 568 | 569 | Local * 570 | DTraceConsumer::ranges_cache(dtrace_aggvarid_t varid, Local *ranges) 571 | { 572 | if (dtc_ranges != NULL) 573 | delete [] dtc_ranges; 574 | 575 | dtc_ranges = ranges; 576 | dtc_ranges_varid = varid; 577 | 578 | return (ranges); 579 | } 580 | 581 | Local * 582 | DTraceConsumer::ranges_quantize(dtrace_aggvarid_t varid) 583 | { 584 | int64_t min, max; 585 | Local *ranges; 586 | int i; 587 | 588 | if ((ranges = ranges_cached(varid)) != NULL) 589 | return (ranges); 590 | 591 | ranges = new Local[DTRACE_QUANTIZE_NBUCKETS]; 592 | 593 | for (i = 0; i < DTRACE_QUANTIZE_NBUCKETS; i++) { 594 | ranges[i] = Array::New(2); 595 | 596 | if (i < DTRACE_QUANTIZE_ZEROBUCKET) { 597 | /* 598 | * If we're less than the zero bucket, our range 599 | * extends from negative infinity through to the 600 | * beginning of our zeroth bucket. 601 | */ 602 | min = i > 0 ? DTRACE_QUANTIZE_BUCKETVAL(i - 1) + 1 : 603 | INT64_MIN; 604 | max = DTRACE_QUANTIZE_BUCKETVAL(i); 605 | } else if (i == DTRACE_QUANTIZE_ZEROBUCKET) { 606 | min = max = 0; 607 | } else { 608 | min = DTRACE_QUANTIZE_BUCKETVAL(i); 609 | max = i < DTRACE_QUANTIZE_NBUCKETS - 1 ? 610 | DTRACE_QUANTIZE_BUCKETVAL(i + 1) - 1 : 611 | INT64_MAX; 612 | } 613 | 614 | ranges[i]->Set(0, Number::New(min)); 615 | ranges[i]->Set(1, Number::New(max)); 616 | } 617 | 618 | return (ranges_cache(varid, ranges)); 619 | } 620 | 621 | Local * 622 | DTraceConsumer::ranges_lquantize(dtrace_aggvarid_t varid, 623 | const uint64_t arg) 624 | { 625 | int64_t min, max; 626 | Local *ranges; 627 | int32_t base; 628 | uint16_t step, levels; 629 | int i; 630 | 631 | if ((ranges = ranges_cached(varid)) != NULL) 632 | return (ranges); 633 | 634 | base = DTRACE_LQUANTIZE_BASE(arg); 635 | step = DTRACE_LQUANTIZE_STEP(arg); 636 | levels = DTRACE_LQUANTIZE_LEVELS(arg); 637 | 638 | ranges = new Local[levels + 2]; 639 | 640 | for (i = 0; i <= levels + 1; i++) { 641 | ranges[i] = Array::New(2); 642 | 643 | min = i == 0 ? INT64_MIN : base + ((i - 1) * step); 644 | max = i > levels ? INT64_MAX : base + (i * step) - 1; 645 | 646 | ranges[i]->Set(0, Number::New(min)); 647 | ranges[i]->Set(1, Number::New(max)); 648 | } 649 | 650 | return (ranges_cache(varid, ranges)); 651 | } 652 | 653 | Local * 654 | DTraceConsumer::ranges_llquantize(dtrace_aggvarid_t varid, 655 | const uint64_t arg, int nbuckets) 656 | { 657 | int64_t value = 1, next, step; 658 | Local *ranges; 659 | int bucket = 0, order; 660 | uint16_t factor, low, high, nsteps; 661 | 662 | if ((ranges = ranges_cached(varid)) != NULL) 663 | return (ranges); 664 | 665 | factor = DTRACE_LLQUANTIZE_FACTOR(arg); 666 | low = DTRACE_LLQUANTIZE_LOW(arg); 667 | high = DTRACE_LLQUANTIZE_HIGH(arg); 668 | nsteps = DTRACE_LLQUANTIZE_NSTEP(arg); 669 | 670 | ranges = new Local[nbuckets]; 671 | 672 | for (order = 0; order < low; order++) 673 | value *= factor; 674 | 675 | ranges[bucket] = Array::New(2); 676 | ranges[bucket]->Set(0, Number::New(0)); 677 | ranges[bucket]->Set(1, Number::New(value - 1)); 678 | bucket++; 679 | 680 | next = value * factor; 681 | step = next > nsteps ? next / nsteps : 1; 682 | 683 | while (order <= high) { 684 | ranges[bucket] = Array::New(2); 685 | ranges[bucket]->Set(0, Number::New(value)); 686 | ranges[bucket]->Set(1, Number::New(value + step - 1)); 687 | bucket++; 688 | 689 | if ((value += step) != next) 690 | continue; 691 | 692 | next = value * factor; 693 | step = next > nsteps ? next / nsteps : 1; 694 | order++; 695 | } 696 | 697 | ranges[bucket] = Array::New(2); 698 | ranges[bucket]->Set(0, Number::New(value)); 699 | ranges[bucket]->Set(1, Number::New(INT64_MAX)); 700 | 701 | assert(bucket + 1 == nbuckets); 702 | 703 | return (ranges_cache(varid, ranges)); 704 | } 705 | 706 | int 707 | DTraceConsumer::aggwalk(const dtrace_aggdata_t *agg, void *arg) 708 | { 709 | DTraceConsumer *dtc = (DTraceConsumer *)arg; 710 | const dtrace_aggdesc_t *aggdesc = agg->dtada_desc; 711 | const dtrace_recdesc_t *aggrec; 712 | Local id = Integer::New(aggdesc->dtagd_varid), val; 713 | Local key; 714 | char errbuf[256]; 715 | int i; 716 | 717 | /* 718 | * We expect to have both a variable ID and an aggregation value here; 719 | * if we have fewer than two records, something is deeply wrong. 720 | */ 721 | assert(aggdesc->dtagd_nrecs >= 2); 722 | key = Array::New(aggdesc->dtagd_nrecs - 2); 723 | 724 | for (i = 1; i < aggdesc->dtagd_nrecs - 1; i++) { 725 | const dtrace_recdesc_t *rec = &aggdesc->dtagd_rec[i]; 726 | caddr_t addr = agg->dtada_data + rec->dtrd_offset; 727 | Local datum; 728 | 729 | if (!dtc->valid(rec)) { 730 | dtc->dtc_error = dtc->error("unsupported action %s " 731 | "as key #%d in aggregation \"%s\"\n", 732 | dtc->action(rec, errbuf, sizeof (errbuf)), i, 733 | aggdesc->dtagd_name); 734 | return (DTRACE_AGGWALK_ERROR); 735 | } 736 | 737 | key->Set(i - 1, dtc->record(rec, addr)); 738 | } 739 | 740 | aggrec = &aggdesc->dtagd_rec[aggdesc->dtagd_nrecs - 1]; 741 | 742 | switch (aggrec->dtrd_action) { 743 | case DTRACEAGG_COUNT: 744 | case DTRACEAGG_MIN: 745 | case DTRACEAGG_MAX: 746 | case DTRACEAGG_SUM: { 747 | caddr_t addr = agg->dtada_data + aggrec->dtrd_offset; 748 | 749 | assert(aggrec->dtrd_size == sizeof (uint64_t)); 750 | val = Number::New(*((int64_t *)addr)); 751 | break; 752 | } 753 | 754 | case DTRACEAGG_AVG: { 755 | const int64_t *data = (int64_t *)(agg->dtada_data + 756 | aggrec->dtrd_offset); 757 | 758 | assert(aggrec->dtrd_size == sizeof (uint64_t) * 2); 759 | val = Number::New(data[1] / (double)data[0]); 760 | break; 761 | } 762 | 763 | case DTRACEAGG_QUANTIZE: { 764 | Local quantize = Array::New(); 765 | const int64_t *data = (int64_t *)(agg->dtada_data + 766 | aggrec->dtrd_offset); 767 | Local *ranges, datum; 768 | int i, j = 0; 769 | 770 | ranges = dtc->ranges_quantize(aggdesc->dtagd_varid); 771 | 772 | for (i = 0; i < DTRACE_QUANTIZE_NBUCKETS; i++) { 773 | if (!data[i]) 774 | continue; 775 | 776 | datum = Array::New(2); 777 | datum->Set(0, ranges[i]); 778 | datum->Set(1, Number::New(data[i])); 779 | 780 | quantize->Set(j++, datum); 781 | } 782 | 783 | val = quantize; 784 | break; 785 | } 786 | 787 | case DTRACEAGG_LQUANTIZE: 788 | case DTRACEAGG_LLQUANTIZE: { 789 | Local lquantize = Array::New(); 790 | const int64_t *data = (int64_t *)(agg->dtada_data + 791 | aggrec->dtrd_offset); 792 | Local *ranges, datum; 793 | int i, j = 0; 794 | 795 | uint64_t arg = *data++; 796 | int levels = (aggrec->dtrd_size / sizeof (uint64_t)) - 1; 797 | 798 | ranges = (aggrec->dtrd_action == DTRACEAGG_LQUANTIZE ? 799 | dtc->ranges_lquantize(aggdesc->dtagd_varid, arg) : 800 | dtc->ranges_llquantize(aggdesc->dtagd_varid, arg, levels)); 801 | 802 | for (i = 0; i < levels; i++) { 803 | if (!data[i]) 804 | continue; 805 | 806 | datum = Array::New(2); 807 | datum->Set(0, ranges[i]); 808 | datum->Set(1, Number::New(data[i])); 809 | 810 | lquantize->Set(j++, datum); 811 | } 812 | 813 | val = lquantize; 814 | break; 815 | } 816 | 817 | default: 818 | dtc->dtc_error = dtc->error("unsupported aggregating action " 819 | " %s in aggregation \"%s\"\n", dtc->action(aggrec, errbuf, 820 | sizeof (errbuf)), aggdesc->dtagd_name); 821 | return (DTRACE_AGGWALK_ERROR); 822 | } 823 | 824 | Local argv[3] = { id, key, val }; 825 | dtc->dtc_callback->Call(dtc->dtc_args->This(), 3, argv); 826 | 827 | return (DTRACE_AGGWALK_REMOVE); 828 | } 829 | 830 | Handle 831 | DTraceConsumer::Aggclear(const Arguments& args) 832 | { 833 | HandleScope scope; 834 | DTraceConsumer *dtc = ObjectWrap::Unwrap(args.Holder()); 835 | dtrace_hdl_t *dtp = dtc->dtc_handle; 836 | 837 | if (dtrace_status(dtp) == -1) { 838 | return (dtc->error("couldn't get status: %s\n", 839 | dtrace_errmsg(dtp, dtrace_errno(dtp)))); 840 | } 841 | 842 | dtrace_aggregate_clear(dtp); 843 | return (Undefined()); 844 | } 845 | 846 | Handle 847 | DTraceConsumer::Aggwalk(const Arguments& args) 848 | { 849 | HandleScope scope; 850 | DTraceConsumer *dtc = ObjectWrap::Unwrap(args.Holder()); 851 | dtrace_hdl_t *dtp = dtc->dtc_handle; 852 | int rval; 853 | 854 | if (!args[0]->IsFunction()) 855 | return (dtc->badarg("expected function as argument")); 856 | 857 | dtc->dtc_callback = Local::Cast(args[0]); 858 | dtc->dtc_args = &args; 859 | dtc->dtc_error = Null(); 860 | 861 | if (dtrace_status(dtp) == -1) { 862 | return (dtc->error("couldn't get status: %s\n", 863 | dtrace_errmsg(dtp, dtrace_errno(dtp)))); 864 | } 865 | 866 | if (dtrace_aggregate_snap(dtp) == -1) { 867 | return (dtc->error("couldn't snap aggregate: %s\n", 868 | dtrace_errmsg(dtp, dtrace_errno(dtp)))); 869 | } 870 | 871 | rval = dtrace_aggregate_walk(dtp, DTraceConsumer::aggwalk, dtc); 872 | 873 | /* 874 | * Flush the ranges cache; the ranges will go out of scope when the 875 | * destructor for our HandleScope is called, and we cannot be left 876 | * holding references. 877 | */ 878 | dtc->ranges_cache(DTRACE_AGGVARIDNONE, NULL); 879 | 880 | if (rval == -1) { 881 | if (!dtc->dtc_error->IsNull()) 882 | return (dtc->dtc_error); 883 | 884 | return (dtc->error("couldn't walk aggregate: %s\n", 885 | dtrace_errmsg(dtp, dtrace_errno(dtp)))); 886 | } 887 | 888 | return (Undefined()); 889 | } 890 | 891 | Handle 892 | DTraceConsumer::Aggmin(const Arguments& args) 893 | { 894 | return (Number::New(INT64_MIN)); 895 | } 896 | 897 | Handle 898 | DTraceConsumer::Aggmax(const Arguments& args) 899 | { 900 | return (Number::New(INT64_MAX)); 901 | } 902 | 903 | Handle 904 | DTraceConsumer::Version(const Arguments& args) 905 | { 906 | return (String::New(_dtrace_version)); 907 | } 908 | 909 | extern "C" void 910 | init (Handle target) 911 | { 912 | DTraceConsumer::Initialize(target); 913 | } 914 | 915 | NODE_MODULE(dtrace, init); 916 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "libdtrace", 3 | "version": "0.0.3", 4 | "description": "Solaris libdtrace bindings", 5 | "homepage": "https://github.com/bcantrill/node-libdtrace", 6 | "author": "Joyent (joyent.com)", 7 | "engines": { "node": ">=0.8" }, 8 | "main": "./index.js", 9 | "dependencies": { 10 | "bindings": "1.0.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/test-aggregation.js: -------------------------------------------------------------------------------- 1 | var sys = require('sys'); 2 | var libdtrace = require('libdtrace'); 3 | var assert = require('assert'); 4 | 5 | dtp = new libdtrace.Consumer(); 6 | dtp.strcompile('BEGIN { @["foo", "bar", 9904, 61707] = count(); }'); 7 | 8 | dtp.go(); 9 | 10 | dtp.aggwalk(function (varid, key, val) { 11 | assert.equal(varid, 1); 12 | assert.ok(key instanceof Array, 'expected key to be an array'); 13 | assert.equal(key.length, 4); 14 | assert.equal(key[0], "foo"); 15 | assert.equal(key[1], "bar"); 16 | assert.equal(key[2], 9904); 17 | assert.equal(key[3], 61707); 18 | assert.equal(val, 1); 19 | }); 20 | 21 | dtp.aggwalk(function (varid, key, val) { 22 | assert.ok(false, 'did not expect to find aggregation contents'); 23 | }); 24 | 25 | dtp = new libdtrace.Consumer(); 26 | 27 | var lq = function (val) 28 | { 29 | return (val + ', 3, 7, 3') 30 | }; 31 | 32 | aggacts = { 33 | max: { args: [ '10', '20' ], expected: 20 }, 34 | min: { args: [ '10', '20' ], expected: 10 }, 35 | count: { args: [ '', '' ], expected: 2 }, 36 | sum: { args: [ '10', '20' ], expected: 30 }, 37 | avg: { args: [ '30', '1' ], expected: 15.5 }, 38 | quantize: { args: [ '2', '4', '5', '8' ], expected: [ 39 | [ [ 2, 3 ], 1 ], 40 | [ [ 4, 7 ], 2 ], 41 | [ [ 8, 15 ], 1 ] 42 | ] }, 43 | lquantize: { args: [ lq(2), lq(4), lq(5), lq(8) ], expected: [ 44 | [ [ dtp.aggmin(), 2 ], 1 ], 45 | [ [ 3, 5 ], 2 ], 46 | [ [ 6, dtp.aggmax() ], 1 ] 47 | ] } 48 | }; 49 | 50 | varids = [ '' ]; 51 | prog = 'BEGIN\n{\n'; 52 | 53 | for (act in aggacts) { 54 | varids.push(act); 55 | 56 | for (i = 0; i < aggacts[act].args.length; i++) { 57 | prog += '\t@agg' + act + ' = ' + act + '(' + 58 | aggacts[act].args[i] + ');\n'; 59 | } 60 | } 61 | 62 | prog += '}\n'; 63 | 64 | dtp.strcompile(prog); 65 | 66 | dtp.go(); 67 | 68 | dtp.aggwalk(function (varid, key, val) { 69 | assert.ok(varids[varid], 'invalid variable ID ' + varid); 70 | assert.ok(aggacts[varids[varid]], 'unknown variable ID ' + varid); 71 | 72 | act = aggacts[varids[varid]]; 73 | assert.deepEqual(act.expected, val); 74 | }); 75 | 76 | -------------------------------------------------------------------------------- /tests/test-basic.js: -------------------------------------------------------------------------------- 1 | var sys = require('sys'); 2 | var libdtrace = require('libdtrace'); 3 | var assert = require('assert'); 4 | 5 | dtp = new libdtrace.Consumer(); 6 | assert.throws(function () { dtp.strcompile(); }); 7 | assert.throws(function () { dtp.strcompile(61707); }); 8 | assert.throws(function () { dtp.strcompile('this is not D'); }); 9 | assert.throws(function () { dtp.strcompile('bogus-probe { trace(0); }') }); 10 | dtp.strcompile('BEGIN { trace(9904); }'); 11 | 12 | assert.throws(function () { dtp.setopt() }); 13 | assert.throws(function () { dtp.setopt('bogusoption') }); 14 | assert.throws(function () { dtp.setopt('bufpolicy') }); 15 | assert.throws(function () { dtp.setopt('bufpolicy', 100) }); 16 | 17 | dtp.setopt('bufpolicy', 'ring'); 18 | dtp.setopt('bufpolicy', 'switch'); 19 | 20 | seen = false; 21 | lastrec = false; 22 | 23 | dtp.go(); 24 | 25 | dtp.consume(function testbasic (probe, rec) { 26 | assert.equal(probe.provider, 'dtrace'); 27 | assert.equal(probe.module, ''); 28 | assert.equal(probe.function, ''); 29 | assert.equal(probe.name, 'BEGIN'); 30 | 31 | if (!rec) { 32 | assert.ok(seen); 33 | lastrec = true; 34 | } else { 35 | seen = true; 36 | assert.equal(rec.data, 9904); 37 | } 38 | }); 39 | 40 | assert.ok(seen, 'did not consume expected record'); 41 | assert.ok(lastrec, 'did not see delineator between EPIDs'); 42 | assert.throws(function () { dtp.go(); }); 43 | assert.throws(function () { dtp.strcompile('BEGIN { trace(0); }') }); 44 | 45 | dtp.stop(); 46 | 47 | /* 48 | * Now test that END clauses work properly. 49 | */ 50 | dtp = new libdtrace.Consumer(); 51 | dtp.strcompile('END { trace(61707); }'); 52 | 53 | dtp.go(); 54 | 55 | seen = false; 56 | 57 | dtp.consume(function testend (probe, rec) { 58 | assert.ok(false); 59 | }); 60 | 61 | dtp.stop(); 62 | 63 | dtp.consume(function testend_consume (probe, rec) { 64 | assert.equal(probe.provider, 'dtrace'); 65 | assert.equal(probe.module, ''); 66 | assert.equal(probe.function, ''); 67 | assert.equal(probe.name, 'END'); 68 | 69 | if (!rec) 70 | return; 71 | 72 | assert.equal(rec.data, 61707); 73 | }); 74 | 75 | dtp = new libdtrace.Consumer(); 76 | dtp.strcompile('tick-1sec { trace(i++); }'); 77 | dtp.go(); 78 | secs = 0; 79 | val = 0; 80 | 81 | id = setInterval(function testtick () { 82 | assert.ok(secs++ < 10, 'failed to terminate (val is ' + val + ')'); 83 | 84 | dtp.consume(function testtick_consume (probe, rec) { 85 | if (!rec) 86 | return; 87 | 88 | if ((val = rec.data) > 5) 89 | clearInterval(id); 90 | 91 | sys.puts(sys.inspect(rec)); 92 | }); 93 | }, 1000); 94 | 95 | -------------------------------------------------------------------------------- /tests/test-footprint.js: -------------------------------------------------------------------------------- 1 | var sys = require('sys'); 2 | var libdtrace = require('libdtrace'); 3 | var assert = require('assert'); 4 | 5 | var tests = [ 6 | 'tick-1000hz { @a = quantize(0); @b = quantize(0) }', 7 | 'tick-1000hz { @a = quantize(rand()); }', 8 | 'tick-1000hz { @a = lquantize(0, 0, 100, 1); }', 9 | 'tick-1000hz { @a = lquantize(0, 0, 100, 1); @b = lquantize(10, -10, 10); }', 10 | ]; 11 | 12 | var pad = function (val, len) 13 | { 14 | var rval = '', i; 15 | var str = val + ''; 16 | 17 | for (i = 0; i < len - str.length; i++) 18 | rval += ' '; 19 | 20 | rval += str; 21 | 22 | return (rval); 23 | }; 24 | 25 | var seconds = -1; 26 | var end = 30; 27 | var dtp = undefined; 28 | var dumped = -1; 29 | 30 | setInterval(function () { 31 | if (!dtp) { 32 | if (!tests.length) 33 | process.exit(0); 34 | 35 | test = tests.shift(); 36 | start = new Date().valueOf(); 37 | 38 | dtp = new libdtrace.Consumer(); 39 | 40 | sys.puts(seconds != -1 ? '\n' : ''); 41 | sys.puts('Testing heap consumption of:\n ' + test + '\n'); 42 | sys.puts(pad('SECONDS', 9) + pad('HEAP-USED', 20) + 43 | pad('HEAP-TOTAL', 20)); 44 | dtp.strcompile(test); 45 | dtp.setopt('aggrate', '5000hz'); 46 | dtp.go(); 47 | } 48 | 49 | dtp.aggwalk(function (varid, key, val) {}); 50 | 51 | seconds = Math.floor((new Date().valueOf() - start) / 1000); 52 | 53 | if (seconds != dumped && seconds % 5 == 0) { 54 | dumped = seconds; 55 | var usage = process.memoryUsage(); 56 | 57 | sys.puts(pad(seconds, 9) + pad(usage.heapUsed, 20) + 58 | pad(usage.heapTotal, 20)); 59 | } 60 | 61 | if (seconds >= end) { 62 | delete dtp; 63 | dtp = undefined; 64 | } 65 | }, 10); 66 | -------------------------------------------------------------------------------- /tests/test-llquantize.js: -------------------------------------------------------------------------------- 1 | var sys = require('sys'); 2 | var libdtrace = require('libdtrace'); 3 | var assert = require('assert'); 4 | 5 | dtp = new libdtrace.Consumer(); 6 | 7 | ver = dtp.version().split(' ')[2].split('.'); 8 | 9 | if (parseInt(ver[0]) == 1 && parseInt(ver[1]) <= 8) { 10 | sys.puts('llquantize() not present in version ' + dtp.version() + 11 | '; not testing.'); 12 | process.exit(0); 13 | } 14 | 15 | prog = 'BEGIN\n{\n'; 16 | 17 | for (i = 0; i < 101; i++) 18 | prog += '\t@ = llquantize(' + i + ', 10, 0, 1, 20);\n'; 19 | 20 | prog += '}\n'; 21 | 22 | dtp.strcompile(prog); 23 | 24 | dtp.go(); 25 | 26 | dtp.aggwalk(function (varid,key, val) { 27 | var expected = [ 28 | [ [ 0, 0 ], 1 ], 29 | [ [ 1, 1 ], 1 ], 30 | [ [ 2, 2 ], 1 ], 31 | [ [ 3, 3 ], 1 ], 32 | [ [ 4, 4 ], 1 ], 33 | [ [ 5, 5 ], 1 ], 34 | [ [ 6, 6 ], 1 ], 35 | [ [ 7, 7 ], 1 ], 36 | [ [ 8, 8 ], 1 ], 37 | [ [ 9, 9 ], 1 ], 38 | [ [ 10, 14 ], 5 ], 39 | [ [ 15, 19 ], 5 ], 40 | [ [ 20, 24 ], 5 ], 41 | [ [ 25, 29 ], 5 ], 42 | [ [ 30, 34 ], 5 ], 43 | [ [ 35, 39 ], 5 ], 44 | [ [ 40, 44 ], 5 ], 45 | [ [ 45, 49 ], 5 ], 46 | [ [ 50, 54 ], 5 ], 47 | [ [ 55, 59 ], 5 ], 48 | [ [ 60, 64 ], 5 ], 49 | [ [ 65, 69 ], 5 ], 50 | [ [ 70, 74 ], 5 ], 51 | [ [ 75, 79 ], 5 ], 52 | [ [ 80, 84 ], 5 ], 53 | [ [ 85, 89 ], 5 ], 54 | [ [ 90, 94 ], 5 ], 55 | [ [ 95, 99 ], 5 ], 56 | [ [ 100, 9223372036854776000 ], 1 ], 57 | ]; 58 | 59 | assert.deepEqual(val, expected); 60 | }); 61 | 62 | dtp = new libdtrace.Consumer(); 63 | 64 | prog = 'BEGIN\n{\n'; 65 | 66 | for (i = 0; i < 10100; i += 50) 67 | prog += '\t@ = llquantize(' + i + ', 10, 2, 3, 10);\n'; 68 | 69 | prog += '}\n'; 70 | 71 | dtp.strcompile(prog); 72 | 73 | dtp.go(); 74 | 75 | dtp.aggwalk(function (varid, key, val) { 76 | var expected = 77 | [ [ [ 0, 99 ], 2 ], 78 | [ [ 100, 199 ], 2 ], 79 | [ [ 200, 299 ], 2 ], 80 | [ [ 300, 399 ], 2 ], 81 | [ [ 400, 499 ], 2 ], 82 | [ [ 500, 599 ], 2 ], 83 | [ [ 600, 699 ], 2 ], 84 | [ [ 700, 799 ], 2 ], 85 | [ [ 800, 899 ], 2 ], 86 | [ [ 900, 999 ], 2 ], 87 | [ [ 1000, 1999 ], 20 ], 88 | [ [ 2000, 2999 ], 20 ], 89 | [ [ 3000, 3999 ], 20 ], 90 | [ [ 4000, 4999 ], 20 ], 91 | [ [ 5000, 5999 ], 20 ], 92 | [ [ 6000, 6999 ], 20 ], 93 | [ [ 7000, 7999 ], 20 ], 94 | [ [ 8000, 8999 ], 20 ], 95 | [ [ 9000, 9999 ], 20 ], 96 | [ [ 10000, 9223372036854776000 ], 2 ] 97 | ]; 98 | 99 | assert.deepEqual(val, expected); 100 | }); 101 | 102 | -------------------------------------------------------------------------------- /tests/test-lquantize.js: -------------------------------------------------------------------------------- 1 | var sys = require('sys'); 2 | var libdtrace = require('libdtrace'); 3 | var assert = require('assert'); 4 | 5 | dtp = new libdtrace.Consumer(); 6 | 7 | prog = 'BEGIN\n{\n'; 8 | 9 | for (i = -5; i < 15; i++) 10 | prog += '\t@ = lquantize(' + i + ', 0, 10, 2);\n'; 11 | 12 | prog += '}\n'; 13 | 14 | sys.puts(prog); 15 | 16 | dtp.strcompile(prog); 17 | 18 | dtp.go(); 19 | 20 | dtp.aggwalk(function (varid, key, val) { 21 | sys.puts(sys.inspect(val)); 22 | }); 23 | 24 | dtp = new libdtrace.Consumer(); 25 | 26 | prog = 'BEGIN\n{\n'; 27 | 28 | for (i = -100; i < 100; i++) 29 | prog += '\t@ = lquantize(' + i + ', -200, 200, 10);\n'; 30 | 31 | prog += '}\n'; 32 | 33 | dtp.strcompile(prog); 34 | 35 | dtp.go(); 36 | 37 | dtp.aggwalk(function (varid, key, val) { 38 | sys.puts(sys.inspect(val)); 39 | }); 40 | 41 | delete dtp; 42 | 43 | -------------------------------------------------------------------------------- /tests/test-options.js: -------------------------------------------------------------------------------- 1 | var sys = require('sys'); 2 | var libdtrace = require('libdtrace'); 3 | var assert = require('assert'); 4 | 5 | dtp = new libdtrace.Consumer(); 6 | 7 | assert.throws(function () { dtp.setopt() }); 8 | assert.throws(function () { dtp.setopt('bogusoption') }); 9 | assert.throws(function () { dtp.setopt('bufpolicy') }); 10 | assert.throws(function () { dtp.setopt('bufpolicy', 100) }); 11 | dtp.setopt('quiet', true); 12 | assert.throws(function () { dtp.setopt('bufpolicy', true); }); 13 | assert.throws(function () { dtp.setopt('dynvarsize', true); }); 14 | assert.throws(function () { dtp.setopt('dynvarsize', 1.23); }); 15 | dtp.setopt('quiet', 1024); 16 | assert.throws(function () { dtp.setopt('quiet', { foo: 1024 }); }); 17 | assert.throws(function () { dtp.setopt('quiet', [ false ]); }); 18 | assert.throws(function () { dtp.setopt('quiet', 1.024); }); 19 | assert.throws(function () { dtp.setopt('quiet', 1.024); }); 20 | assert.throws(function () { dtp.setopt('quiet', new Date); }); 21 | dtp.setopt('quiet'); 22 | dtp.setopt('quiet', true); 23 | dtp.setopt('quiet', false); 24 | dtp.setopt('quiet', '1'); 25 | dtp.setopt('quiet', '0'); 26 | 27 | 28 | -------------------------------------------------------------------------------- /tests/test-printf.js: -------------------------------------------------------------------------------- 1 | var sys = require('sys'); 2 | var libdtrace = require('libdtrace'); 3 | var assert = require('assert'); 4 | 5 | dtp = new libdtrace.Consumer(); 6 | dtp.strcompile('BEGIN { printf("{ foo: %d", 123); printf(", bar: %d", 456); }'); 7 | 8 | dtp.go(); 9 | 10 | dtp.consume(function testbasic (probe, rec) { 11 | assert.equal(probe.provider, 'dtrace'); 12 | assert.equal(probe.module, ''); 13 | assert.equal(probe.function, ''); 14 | assert.equal(probe.name, 'BEGIN'); 15 | 16 | sys.puts(sys.inspect(probe)); 17 | sys.puts(sys.inspect(rec)); 18 | }); 19 | 20 | dtp.stop(); 21 | 22 | -------------------------------------------------------------------------------- /tests/test-quantize.js: -------------------------------------------------------------------------------- 1 | var sys = require('sys'); 2 | var libdtrace = require('libdtrace'); 3 | var assert = require('assert'); 4 | 5 | dtp = new libdtrace.Consumer(); 6 | 7 | prog = 'BEGIN\n{\n'; 8 | 9 | for (i = -32; i < 32; i++) 10 | prog += '\t@ = quantize(' + i + ');\n'; 11 | 12 | prog += '}\n'; 13 | 14 | sys.puts(prog); 15 | 16 | dtp.strcompile(prog); 17 | 18 | dtp.go(); 19 | 20 | dtp.aggwalk(function (varid, key, val) { 21 | var expected = [ 22 | [ [ -63, -32 ], 1 ], 23 | [ [ -31, -16 ], 16 ], 24 | [ [ -15, -8 ], 8 ], 25 | [ [ -7, -4 ], 4 ], 26 | [ [ -3, -2 ], 2 ], 27 | [ [ -1, -1 ], 1 ], 28 | [ [ 0, 0 ], 1 ], 29 | [ [ 1, 1 ], 1 ], 30 | [ [ 2, 3 ], 2 ], 31 | [ [ 4, 7 ], 4 ], 32 | [ [ 8, 15 ], 8 ], 33 | [ [ 16, 31 ], 16 ], 34 | ]; 35 | 36 | assert.equal(varid, 1); 37 | assert.ok(key instanceof Array, 'expected key to be an array'); 38 | assert.equal(key.length, 0); 39 | assert.ok(val instanceof Array, 'expected val to be an array'); 40 | assert.deepEqual(expected, val); 41 | sys.puts(sys.inspect(val)); 42 | }); 43 | 44 | delete dtp; 45 | 46 | -------------------------------------------------------------------------------- /tests/test-sched.js: -------------------------------------------------------------------------------- 1 | var util = require('util'); 2 | var libdtrace = require('libdtrace'); 3 | var i; 4 | 5 | dtp = new libdtrace.Consumer(); 6 | 7 | prog = 8 | 'sched:::on-cpu\n' + 9 | '{\n' + 10 | ' self->on = timestamp;\n' + 11 | '}\n' + 12 | '\n' + 13 | 'sched:::off-cpu\n' + 14 | '/self->on/\n' + 15 | '{\n' + 16 | ' @ = lquantize((timestamp - self->on) / 1000, \n' + 17 | ' 0, 10000, 100);\n' + 18 | '}\n' + 19 | '\n'; 20 | 21 | util.puts(prog); 22 | 23 | dtp.setopt('aggrate', '10ms'); 24 | dtp.strcompile(prog); 25 | dtp.go(); 26 | data = []; 27 | 28 | var start = new Date(); 29 | 30 | setInterval(function () { 31 | 32 | var delta = (new Date()).valueOf() - start.valueOf(); 33 | 34 | if (delta >= 5000) 35 | process.exit(0); 36 | 37 | dtp.aggwalk(function (varid, key, val) { 38 | data.push(val); 39 | 40 | if (data.length >= 50) 41 | return; 42 | 43 | for (i = data.length - 1; i >= 0 && i >= data.length - 5; i--) 44 | util.puts(util.inspect(data[i], false, null)); 45 | }); 46 | }, 10); 47 | 48 | -------------------------------------------------------------------------------- /tests/test-stddev.js: -------------------------------------------------------------------------------- 1 | var sys = require('sys'); 2 | var libdtrace = require('libdtrace'); 3 | var assert = require('assert'); 4 | 5 | /* 6 | * Don't get your hopes up -- this test is asserting that stddev() is 7 | * properly not supported. 8 | */ 9 | dtp = new libdtrace.Consumer(); 10 | 11 | dtp.strcompile('BEGIN { @ = stddev(0) }'); 12 | 13 | dtp.go(); 14 | 15 | assert.throws(function () { dtp.strcompile('BEGIN { @ = stddev(0) }') }); 16 | 17 | -------------------------------------------------------------------------------- /tests/test-sym.js: -------------------------------------------------------------------------------- 1 | var sys = require('sys'); 2 | var libdtrace = require('libdtrace'); 3 | var assert = require('assert'); 4 | var fs = require('fs'); 5 | 6 | var test = function (action, valid, work) 7 | { 8 | /* 9 | * First, verify that the action can be compiled and consumed. 10 | */ 11 | var dtp = new libdtrace.Consumer(); 12 | sys.puts('>>>> basic compile for ' + action + '()'); 13 | 14 | dtp.strcompile('BEGIN { ' + action + '(0); }'); 15 | 16 | dtp.go(); 17 | 18 | var found = false, start, now; 19 | 20 | if (!work) 21 | work = function () {}; 22 | 23 | dtp.consume(function testbasic (probe, rec) { 24 | assert.equal(probe.provider, 'dtrace'); 25 | assert.equal(probe.module, ''); 26 | assert.equal(probe.function, ''); 27 | assert.equal(probe.name, 'BEGIN'); 28 | 29 | if (!rec || !rec.hasOwnProperty('data')) 30 | return; 31 | 32 | found = true; 33 | sys.puts(' ' + sys.inspect(rec)); 34 | }); 35 | 36 | assert.ok(found, 'did not find valid data record'); 37 | 38 | var argument = (action.indexOf('u') == 0 ? 'arg1' : 'arg0'); 39 | 40 | /* 41 | * Now verify the straight consumption. 42 | */ 43 | sys.puts('>>>> consumption for ' + action + 44 | '() (using ' + argument + ' on profile probe)'); 45 | 46 | dtp = new libdtrace.Consumer(); 47 | dtp.strcompile('profile-97hz /' + argument + ' != 0/ ' + 48 | '{ ' + action + '(' + argument + ') }'); 49 | 50 | dtp.go(); 51 | 52 | start = (new Date()).valueOf(); 53 | 54 | do { 55 | now = (new Date()).valueOf(); 56 | work(); 57 | } while (now - start < 2000); 58 | 59 | found = false; 60 | 61 | dtp.consume(function testbasic (probe, rec) { 62 | if (!rec) 63 | return; 64 | 65 | if (valid(rec.data)) 66 | found = true; 67 | 68 | sys.puts(' ' + sys.inspect(rec)); 69 | }); 70 | 71 | dtp.stop(); 72 | assert.ok(found, 'did not find valid record in principal buffer'); 73 | 74 | sys.puts('>>>> aggregation on ' + action + 75 | '() (using ' + argument + ' on profile probe)'); 76 | 77 | dtp = new libdtrace.Consumer(); 78 | dtp.strcompile('profile-97hz /' + argument + ' != 0/ ' + 79 | '{ @[' + action + '(' + argument + ')] = count() }'); 80 | 81 | dtp.setopt('aggrate', '10ms'); 82 | dtp.go(); 83 | 84 | var start = new Date().valueOf(); 85 | 86 | do { 87 | now = (new Date()).valueOf(); 88 | work(); 89 | } while (now - start < 2000); 90 | 91 | found = false; 92 | 93 | dtp.aggwalk(function (varid, key, val) { 94 | if (valid(key[0])) 95 | found = true; 96 | 97 | sys.puts(' ' + sys.inspect(key) + ': ' + sys.inspect(val)); 98 | }); 99 | }; 100 | 101 | var validsym = function (str) 102 | { 103 | assert.ok(str.indexOf('+0x') == -1, 104 | 'expected symbol without offset; found "' + str + '"'); 105 | 106 | return (str.indexOf('`') != -1 && str.indexOf('+') == -1); 107 | } 108 | 109 | var validaddr = function (str) 110 | { 111 | assert.ok(str.indexOf('0x') == 0 || str.indexOf('`') != -1, 112 | 'expected address; found "' + str + '"'); 113 | 114 | return (str.indexOf('`') != -1); 115 | } 116 | 117 | var kernelwork = function () 118 | { 119 | fs.unlink('/tmp/does/not/exist'); 120 | } 121 | 122 | var validkmod = function (str) 123 | { 124 | if (str == 'unix' || 'genunix') /* Solaris */ 125 | return (true); 126 | 127 | if (str == 'mach_kernel') /* OS X */ 128 | return (true); 129 | 130 | return (false); 131 | }; 132 | 133 | var validmod = function (str) 134 | { 135 | if (str.indexOf('.so.') != -1) /* Solaris */ 136 | return (true); 137 | 138 | if (str.indexOf('dylib') != -1) /* OS X */ 139 | return (true); 140 | }; 141 | 142 | test('sym', validsym, kernelwork); 143 | test('func', validsym, kernelwork); 144 | test('mod', validkmod, kernelwork); 145 | 146 | test('uaddr', validaddr); 147 | test('usym', validsym); 148 | test('umod', validmod); 149 | 150 | /* 151 | * Note that this test may induce a seg fault in node on at least some variants 152 | * of Mac OS X. For more details, see the post to dtrace-discuss with the 153 | * subject "Bug in dtrace_uaddr2str() on OS X?" (Dec. 1, 2010). (As of this 154 | * writing, the bug had been root-caused within Apple, and a fix forthcoming.) 155 | */ 156 | test('ufunc', validsym); 157 | 158 | -------------------------------------------------------------------------------- /tests/test-version.js: -------------------------------------------------------------------------------- 1 | var sys = require('sys'); 2 | var libdtrace = require('libdtrace'); 3 | var assert = require('assert'); 4 | var fs = require('fs'); 5 | 6 | var dtp = new libdtrace.Consumer(); 7 | 8 | var ver = dtp.version(); 9 | 10 | elems = ver.split(' '); 11 | assert.equal(elems.length, 3); 12 | assert.equal(elems[0], 'Sun'); 13 | assert.equal(elems[1], 'D'); 14 | 15 | vernum = elems[2].split('.'); 16 | 17 | assert.ok(vernum.length >= 2, 18 | 'expected dot-delimited version; found "' + elems[2] + '"'); 19 | 20 | /* 21 | * Given the constraints around changing the major number for DTrace, it's 22 | * reasonable to assume that if does indeed happen, other elements of 23 | * node-libdtrace have broken as well. 24 | */ 25 | assert.equal(vernum[0], '1', 'According to dt_open.c: "The major number ' + 26 | 'should be incremented when a fundamental change has been made that ' + 27 | 'would affect all consumers, and would reflect sweeping changes to ' + 28 | 'DTrace or the D language." Given that the major number seems to have ' + 29 | 'been bumped to ' + vernum[0] + ', it appears that this rapture is upon ' + 30 | 'us! Committing ritual suicide accordingly...'); 31 | 32 | assert.ok(parseInt(vernum[1], 10) >= 5); 33 | 34 | --------------------------------------------------------------------------------