mooremachine
Advanced tools
Comparing version 2.0.1 to 2.1.0
@@ -13,6 +13,28 @@ /* | ||
var assert = require('assert-plus'); | ||
var util = require('util'); | ||
var mod_assert = require('assert-plus'); | ||
var mod_crypto = require('crypto'); | ||
var mod_util = require('util'); | ||
var EventEmitter = require('events').EventEmitter; | ||
var mod_dtrace; | ||
try { | ||
mod_dtrace = require('dtrace-provider'); | ||
} catch (e) { | ||
mod_dtrace = undefined; | ||
} | ||
var dt; | ||
if (mod_dtrace !== undefined) { | ||
dt = {}; | ||
dt.provider = mod_dtrace.createDTraceProvider('moorefsm'); | ||
dt.create = dt.provider.addProbe('create-fsm', 'char *', 'char *'); | ||
dt.start = dt.provider.addProbe('transition-start', 'char *', 'char *', | ||
'char *', 'char *'); | ||
dt.end = dt.provider.addProbe('transition-end', 'char *', 'char *', | ||
'char *', 'char *'); | ||
dt.provider.enable(); | ||
} | ||
function FSMStateHandle(fsm, state, link) { | ||
@@ -34,3 +56,3 @@ this.fsh_fsm = fsm; | ||
throw (new Error('FSM validTransitions already set')); | ||
assert.arrayOfString(states, 'states'); | ||
mod_assert.arrayOfString(states, 'states'); | ||
this.fsh_validTransitions = states; | ||
@@ -178,3 +200,7 @@ }; | ||
function FSM(defState) { | ||
assert.string(defState, 'default state'); | ||
mod_assert.string(defState, 'default state'); | ||
this.fsm_id = FSM.genId(); | ||
this.fsm_clsname = this.constructor.name; | ||
if (this.fsm_clsname.length === 0) | ||
this.fsm_clsname = 'FSM'; | ||
this.fsm_history = []; | ||
@@ -188,6 +214,21 @@ this.fsm_handle = undefined; | ||
EventEmitter.call(this); | ||
if (dt !== undefined) { | ||
var self = this; | ||
dt.create.fire(function () { | ||
return ([self.fsm_clsname, self.fsm_id]); | ||
}); | ||
} | ||
this._gotoState(defState); | ||
} | ||
util.inherits(FSM, EventEmitter); | ||
mod_util.inherits(FSM, EventEmitter); | ||
FSM.genId = function () { | ||
var b = mod_crypto.randomBytes(8); | ||
/* | ||
* Use slice() to strip off the trailing "=" padding, as the last 2 | ||
* chars are always the same and make for unnecessary noise. | ||
*/ | ||
return (b.toString('base64').slice(0, 11)); | ||
}; | ||
FSM.prototype.getState = function () { | ||
@@ -203,3 +244,3 @@ return (this.fsm_state); | ||
FSM.prototype.allStateEvent = function (evt) { | ||
assert.string(evt, 'event'); | ||
mod_assert.string(evt, 'event'); | ||
if (this.fsm_allStateEvents === undefined) | ||
@@ -212,6 +253,6 @@ this.fsm_allStateEvents = []; | ||
FSM.prototype._gotoState = function (state) { | ||
assert.string(state, 'state'); | ||
mod_assert.string(state, 'state'); | ||
if (this.fsm_inTransition) { | ||
assert.ok(this.fsm_nextState === undefined); | ||
mod_assert.ok(this.fsm_nextState === undefined); | ||
this.fsm_nextState = state; | ||
@@ -221,2 +262,11 @@ return; | ||
var self = this; | ||
var oldState = this.fsm_state; | ||
if (dt !== undefined) { | ||
dt.start.fire(function () { | ||
return ([self.fsm_clsname, self.fsm_id, | ||
oldState, state]); | ||
}); | ||
} | ||
/* | ||
@@ -283,3 +333,2 @@ * First, kill event handlers and timers from our previous state, as | ||
var self = this; | ||
this.fsm_allStateEvents.forEach(function (evt) { | ||
@@ -305,2 +354,9 @@ if (self.listeners(evt).length < 1) { | ||
if (dt !== undefined) { | ||
dt.end.fire(function () { | ||
return ([self.fsm_clsname, self.fsm_id, | ||
oldState, state]); | ||
}); | ||
} | ||
var next = this.fsm_nextState; | ||
@@ -307,0 +363,0 @@ if (next !== undefined) { |
{ | ||
"name": "mooremachine", | ||
"version": "2.0.1", | ||
"version": "2.1.0", | ||
"description": "Moore finite state machines", | ||
@@ -34,2 +34,5 @@ "main": "lib/index.js", | ||
}, | ||
"optionalDependencies": { | ||
"dtrace-provider": "~0.8" | ||
}, | ||
"devDependencies": { | ||
@@ -36,0 +39,0 @@ "tape": ">=3.5.0 <4.0.0" |
130
README.md
@@ -289,1 +289,131 @@ mooremachine | ||
sub-state) as usual before entering the new state. | ||
DTrace support | ||
-------------- | ||
Mooremachine has support for DTrace probes using `dtrace-provider` (and | ||
`libusdt`). The following probes are provided under the | ||
`moorefsm$pid` provider: | ||
* `create-fsm(char *klass, char *id)` -- fired at the creation of a new FSM | ||
instance. The `klass` argument contains the string name of the constructor | ||
of the FSM sub-class. The `id` argument contains a short randomly generated | ||
string that should be unique to this FSM as long as <~6M instances of this | ||
class exist in the program (it consists of 64 random bits, base64-encoded, | ||
so about a 1/1M chance of collision at 6M instances). | ||
* `transition-start(char *klass, char *id, char *oldState, char *newState)` -- | ||
fired at the beginning of an FSM transitioning to a new state. | ||
* `transition-end(char *klass, char *id, char *oldState, char *newState)` -- | ||
fired at the end of an FSM transitioning to a new state. | ||
For example: | ||
``` | ||
dtrace -Zc 'node thingfsm.js' -n ' | ||
moorefsm$target:::transition-start | ||
/copyinstr(arg0) == "ThingFSM"/ | ||
{ | ||
printf("%s => %s", copyinstr(arg2), copyinstr(arg3)); | ||
}' | ||
``` | ||
When used on the `ThingFSM` above might output: | ||
``` | ||
CPU ID FUNCTION:NAME | ||
4 8216 transition-start:transition-start undefined => stopped | ||
4 8216 transition-start:transition-start stopped => connecting | ||
4 8216 transition-start:transition-start connecting => error | ||
``` | ||
This is will list all the transitions of `ThingFSM` instances. | ||
Another example (as a d-script file): | ||
``` | ||
uint64_t timeIn[string]; | ||
moorefsm$target:::transition-start | ||
/copyinstr(arg0) == "SocketMgrFSM" && copyinstr(arg2) != "undefined"/ | ||
{ | ||
this->id = copyinstr(arg1); | ||
this->state = copyinstr(arg2); | ||
this->entryTime = timeIn[this->id]; | ||
this->exitTime = timestamp; | ||
this->time = (this->exitTime - this->entryTime) / 1000000; | ||
@timeInState[this->state] = quantize(this->time); | ||
} | ||
moorefsm$target:::transition-end | ||
/copyinstr(arg0) == "SocketMgrFSM"/ | ||
{ | ||
this->id = copyinstr(arg1); | ||
timeIn[this->id] = timestamp; | ||
} | ||
``` | ||
This reports on the number of milliseconds spent in each state by | ||
all SocketMgrFSM instances in the process. | ||
The output from this could look like: | ||
``` | ||
$ dtrace -Zc 'node test.js' -s script.d | ||
... | ||
error | ||
value ------------- Distribution ------------- count | ||
-1 | 0 | ||
0 |@@@@@@@@@@@@@@@@@@@@ 1 | ||
1 | 0 | ||
2 |@@@@@@@@@@@@@@@@@@@@ 1 | ||
4 | 0 | ||
backoff | ||
value ------------- Distribution ------------- count | ||
-1 | 0 | ||
0 |@@@@@@@@@@@@@@@@@@@@ 1 | ||
1 | 0 | ||
2 | 0 | ||
4 | 0 | ||
8 | 0 | ||
16 | 0 | ||
32 | 0 | ||
64 |@@@@@@@@@@@@@@@@@@@@ 1 | ||
128 | 0 | ||
connected | ||
value ------------- Distribution ------------- count | ||
2 | 0 | ||
4 |@@@@@@@@@@@ 2 | ||
8 |@@@@@@ 1 | ||
16 | 0 | ||
32 | 0 | ||
64 | 0 | ||
128 | 0 | ||
256 |@@@@@@ 1 | ||
512 |@@@@@@@@@@@@@@@@@ 3 | ||
1024 | 0 | ||
connecting | ||
value ------------- Distribution ------------- count | ||
-1 | 0 | ||
0 |@@@@@@@@@@@@@@@@@@ 4 | ||
1 |@@@@ 1 | ||
2 | 0 | ||
4 | 0 | ||
8 | 0 | ||
16 | 0 | ||
32 | 0 | ||
64 |@@@@ 1 | ||
128 | 0 | ||
256 |@@@@ 1 | ||
512 |@@@@ 1 | ||
1024 |@@@@ 1 | ||
2048 | 0 | ||
``` | ||
It's generally safe enough to use only the `id` of the FSM as a key in an | ||
associative array or aggregation in DTrace, even when tracing multiple | ||
processes. This only becomes a problem if you expect to have more than a few | ||
million FSMs running at the same time on a system (in which case you can scope | ||
it by pid and class as well as key). |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
27202
335
419
2