| # Loadbench: Configurable Workload Simulator |
| |
| ## Introduction |
| |
| Loadbench is a tool for stressing the system in interesting ways to validate |
| performance, latency, and stability under load. It provides a simple JSON-based |
| configuration language to describe workload behavior, including interactions |
| with kernel objects, synchronization primitives, and timing. |
| |
| The tool is intended to support the following use cases: |
| * Manually testing kernel and system behavior, in conjunction with tracing, to |
| observe and validate correctness and performance under directed stress. |
| * Providing a framework for automated load metrics and stress testing. |
| * Supporting development of a suite of regression tests to capture minimal |
| reproduction cases of issues found in real world applications. |
| |
| ## General Usage |
| |
| The loadbench package is not included in any standard product build. To use the |
| tool for local manual testing, add the following to your `fx set` command: |
| |
| ```bash |
| $ fx set <other args> --with-base //src/testing:testing |
| ``` |
| |
| Once the tool is included in a build and flashed to the device, it can be driven |
| from the commandline using the `run` command: |
| |
| ```bash |
| $ fx shell run loadbench --help |
| ``` |
| |
| Use the `--file <path>` option to specify a JSON workload specification file to |
| execute: |
| |
| ```bash |
| $ fx scp ./my_test_load.json "[$(fx get-device-addr)]:/tmp/" |
| $ fx shell run loadbench --file /tmp/my_test_load.json |
| ``` |
| |
| Workload specification files can also be built into the package. This is useful |
| for workloads intended for automated and regression testing. These files can be |
| run manually by referencing them relative to the `/pkg/data/` path prefix: |
| |
| ```bash |
| $ fx shell run loadbench --file /pkg/data/simple_load.json |
| ``` |
| |
| ## Workload Specification Files |
| |
| Workload specification files are standard JSON format, with extensions to allow |
| trailing commas and single `// ...` and multi-line `/* ... */` comments. |
| |
| The primary document element of a specification file is always a JSON object: |
| |
| ```JSON |
| { |
| // Top-level specification members ... |
| } |
| ``` |
| |
| ### Top-Level Specification Members |
| |
| The primary document element may contain any of the following members. |
| |
| #### name - Workload Name (optional) |
| |
| The *name* member is an optional string to print when running the workload. The |
| value of this member must be a string. |
| |
| Example: |
| ```JSON |
| { |
| "name": "Example workload name string." |
| // ... |
| } |
| ``` |
| |
| #### config - Workload Config Options (optional) |
| |
| The *config* member is an optional JSON object that may contain the following |
| options: |
| * *priority*: Integer specifying the priority to set the main control thread to |
| before kicking off the worker threads. This may be used to prevent high |
| priority workers from delaying the main control thread. |
| * *interval*: Specifies the duration to run the benchmark. See _duration spec_ |
| for permitted values and formats. |
| |
| Example: |
| ```JSON |
| { |
| "config": { |
| "priority": 25, |
| "interval": "30s", |
| } |
| // ... |
| } |
| ``` |
| |
| #### intervals - Named Intervals (optional) |
| |
| The *intervals* member is an optional JSON object that specifies named intervals |
| that worker threads may specify as parameters to time-based actions. The key of |
| each member of this object specifies the interval's name, while the value |
| is an object that specifies its duration. |
| |
| The duration object may have one of the following key/value pairs: |
| * *duration*: Sepcifies the numeric duration of the interval. |
| * *uniform*: A JSON object that specifies the min and max durations to select |
| from a random uniform distribution. |
| |
| See _duration spec_ for permitted values and formats. |
| |
| Example: |
| ```JSON |
| { |
| "intervals": { |
| "fast inverval": { "duration": "250us" }, |
| "short random interval": { "uniform": { "min": "1ms", "max": "5ms" } }, |
| "long random interval": { "uniform": { "min": "10s", "max": "20s" } }, |
| }, |
| // ... |
| } |
| ``` |
| |
| #### objects - Global Objects (optional) |
| |
| The *objects* member is an optional JSON object that specifies named objects |
| that worker threads may interact with. The key of each member of this object |
| specifies the object's name, while the value is a JSON object that specifies the |
| object's type and parameters. |
| |
| Example: |
| ```JSON |
| { |
| "objects": { |
| "main timer": { "type": "timer" }, |
| "port a": { "type": "port" }, |
| "port b": { "type": "port" }, |
| }, |
| // ... |
| } |
| ``` |
| |
| The following types are currently supported: |
| * *channel*: Zircon channel object. |
| * *event*: Zircon event object. |
| * *port*: Zircon port object. |
| * *timer*: Zircon timer object. |
| |
| Other object types and parameters will be introduced over time. |
| |
| #### behaviors - Global Behaviors (optional) |
| |
| The *behaviors* member is an optional JSON object that specifies named behaviors |
| that may be referenced by worker threads. Named behaviors make it easier to add |
| a common action or set of actions to workers without verbose repetition in each |
| worker spec. |
| |
| The key of each member of this object specifies the behavior's name, while the |
| value may either be a JSON action spec or an array of JSON action specs. See |
| _action spec_ for more details on actions. |
| |
| Example: |
| ```JSON |
| { |
| "behaviors": { |
| "spinner": { "action": "spin", "duration": "10m" }, |
| "half load": [ |
| { "action": "spin", "duration": "5ms" }, |
| { "action": "sleep", "duration": "5ms" }, |
| ] |
| }, |
| // ... |
| } |
| ``` |
| |
| #### workers - Worker Specification |
| |
| The *workers* member is an array of JSON objects that specify the number, |
| behavior, and grouping parameters of worker threads making up the workload. |
| |
| Each array element is a JSON object representing one or more worker threads. A |
| worker spec may have the following members: |
| |
| ##### name - Worker Name (optional) |
| |
| The *name* member is an optional string value to associate with each worker |
| spawned by the containing worker spec. The name is printed in statistics output |
| and also used as a secondary grouping key in aggregate statistics calculations. |
| |
| ##### group - Worker Group (optional) |
| |
| The *group* member is an optional string value that is used to group worker |
| threads during aggregate statistics calculations. The group name is the primary |
| grouping key. |
| |
| ##### instances - Worker Instances (optional) |
| |
| The *instances* member is an optinal integer specifying the number of workers to |
| spawn from the containing worker spec. |
| |
| The default value is 1. |
| |
| ##### priority - Worker Priority (optional) |
| |
| The *priority* member is an optional integer or JSON object that specifies the |
| fair priority or the deadline parameters, respectively, of the workers spawned |
| from the containing worker spec. |
| |
| How the value is interpreted depends on whether it is an integer or a JSON |
| object. When the value is an integer, it is interpreted as a fair priority and |
| the workers are bound to a fair priority profile. When the value is a JSON |
| object, it is interpreted as a deadline parameter set and must contain the |
| string keys "capacity", "deadline", and "period". The values of these keys |
| are durations representing the corresponding deadline parameters. The workers |
| are bound to a deadline profile with the given parameters. |
| |
| Example: |
| ```JSON |
| // Worker spec with fair priority. |
| { |
| "priority": 24, |
| // ... |
| } |
| |
| // Worker spec with deadline priority. |
| { |
| "priority": { "capacity": "2ms", "deadline": "10ms", "period": "10ms" }, |
| // ... |
| } |
| ``` |
| |
| ##### actions - Worker Actions |
| |
| The *actions* member is an array of JSON objects that describe the sequence of |
| actions for the worker to perform. In general, actions are performed in the |
| order specified in the array. When the last action is reached, the worker loops |
| back to the first action and the sequence is repeated. |
| |
| The sequence of actions is performed either until the overall workload interval |
| expires or until an early exit action is executed. |
| |
| See _action spec_ for more details on actions. |
| |
| Example: |
| ```JSON |
| { |
| // ... |
| "workers": [ |
| // First worker spec defining a group of 8 identical workers. |
| { |
| "instances": 8, |
| "group": "High Priority", |
| "name": "CPU Bound", |
| "priority": 24, |
| "actions": [ |
| { "action": "spin", "duration": "10m" }, |
| ] |
| }, |
| // Second worker spec defining a group of 8 identical workers with different |
| // behavior. |
| { |
| "instances": 8, |
| "group": "High Priority", |
| "name": "Bursty", |
| "priority": 24, |
| "actions": [ |
| { "action": "spin", "uniform": { "min": "1ms", "max": "10ms"} }, |
| { "action": "sleep", "uniform": { "min": "100ms", "max": "1s"} }, |
| ] |
| }, |
| // Third worker spec defining a group of 8 identical workers with different |
| // priority from the first. |
| { |
| "instances": 8, |
| "group": "Low Priority", |
| "name": "CPU Bound", |
| "priority": 12, |
| "actions": [ |
| { "action": "spin", "duration": "10m" }, |
| ] |
| }, |
| // Fourth worker spec defining a group of 8 identical workers with different |
| // priority from the second. |
| { |
| "instances": 8, |
| "group": "Low Priority", |
| "name": "Bursty", |
| "priority": 12, |
| "actions": [ |
| { "action": "spin", "uniform": { "min": "1ms", "max": "10ms"} }, |
| { "action": "sleep", "uniform": { "min": "100ms", "max": "1s"} }, |
| ] |
| } |
| ], |
| // ... |
| } |
| ``` |
| |
| ## Duration Spec |
| |
| Various objects have duration perameters, incuding intervals, time-based |
| actions, and deadline profiles. A duration value may either be an integer or |
| a string containing an integer scalar value and a unit suffix. |
| |
| When the duration value is an integer, it is taken in units of nanoseconds. |
| |
| String values must include one of the following units: |
| * Hours: "h" |
| * Minutes: "m" |
| * Seconds: "s" |
| * Milliseconds: "ms" |
| * Microseconds: "us" |
| * Nanoseconds: "ns" |
| |
| Example: |
| ```JSON |
| |
| { "action": "spin", "duration": 1000 } // Spin for 1000ns or 1us. |
| |
| { "action": "sleep", "uniform": { "min": "1s", "max": "10s" } } // Sleep for between 1 and 10 s. |
| |
| { "priority": { "capacity": "1ms", "deadline": "10ms", "period": "20ms" } } |
| ``` |
| |
| ## Action Spec |
| |
| Actions are JSON objects that describe a discrete action for a worker to |
| execute. All actions have an *action* member with a string value specifying the |
| type of action. Actions may require additional members to specify relevant |
| parameters, such as intervals, objects, and even other actions. |
| |
| The following actions are supported: |
| |
| ### spin - Spin for an Interval |
| |
| The *spin* action causes the worker to enter a spin loop for the given interval. |
| The interval may either be a simple duration or a uniform distribution. |
| |
| Example: |
| ```JSON |
| { "action": "spin", "duration": "2m" } |
| |
| { "action": "spin", "uniform": { "min": "1ms", "max": "3ms" } } |
| ``` |
| |
| #### duration |
| |
| When the spin action contains a *duration* member, the spin will continue for the |
| constant value given as a _duration spec_. |
| |
| #### uniform |
| |
| When the spin action contains a *uniform* member, the spin will continue for the |
| duration selected from a uniform distrubution. The value must be a JSON object |
| string keys for the *min* and *max* _duration specs_ defining the uniform range. |
| |
| ### sleep - Sleep for an Interval |
| |
| The *sleep* action causes the worker to sleep for the given interval. This |
| action supports the same parameters as the *spin* action. |
| |
| ### write - Write to a Channel |
| |
| The *write* action causes the worker to write a message with the given number |
| of bytes to the given named channnel object. |
| |
| #### channel |
| |
| The *channel* member specifies the string name of the channel object to write |
| to. |
| |
| Example: |
| ```JSON |
| { "action": "write", "channel": "main channel", "side": 0, "bytes": 1024 } |
| ``` |
| |
| #### side |
| |
| The *side* member specifies which side of the channel object to write to. The |
| value must be an integer with either the value 0 or 1. |
| |
| #### bytes |
| |
| The *bytes* member specifies how many bytes to write in the message. |
| |
| ### read - Read from a Channel |
| |
| The *read* action is similar to the *write* action, except that the given number |
| of bytes is read from the channel instead of written. |
| |
| ### behavior - Execute a Named Behavior |
| |
| The *behavior* action causes the worker to execute the given named behavior |
| from the top-level *behavior* member. |
| |
| Example: |
| ```JSON |
| { "action": "behavior", "name": "spin action" } |
| ``` |
| |
| #### name |
| |
| The *name* member specifies the string name of the behavior to execute. |
| |
| ### wait_async - Async Wait on an Object |
| |
| The *wait_async* action causes the worker to perform `zx_object_wait_async` on |
| the given port and object. |
| |
| Example: |
| ```JSON |
| { "action": "wait_async", "port": "port name", "object": "object name", "signals": 8 } |
| ``` |
| |
| #### port |
| |
| The *port* member specifies the string name of the port object to register with. |
| #### object |
| |
| The *object* member specifies the string name of the object to register with the |
| port. |
| |
| #### signals |
| |
| The *signals* member specifies the integer signal mask of events to wait for. |
| |
| ### wait_one - Wait on an Object |
| |
| The *wait_one* action causes the worker to perform `zx_object_wait_one` on the |
| given object. |
| |
| Example: |
| ```JSON |
| { "action": "wait_one", "object": "object name", "signals": 8, "deadline": "5s" } |
| ``` |
| |
| #### object |
| |
| The *object* member specifies the string name of the object to wait on. |
| |
| #### signals |
| |
| The *signals* member specifies the integer signal mask of events to wait for. |
| |
| #### deadline (optional) |
| |
| The *deadline* member optionally specifies the timeout duration of the wait |
| operation. |
| |
| ### port_wait - Wait on a Port |
| |
| The *port_wait* action causes the worker to perform `zx_port_wait` on the |
| given port object. |
| |
| Example: |
| ```JSON |
| { "action": "port_wait", "port": "port name", "deadline": "5s" } |
| ``` |
| |
| #### port |
| |
| The *port* member specifies the string name of the port object to wait on. |
| |
| #### deadline (optional) |
| |
| The *deadline* member optionally specifies the timeout duration of the wait |
| operation. |
| |
| ### signal - Signal an Object |
| |
| The *signal* action causes the worker to perform `zx_object_signal` on the given |
| object. |
| |
| Example: |
| ```JSON |
| { "action": "signal", "object": "object name", "clear": 0, "set": 8 } |
| ``` |
| |
| #### object |
| |
| The *object* member specifies the string name of the object to signal. |
| |
| #### clear |
| |
| The *clear* member specifies the integer mask of the events to clear. |
| |
| #### set |
| |
| The *set* member specifies the integer mask of the events to set. |
| |
| ### timer_set - Set a Timer Object |
| |
| The *timer_set* object sets the given timer to the given relative deadline and |
| optional slack. |
| |
| Example: |
| ```JSON |
| { "action": "timer_set", "timer": "timer name", "deadline": "100ms", "slack": "250us" } |
| ``` |
| |
| #### timer |
| |
| The *timer* member specifies the string name of the timer object to set. |
| |
| #### deadline |
| |
| The *deadline* member specifies the deadline of the timeout, relative to the |
| current time, as a duration. |
| |
| #### slack |
| |
| The *slack* member specifies the timer slack as a duration. |
| |
| ### exit - Exit the Action Loop Early |
| |
| The *exit* action causes the worker to exit the action loop and idle until the |
| end of the workload run. |
| |
| Example: |
| ```JSON |
| { "action": "exit" } |
| ``` |