1*61c4878aSAndroid Build Coastguard Worker.. _module-pw_metric: 2*61c4878aSAndroid Build Coastguard Worker 3*61c4878aSAndroid Build Coastguard Worker========= 4*61c4878aSAndroid Build Coastguard Workerpw_metric 5*61c4878aSAndroid Build Coastguard Worker========= 6*61c4878aSAndroid Build Coastguard Worker 7*61c4878aSAndroid Build Coastguard Worker.. attention:: 8*61c4878aSAndroid Build Coastguard Worker This module is **not yet production ready**; ask us if you are interested in 9*61c4878aSAndroid Build Coastguard Worker using it out or have ideas about how to improve it. 10*61c4878aSAndroid Build Coastguard Worker 11*61c4878aSAndroid Build Coastguard Worker-------- 12*61c4878aSAndroid Build Coastguard WorkerOverview 13*61c4878aSAndroid Build Coastguard Worker-------- 14*61c4878aSAndroid Build Coastguard WorkerPigweed's metric module is a **lightweight manual instrumentation system** for 15*61c4878aSAndroid Build Coastguard Workertracking system health metrics like counts or set values. For example, 16*61c4878aSAndroid Build Coastguard Worker``pw_metric`` could help with tracking the number of I2C bus writes, or the 17*61c4878aSAndroid Build Coastguard Workernumber of times a buffer was filled before it could drain in time, or safely 18*61c4878aSAndroid Build Coastguard Workerincrementing counters from ISRs. 19*61c4878aSAndroid Build Coastguard Worker 20*61c4878aSAndroid Build Coastguard WorkerKey features of ``pw_metric``: 21*61c4878aSAndroid Build Coastguard Worker 22*61c4878aSAndroid Build Coastguard Worker- **Tokenized names** - Names are tokenized using the ``pw_tokenizer`` enabling 23*61c4878aSAndroid Build Coastguard Worker long metric names that don't bloat your binary. 24*61c4878aSAndroid Build Coastguard Worker 25*61c4878aSAndroid Build Coastguard Worker- **Tree structure** - Metrics can form a tree, enabling grouping of related 26*61c4878aSAndroid Build Coastguard Worker metrics for clearer organization. 27*61c4878aSAndroid Build Coastguard Worker 28*61c4878aSAndroid Build Coastguard Worker- **Per object collection** - Metrics and groups can live on object instances 29*61c4878aSAndroid Build Coastguard Worker and be flexibly combined with metrics from other instances. 30*61c4878aSAndroid Build Coastguard Worker 31*61c4878aSAndroid Build Coastguard Worker- **Global registration** - For legacy code bases or just because it's easier, 32*61c4878aSAndroid Build Coastguard Worker ``pw_metric`` supports automatic aggregation of metrics. This is optional but 33*61c4878aSAndroid Build Coastguard Worker convenient in many cases. 34*61c4878aSAndroid Build Coastguard Worker 35*61c4878aSAndroid Build Coastguard Worker- **Simple design** - There are only two core data structures: ``Metric`` and 36*61c4878aSAndroid Build Coastguard Worker ``Group``, which are both simple to understand and use. The only type of 37*61c4878aSAndroid Build Coastguard Worker metric supported is ``uint32_t`` and ``float``. This module does not support 38*61c4878aSAndroid Build Coastguard Worker complicated aggregations like running average or min/max. 39*61c4878aSAndroid Build Coastguard Worker 40*61c4878aSAndroid Build Coastguard WorkerExample: Instrumenting a single object 41*61c4878aSAndroid Build Coastguard Worker-------------------------------------- 42*61c4878aSAndroid Build Coastguard WorkerThe below example illustrates what instrumenting a class with a metric group 43*61c4878aSAndroid Build Coastguard Workerand metrics might look like. In this case, the object's 44*61c4878aSAndroid Build Coastguard Worker``MySubsystem::metrics()`` member is not globally registered; the user is on 45*61c4878aSAndroid Build Coastguard Workertheir own for combining this subsystem's metrics with others. 46*61c4878aSAndroid Build Coastguard Worker 47*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp 48*61c4878aSAndroid Build Coastguard Worker 49*61c4878aSAndroid Build Coastguard Worker #include "pw_metric/metric.h" 50*61c4878aSAndroid Build Coastguard Worker 51*61c4878aSAndroid Build Coastguard Worker class MySubsystem { 52*61c4878aSAndroid Build Coastguard Worker public: 53*61c4878aSAndroid Build Coastguard Worker void DoSomething() { 54*61c4878aSAndroid Build Coastguard Worker attempts_.Increment(); 55*61c4878aSAndroid Build Coastguard Worker if (ActionSucceeds()) { 56*61c4878aSAndroid Build Coastguard Worker successes_.Increment(); 57*61c4878aSAndroid Build Coastguard Worker } 58*61c4878aSAndroid Build Coastguard Worker } 59*61c4878aSAndroid Build Coastguard Worker Group& metrics() { return metrics_; } 60*61c4878aSAndroid Build Coastguard Worker 61*61c4878aSAndroid Build Coastguard Worker private: 62*61c4878aSAndroid Build Coastguard Worker PW_METRIC_GROUP(metrics_, "my_subsystem"); 63*61c4878aSAndroid Build Coastguard Worker PW_METRIC(metrics_, attempts_, "attempts", 0u); 64*61c4878aSAndroid Build Coastguard Worker PW_METRIC(metrics_, successes_, "successes", 0u); 65*61c4878aSAndroid Build Coastguard Worker }; 66*61c4878aSAndroid Build Coastguard Worker 67*61c4878aSAndroid Build Coastguard WorkerThe metrics subsystem has no canonical output format at this time, but a JSON 68*61c4878aSAndroid Build Coastguard Workerdump might look something like this: 69*61c4878aSAndroid Build Coastguard Worker 70*61c4878aSAndroid Build Coastguard Worker.. code-block:: json 71*61c4878aSAndroid Build Coastguard Worker 72*61c4878aSAndroid Build Coastguard Worker { 73*61c4878aSAndroid Build Coastguard Worker "my_subsystem" : { 74*61c4878aSAndroid Build Coastguard Worker "successes" : 1000, 75*61c4878aSAndroid Build Coastguard Worker "attempts" : 1200, 76*61c4878aSAndroid Build Coastguard Worker } 77*61c4878aSAndroid Build Coastguard Worker } 78*61c4878aSAndroid Build Coastguard Worker 79*61c4878aSAndroid Build Coastguard WorkerIn this case, every instance of ``MySubsystem`` will have unique counters. 80*61c4878aSAndroid Build Coastguard Worker 81*61c4878aSAndroid Build Coastguard WorkerExample: Instrumenting a legacy codebase 82*61c4878aSAndroid Build Coastguard Worker---------------------------------------- 83*61c4878aSAndroid Build Coastguard WorkerA common situation in embedded development is **debugging legacy code** or code 84*61c4878aSAndroid Build Coastguard Workerwhich is hard to change; where it is perhaps impossible to plumb metrics 85*61c4878aSAndroid Build Coastguard Workerobjects around with dependency injection. The alternative to plumbing metrics 86*61c4878aSAndroid Build Coastguard Workeris to register the metrics through a global mechanism. ``pw_metric`` supports 87*61c4878aSAndroid Build Coastguard Workerthis use case. For example: 88*61c4878aSAndroid Build Coastguard Worker 89*61c4878aSAndroid Build Coastguard Worker**Before instrumenting:** 90*61c4878aSAndroid Build Coastguard Worker 91*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp 92*61c4878aSAndroid Build Coastguard Worker 93*61c4878aSAndroid Build Coastguard Worker // This code was passed down from generations of developers before; no one 94*61c4878aSAndroid Build Coastguard Worker // knows what it does or how it works. But it needs to be fixed! 95*61c4878aSAndroid Build Coastguard Worker void OldCodeThatDoesntWorkButWeDontKnowWhy() { 96*61c4878aSAndroid Build Coastguard Worker if (some_variable) { 97*61c4878aSAndroid Build Coastguard Worker DoSomething(); 98*61c4878aSAndroid Build Coastguard Worker } else { 99*61c4878aSAndroid Build Coastguard Worker DoSomethingElse(); 100*61c4878aSAndroid Build Coastguard Worker } 101*61c4878aSAndroid Build Coastguard Worker } 102*61c4878aSAndroid Build Coastguard Worker 103*61c4878aSAndroid Build Coastguard Worker**After instrumenting:** 104*61c4878aSAndroid Build Coastguard Worker 105*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp 106*61c4878aSAndroid Build Coastguard Worker 107*61c4878aSAndroid Build Coastguard Worker #include "pw_metric/global.h" 108*61c4878aSAndroid Build Coastguard Worker #include "pw_metric/metric.h" 109*61c4878aSAndroid Build Coastguard Worker 110*61c4878aSAndroid Build Coastguard Worker PW_METRIC_GLOBAL(legacy_do_something, "legacy_do_something"); 111*61c4878aSAndroid Build Coastguard Worker PW_METRIC_GLOBAL(legacy_do_something_else, "legacy_do_something_else"); 112*61c4878aSAndroid Build Coastguard Worker 113*61c4878aSAndroid Build Coastguard Worker // This code was passed down from generations of developers before; no one 114*61c4878aSAndroid Build Coastguard Worker // knows what it does or how it works. But it needs to be fixed! 115*61c4878aSAndroid Build Coastguard Worker void OldCodeThatDoesntWorkButWeDontKnowWhy() { 116*61c4878aSAndroid Build Coastguard Worker if (some_variable) { 117*61c4878aSAndroid Build Coastguard Worker legacy_do_something.Increment(); 118*61c4878aSAndroid Build Coastguard Worker DoSomething(); 119*61c4878aSAndroid Build Coastguard Worker } else { 120*61c4878aSAndroid Build Coastguard Worker legacy_do_something_else.Increment(); 121*61c4878aSAndroid Build Coastguard Worker DoSomethingElse(); 122*61c4878aSAndroid Build Coastguard Worker } 123*61c4878aSAndroid Build Coastguard Worker } 124*61c4878aSAndroid Build Coastguard Worker 125*61c4878aSAndroid Build Coastguard WorkerIn this case, the developer merely had to add the metrics header, define some 126*61c4878aSAndroid Build Coastguard Workermetrics, and then start incrementing them. These metrics will be available 127*61c4878aSAndroid Build Coastguard Workerglobally through the ``pw::metric::global_metrics`` object defined in 128*61c4878aSAndroid Build Coastguard Worker``pw_metric/global.h``. 129*61c4878aSAndroid Build Coastguard Worker 130*61c4878aSAndroid Build Coastguard WorkerWhy not just use simple counter variables? 131*61c4878aSAndroid Build Coastguard Worker------------------------------------------ 132*61c4878aSAndroid Build Coastguard WorkerOne might wonder what the point of leveraging a metric library is when it is 133*61c4878aSAndroid Build Coastguard Workertrivial to make some global variables and print them out. There are a few 134*61c4878aSAndroid Build Coastguard Workerreasons: 135*61c4878aSAndroid Build Coastguard Worker 136*61c4878aSAndroid Build Coastguard Worker- **Metrics offload** - To make it easy to get metrics off-device by sharing 137*61c4878aSAndroid Build Coastguard Worker the infrastructure for offloading. 138*61c4878aSAndroid Build Coastguard Worker 139*61c4878aSAndroid Build Coastguard Worker- **Consistent format** - To get the metrics in a consistent format (e.g. 140*61c4878aSAndroid Build Coastguard Worker protobuf or JSON) for analysis 141*61c4878aSAndroid Build Coastguard Worker 142*61c4878aSAndroid Build Coastguard Worker- **Uncoordinated collection** - To provide a simple and reliable way for 143*61c4878aSAndroid Build Coastguard Worker developers on a team to all collect metrics for their subsystems, without 144*61c4878aSAndroid Build Coastguard Worker having to coordinate to offload. This could extend to code in libraries 145*61c4878aSAndroid Build Coastguard Worker written by other teams. 146*61c4878aSAndroid Build Coastguard Worker 147*61c4878aSAndroid Build Coastguard Worker- **Pre-boot or interrupt visibility** - Some of the most challenging bugs come 148*61c4878aSAndroid Build Coastguard Worker from early system boot when not all system facilities are up (e.g. logging or 149*61c4878aSAndroid Build Coastguard Worker UART). In those cases, metrics provide a low-overhead approach to understand 150*61c4878aSAndroid Build Coastguard Worker what is happening. During early boot, metrics can be incremented, then after 151*61c4878aSAndroid Build Coastguard Worker boot dumping the metrics provides insights into what happened. While basic 152*61c4878aSAndroid Build Coastguard Worker counter variables can work in these contexts too, one still has to deal with 153*61c4878aSAndroid Build Coastguard Worker the offloading problem; which the library handles. 154*61c4878aSAndroid Build Coastguard Worker 155*61c4878aSAndroid Build Coastguard Worker--------------------- 156*61c4878aSAndroid Build Coastguard WorkerMetrics API reference 157*61c4878aSAndroid Build Coastguard Worker--------------------- 158*61c4878aSAndroid Build Coastguard Worker 159*61c4878aSAndroid Build Coastguard WorkerThe metrics API consists of just a few components: 160*61c4878aSAndroid Build Coastguard Worker 161*61c4878aSAndroid Build Coastguard Worker- The core data structures ``pw::metric::Metric`` and ``pw::metric::Group`` 162*61c4878aSAndroid Build Coastguard Worker- The macros for scoped metrics and groups ``PW_METRIC`` and 163*61c4878aSAndroid Build Coastguard Worker ``PW_METRIC_GROUP`` 164*61c4878aSAndroid Build Coastguard Worker- The macros for globally registered metrics and groups 165*61c4878aSAndroid Build Coastguard Worker ``PW_METRIC_GLOBAL`` and ``PW_METRIC_GROUP_GLOBAL`` 166*61c4878aSAndroid Build Coastguard Worker- The global groups and metrics list: ``pw::metric::global_groups`` and 167*61c4878aSAndroid Build Coastguard Worker ``pw::metric::global_metrics``. 168*61c4878aSAndroid Build Coastguard Worker 169*61c4878aSAndroid Build Coastguard WorkerMetric 170*61c4878aSAndroid Build Coastguard Worker------ 171*61c4878aSAndroid Build Coastguard WorkerThe ``pw::metric::Metric`` provides: 172*61c4878aSAndroid Build Coastguard Worker 173*61c4878aSAndroid Build Coastguard Worker- A 31-bit tokenized name 174*61c4878aSAndroid Build Coastguard Worker- A 1-bit discriminator for int or float 175*61c4878aSAndroid Build Coastguard Worker- A 32-bit payload (int or float) 176*61c4878aSAndroid Build Coastguard Worker- A 32-bit next pointer (intrusive list) 177*61c4878aSAndroid Build Coastguard Worker 178*61c4878aSAndroid Build Coastguard WorkerThe metric object is 12 bytes on 32-bit platforms. 179*61c4878aSAndroid Build Coastguard Worker 180*61c4878aSAndroid Build Coastguard Worker.. cpp:class:: pw::metric::Metric 181*61c4878aSAndroid Build Coastguard Worker 182*61c4878aSAndroid Build Coastguard Worker .. cpp:function:: Increment(uint32_t amount = 0) 183*61c4878aSAndroid Build Coastguard Worker 184*61c4878aSAndroid Build Coastguard Worker Increment the metric by the given amount. Results in undefined behaviour if 185*61c4878aSAndroid Build Coastguard Worker the metric is not of type int. 186*61c4878aSAndroid Build Coastguard Worker 187*61c4878aSAndroid Build Coastguard Worker .. cpp:function:: Set(uint32_t value) 188*61c4878aSAndroid Build Coastguard Worker 189*61c4878aSAndroid Build Coastguard Worker Set the metric to the given value. Results in undefined behaviour if the 190*61c4878aSAndroid Build Coastguard Worker metric is not of type int. 191*61c4878aSAndroid Build Coastguard Worker 192*61c4878aSAndroid Build Coastguard Worker .. cpp:function:: Set(float value) 193*61c4878aSAndroid Build Coastguard Worker 194*61c4878aSAndroid Build Coastguard Worker Set the metric to the given value. Results in undefined behaviour if the 195*61c4878aSAndroid Build Coastguard Worker metric is not of type float. 196*61c4878aSAndroid Build Coastguard Worker 197*61c4878aSAndroid Build Coastguard Worker.. _module-pw_metric-group: 198*61c4878aSAndroid Build Coastguard Worker 199*61c4878aSAndroid Build Coastguard WorkerGroup 200*61c4878aSAndroid Build Coastguard Worker----- 201*61c4878aSAndroid Build Coastguard WorkerThe ``pw::metric::Group`` object is simply: 202*61c4878aSAndroid Build Coastguard Worker 203*61c4878aSAndroid Build Coastguard Worker- A name for the group 204*61c4878aSAndroid Build Coastguard Worker- A list of children groups 205*61c4878aSAndroid Build Coastguard Worker- A list of leaf metrics groups 206*61c4878aSAndroid Build Coastguard Worker- A 32-bit next pointer (intrusive list) 207*61c4878aSAndroid Build Coastguard Worker 208*61c4878aSAndroid Build Coastguard WorkerThe group object is 16 bytes on 32-bit platforms. 209*61c4878aSAndroid Build Coastguard Worker 210*61c4878aSAndroid Build Coastguard Worker.. cpp:class:: pw::metric::Group 211*61c4878aSAndroid Build Coastguard Worker 212*61c4878aSAndroid Build Coastguard Worker .. cpp:function:: Dump(int indent_level = 0) 213*61c4878aSAndroid Build Coastguard Worker 214*61c4878aSAndroid Build Coastguard Worker Recursively dump a metrics group to ``pw_log``. Produces output like: 215*61c4878aSAndroid Build Coastguard Worker 216*61c4878aSAndroid Build Coastguard Worker .. code-block:: none 217*61c4878aSAndroid Build Coastguard Worker 218*61c4878aSAndroid Build Coastguard Worker "$6doqFw==": { 219*61c4878aSAndroid Build Coastguard Worker "$05OCZw==": { 220*61c4878aSAndroid Build Coastguard Worker "$VpPfzg==": 1, 221*61c4878aSAndroid Build Coastguard Worker "$LGPMBQ==": 1.000000, 222*61c4878aSAndroid Build Coastguard Worker "$+iJvUg==": 5, 223*61c4878aSAndroid Build Coastguard Worker } 224*61c4878aSAndroid Build Coastguard Worker "$9hPNxw==": 65, 225*61c4878aSAndroid Build Coastguard Worker "$oK7HmA==": 13, 226*61c4878aSAndroid Build Coastguard Worker "$FCM4qQ==": 0, 227*61c4878aSAndroid Build Coastguard Worker } 228*61c4878aSAndroid Build Coastguard Worker 229*61c4878aSAndroid Build Coastguard Worker Note the metric names are tokenized with base64. Decoding requires using 230*61c4878aSAndroid Build Coastguard Worker the Pigweed detokenizer. With a detokenizing-enabled logger, you could get 231*61c4878aSAndroid Build Coastguard Worker something like: 232*61c4878aSAndroid Build Coastguard Worker 233*61c4878aSAndroid Build Coastguard Worker .. code-block:: none 234*61c4878aSAndroid Build Coastguard Worker 235*61c4878aSAndroid Build Coastguard Worker "i2c_1": { 236*61c4878aSAndroid Build Coastguard Worker "gyro": { 237*61c4878aSAndroid Build Coastguard Worker "num_sampleses": 1, 238*61c4878aSAndroid Build Coastguard Worker "init_time_us": 1.000000, 239*61c4878aSAndroid Build Coastguard Worker "initialized": 5, 240*61c4878aSAndroid Build Coastguard Worker } 241*61c4878aSAndroid Build Coastguard Worker "bus_errors": 65, 242*61c4878aSAndroid Build Coastguard Worker "transactions": 13, 243*61c4878aSAndroid Build Coastguard Worker "bytes_sent": 0, 244*61c4878aSAndroid Build Coastguard Worker } 245*61c4878aSAndroid Build Coastguard Worker 246*61c4878aSAndroid Build Coastguard WorkerMacros 247*61c4878aSAndroid Build Coastguard Worker------ 248*61c4878aSAndroid Build Coastguard WorkerThe **macros are the primary mechanism for creating metrics**, and should be 249*61c4878aSAndroid Build Coastguard Workerused instead of directly constructing metrics or groups. The macros handle 250*61c4878aSAndroid Build Coastguard Workertokenizing the metric and group names. 251*61c4878aSAndroid Build Coastguard Worker 252*61c4878aSAndroid Build Coastguard Worker.. cpp:function:: PW_METRIC(identifier, name, value) 253*61c4878aSAndroid Build Coastguard Worker.. cpp:function:: PW_METRIC(group, identifier, name, value) 254*61c4878aSAndroid Build Coastguard Worker.. cpp:function:: PW_METRIC_STATIC(identifier, name, value) 255*61c4878aSAndroid Build Coastguard Worker.. cpp:function:: PW_METRIC_STATIC(group, identifier, name, value) 256*61c4878aSAndroid Build Coastguard Worker 257*61c4878aSAndroid Build Coastguard Worker Declare a metric, optionally adding it to a group. 258*61c4878aSAndroid Build Coastguard Worker 259*61c4878aSAndroid Build Coastguard Worker - **identifier** - An identifier name for the created variable or member. 260*61c4878aSAndroid Build Coastguard Worker For example: ``i2c_transactions`` might be used as a local or global 261*61c4878aSAndroid Build Coastguard Worker metric; inside a class, could be named according to members 262*61c4878aSAndroid Build Coastguard Worker (``i2c_transactions_`` for Google's C++ style). 263*61c4878aSAndroid Build Coastguard Worker - **name** - The string name for the metric. This will be tokenized. There 264*61c4878aSAndroid Build Coastguard Worker are no restrictions on the contents of the name; however, consider 265*61c4878aSAndroid Build Coastguard Worker restricting these to be valid C++ identifiers to ease integration with 266*61c4878aSAndroid Build Coastguard Worker other systems. 267*61c4878aSAndroid Build Coastguard Worker - **value** - The initial value for the metric. Must be either a floating 268*61c4878aSAndroid Build Coastguard Worker point value (e.g. ``3.2f``) or unsigned int (e.g. ``21u``). 269*61c4878aSAndroid Build Coastguard Worker - **group** - A ``pw::metric::Group`` instance. If provided, the metric is 270*61c4878aSAndroid Build Coastguard Worker added to the given group. 271*61c4878aSAndroid Build Coastguard Worker 272*61c4878aSAndroid Build Coastguard Worker The macro declares a variable or member named "name" with type 273*61c4878aSAndroid Build Coastguard Worker ``pw::metric::Metric``, and works in three contexts: global, local, and 274*61c4878aSAndroid Build Coastguard Worker member. 275*61c4878aSAndroid Build Coastguard Worker 276*61c4878aSAndroid Build Coastguard Worker If the ``_STATIC`` variant is used, the macro declares a variable with static 277*61c4878aSAndroid Build Coastguard Worker storage. These can be used in function scopes, but not in classes. 278*61c4878aSAndroid Build Coastguard Worker 279*61c4878aSAndroid Build Coastguard Worker 1. At global scope: 280*61c4878aSAndroid Build Coastguard Worker 281*61c4878aSAndroid Build Coastguard Worker .. code-block:: cpp 282*61c4878aSAndroid Build Coastguard Worker 283*61c4878aSAndroid Build Coastguard Worker PW_METRIC(foo, "foo", 15.5f); 284*61c4878aSAndroid Build Coastguard Worker 285*61c4878aSAndroid Build Coastguard Worker void MyFunc() { 286*61c4878aSAndroid Build Coastguard Worker foo.Increment(); 287*61c4878aSAndroid Build Coastguard Worker } 288*61c4878aSAndroid Build Coastguard Worker 289*61c4878aSAndroid Build Coastguard Worker 2. At local function or member function scope: 290*61c4878aSAndroid Build Coastguard Worker 291*61c4878aSAndroid Build Coastguard Worker .. code-block:: cpp 292*61c4878aSAndroid Build Coastguard Worker 293*61c4878aSAndroid Build Coastguard Worker void MyFunc() { 294*61c4878aSAndroid Build Coastguard Worker PW_METRIC(foo, "foo", 15.5f); 295*61c4878aSAndroid Build Coastguard Worker foo.Increment(); 296*61c4878aSAndroid Build Coastguard Worker // foo goes out of scope here; be careful! 297*61c4878aSAndroid Build Coastguard Worker } 298*61c4878aSAndroid Build Coastguard Worker 299*61c4878aSAndroid Build Coastguard Worker 3. At member level inside a class or struct: 300*61c4878aSAndroid Build Coastguard Worker 301*61c4878aSAndroid Build Coastguard Worker .. code-block:: cpp 302*61c4878aSAndroid Build Coastguard Worker 303*61c4878aSAndroid Build Coastguard Worker struct MyStructy { 304*61c4878aSAndroid Build Coastguard Worker void DoSomething() { 305*61c4878aSAndroid Build Coastguard Worker somethings.Increment(); 306*61c4878aSAndroid Build Coastguard Worker } 307*61c4878aSAndroid Build Coastguard Worker // Every instance of MyStructy will have a separate somethings counter. 308*61c4878aSAndroid Build Coastguard Worker PW_METRIC(somethings, "somethings", 0u); 309*61c4878aSAndroid Build Coastguard Worker } 310*61c4878aSAndroid Build Coastguard Worker 311*61c4878aSAndroid Build Coastguard Worker You can also put a metric into a group with the macro. Metrics can belong to 312*61c4878aSAndroid Build Coastguard Worker strictly one group, otherwise an assertion will fail. Example: 313*61c4878aSAndroid Build Coastguard Worker 314*61c4878aSAndroid Build Coastguard Worker .. code-block:: cpp 315*61c4878aSAndroid Build Coastguard Worker 316*61c4878aSAndroid Build Coastguard Worker PW_METRIC_GROUP(my_group, "my_group"); 317*61c4878aSAndroid Build Coastguard Worker PW_METRIC(my_group, foo, "foo", 0.2f); 318*61c4878aSAndroid Build Coastguard Worker PW_METRIC(my_group, bar, "bar", 44000u); 319*61c4878aSAndroid Build Coastguard Worker PW_METRIC(my_group, zap, "zap", 3.14f); 320*61c4878aSAndroid Build Coastguard Worker 321*61c4878aSAndroid Build Coastguard Worker .. tip:: 322*61c4878aSAndroid Build Coastguard Worker If you want a globally registered metric, see ``pw_metric/global.h``; in 323*61c4878aSAndroid Build Coastguard Worker that context, metrics are globally registered without the need to 324*61c4878aSAndroid Build Coastguard Worker centrally register in a single place. 325*61c4878aSAndroid Build Coastguard Worker 326*61c4878aSAndroid Build Coastguard Worker.. cpp:function:: PW_METRIC_GROUP(identifier, name) 327*61c4878aSAndroid Build Coastguard Worker.. cpp:function:: PW_METRIC_GROUP(parent_group, identifier, name) 328*61c4878aSAndroid Build Coastguard Worker.. cpp:function:: PW_METRIC_GROUP_STATIC(identifier, name) 329*61c4878aSAndroid Build Coastguard Worker.. cpp:function:: PW_METRIC_GROUP_STATIC(parent_group, identifier, name) 330*61c4878aSAndroid Build Coastguard Worker 331*61c4878aSAndroid Build Coastguard Worker Declares a ``pw::metric::Group`` with name name; the name is tokenized. 332*61c4878aSAndroid Build Coastguard Worker Works similar to ``PW_METRIC`` and can be used in the same contexts (global, 333*61c4878aSAndroid Build Coastguard Worker local, and member). Optionally, the group can be added to a parent group. 334*61c4878aSAndroid Build Coastguard Worker 335*61c4878aSAndroid Build Coastguard Worker If the ``_STATIC`` variant is used, the macro declares a variable with static 336*61c4878aSAndroid Build Coastguard Worker storage. These can be used in function scopes, but not in classes. 337*61c4878aSAndroid Build Coastguard Worker 338*61c4878aSAndroid Build Coastguard Worker Example: 339*61c4878aSAndroid Build Coastguard Worker 340*61c4878aSAndroid Build Coastguard Worker .. code-block:: cpp 341*61c4878aSAndroid Build Coastguard Worker 342*61c4878aSAndroid Build Coastguard Worker PW_METRIC_GROUP(my_group, "my_group"); 343*61c4878aSAndroid Build Coastguard Worker PW_METRIC(my_group, foo, "foo", 0.2f); 344*61c4878aSAndroid Build Coastguard Worker PW_METRIC(my_group, bar, "bar", 44000u); 345*61c4878aSAndroid Build Coastguard Worker PW_METRIC(my_group, zap, "zap", 3.14f); 346*61c4878aSAndroid Build Coastguard Worker 347*61c4878aSAndroid Build Coastguard Worker.. cpp:function:: PW_METRIC_GLOBAL(identifier, name, value) 348*61c4878aSAndroid Build Coastguard Worker 349*61c4878aSAndroid Build Coastguard Worker Declare a ``pw::metric::Metric`` with name name, and register it in the 350*61c4878aSAndroid Build Coastguard Worker global metrics list ``pw::metric::global_metrics``. 351*61c4878aSAndroid Build Coastguard Worker 352*61c4878aSAndroid Build Coastguard Worker Example: 353*61c4878aSAndroid Build Coastguard Worker 354*61c4878aSAndroid Build Coastguard Worker .. code-block:: cpp 355*61c4878aSAndroid Build Coastguard Worker 356*61c4878aSAndroid Build Coastguard Worker #include "pw_metric/metric.h" 357*61c4878aSAndroid Build Coastguard Worker #include "pw_metric/global.h" 358*61c4878aSAndroid Build Coastguard Worker 359*61c4878aSAndroid Build Coastguard Worker // No need to coordinate collection of foo and bar; they're autoregistered. 360*61c4878aSAndroid Build Coastguard Worker PW_METRIC_GLOBAL(foo, "foo", 0.2f); 361*61c4878aSAndroid Build Coastguard Worker PW_METRIC_GLOBAL(bar, "bar", 44000u); 362*61c4878aSAndroid Build Coastguard Worker 363*61c4878aSAndroid Build Coastguard Worker Note that metrics defined with ``PW_METRIC_GLOBAL`` should never be added to 364*61c4878aSAndroid Build Coastguard Worker groups defined with ``PW_METRIC_GROUP_GLOBAL``. Each metric can only belong 365*61c4878aSAndroid Build Coastguard Worker to one group, and metrics defined with ``PW_METRIC_GLOBAL`` are 366*61c4878aSAndroid Build Coastguard Worker pre-registered with the global metrics list. 367*61c4878aSAndroid Build Coastguard Worker 368*61c4878aSAndroid Build Coastguard Worker .. attention:: 369*61c4878aSAndroid Build Coastguard Worker Do not create ``PW_METRIC_GLOBAL`` instances anywhere other than global 370*61c4878aSAndroid Build Coastguard Worker scope. Putting these on an instance (member context) would lead to dangling 371*61c4878aSAndroid Build Coastguard Worker pointers and misery. Metrics are never deleted or unregistered! 372*61c4878aSAndroid Build Coastguard Worker 373*61c4878aSAndroid Build Coastguard Worker.. cpp:function:: PW_METRIC_GROUP_GLOBAL(identifier, name, value) 374*61c4878aSAndroid Build Coastguard Worker 375*61c4878aSAndroid Build Coastguard Worker Declare a ``pw::metric::Group`` with name name, and register it in the 376*61c4878aSAndroid Build Coastguard Worker global metric groups list ``pw::metric::global_groups``. 377*61c4878aSAndroid Build Coastguard Worker 378*61c4878aSAndroid Build Coastguard Worker Note that metrics created with ``PW_METRIC_GLOBAL`` should never be added to 379*61c4878aSAndroid Build Coastguard Worker groups! Instead, just create a freestanding metric and register it into the 380*61c4878aSAndroid Build Coastguard Worker global group (like in the example below). 381*61c4878aSAndroid Build Coastguard Worker 382*61c4878aSAndroid Build Coastguard Worker Example: 383*61c4878aSAndroid Build Coastguard Worker 384*61c4878aSAndroid Build Coastguard Worker .. code-block:: cpp 385*61c4878aSAndroid Build Coastguard Worker 386*61c4878aSAndroid Build Coastguard Worker #include "pw_metric/metric.h" 387*61c4878aSAndroid Build Coastguard Worker #include "pw_metric/global.h" 388*61c4878aSAndroid Build Coastguard Worker 389*61c4878aSAndroid Build Coastguard Worker // No need to coordinate collection of this group; it's globally registered. 390*61c4878aSAndroid Build Coastguard Worker PW_METRIC_GROUP_GLOBAL(leagcy_system, "legacy_system"); 391*61c4878aSAndroid Build Coastguard Worker PW_METRIC(leagcy_system, foo, "foo",0.2f); 392*61c4878aSAndroid Build Coastguard Worker PW_METRIC(leagcy_system, bar, "bar",44000u); 393*61c4878aSAndroid Build Coastguard Worker 394*61c4878aSAndroid Build Coastguard Worker .. attention:: 395*61c4878aSAndroid Build Coastguard Worker Do not create ``PW_METRIC_GROUP_GLOBAL`` instances anywhere other than 396*61c4878aSAndroid Build Coastguard Worker global scope. Putting these on an instance (member context) would lead to 397*61c4878aSAndroid Build Coastguard Worker dangling pointers and misery. Metrics are never deleted or unregistered! 398*61c4878aSAndroid Build Coastguard Worker 399*61c4878aSAndroid Build Coastguard Worker.. cpp:function:: PW_METRIC_TOKEN(name) 400*61c4878aSAndroid Build Coastguard Worker 401*61c4878aSAndroid Build Coastguard Worker Declare a ``pw::metric::Token`` (``pw::tokenizer::Token``) for a metric with 402*61c4878aSAndroid Build Coastguard Worker name ``name``. This token matches the ``.name()`` of a metric with the same 403*61c4878aSAndroid Build Coastguard Worker name. 404*61c4878aSAndroid Build Coastguard Worker 405*61c4878aSAndroid Build Coastguard Worker This is a wrapper around ``PW_TOKENIZE_STRING_MASK`` and carries the same 406*61c4878aSAndroid Build Coastguard Worker semantics. 407*61c4878aSAndroid Build Coastguard Worker 408*61c4878aSAndroid Build Coastguard Worker This is unlikely to be used by most pw_metric consumers. 409*61c4878aSAndroid Build Coastguard Worker 410*61c4878aSAndroid Build Coastguard Worker---------------------- 411*61c4878aSAndroid Build Coastguard WorkerUsage & Best Practices 412*61c4878aSAndroid Build Coastguard Worker---------------------- 413*61c4878aSAndroid Build Coastguard WorkerThis library makes several tradeoffs to enable low memory use per-metric, and 414*61c4878aSAndroid Build Coastguard Workerone of those tradeoffs results in requiring care in constructing the metric 415*61c4878aSAndroid Build Coastguard Workertrees. 416*61c4878aSAndroid Build Coastguard Worker 417*61c4878aSAndroid Build Coastguard WorkerUse the Init() pattern for static objects with metrics 418*61c4878aSAndroid Build Coastguard Worker------------------------------------------------------ 419*61c4878aSAndroid Build Coastguard WorkerA common pattern in embedded systems is to allocate many objects globally, and 420*61c4878aSAndroid Build Coastguard Workerreduce reliance on dynamic allocation (or eschew malloc entirely). This leads 421*61c4878aSAndroid Build Coastguard Workerto a pattern where rich/large objects are statically constructed at global 422*61c4878aSAndroid Build Coastguard Workerscope, then interacted with via tasks or threads. For example, consider a 423*61c4878aSAndroid Build Coastguard Workerhypothetical global ``Uart`` object: 424*61c4878aSAndroid Build Coastguard Worker 425*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp 426*61c4878aSAndroid Build Coastguard Worker 427*61c4878aSAndroid Build Coastguard Worker class Uart { 428*61c4878aSAndroid Build Coastguard Worker public: 429*61c4878aSAndroid Build Coastguard Worker Uart(span<std::byte> rx_buffer, span<std::byte> tx_buffer) 430*61c4878aSAndroid Build Coastguard Worker : rx_buffer_(rx_buffer), tx_buffer_(tx_buffer) {} 431*61c4878aSAndroid Build Coastguard Worker 432*61c4878aSAndroid Build Coastguard Worker // Send/receive here... 433*61c4878aSAndroid Build Coastguard Worker 434*61c4878aSAndroid Build Coastguard Worker private: 435*61c4878aSAndroid Build Coastguard Worker pw::span<std::byte> rx_buffer; 436*61c4878aSAndroid Build Coastguard Worker pw::span<std::byte> tx_buffer; 437*61c4878aSAndroid Build Coastguard Worker }; 438*61c4878aSAndroid Build Coastguard Worker 439*61c4878aSAndroid Build Coastguard Worker std::array<std::byte, 512> uart_rx_buffer; 440*61c4878aSAndroid Build Coastguard Worker std::array<std::byte, 512> uart_tx_buffer; 441*61c4878aSAndroid Build Coastguard Worker Uart uart1(uart_rx_buffer, uart_tx_buffer); 442*61c4878aSAndroid Build Coastguard Worker 443*61c4878aSAndroid Build Coastguard WorkerThrough the course of building a product, the team may want to add metrics to 444*61c4878aSAndroid Build Coastguard Workerthe UART to for example gain insight into which operations are triggering lots 445*61c4878aSAndroid Build Coastguard Workerof data transfer. When adding metrics to the above imaginary UART object, one 446*61c4878aSAndroid Build Coastguard Workermight consider the following approach: 447*61c4878aSAndroid Build Coastguard Worker 448*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp 449*61c4878aSAndroid Build Coastguard Worker 450*61c4878aSAndroid Build Coastguard Worker class Uart { 451*61c4878aSAndroid Build Coastguard Worker public: 452*61c4878aSAndroid Build Coastguard Worker Uart(span<std::byte> rx_buffer, 453*61c4878aSAndroid Build Coastguard Worker span<std::byte> tx_buffer, 454*61c4878aSAndroid Build Coastguard Worker Group& parent_metrics) 455*61c4878aSAndroid Build Coastguard Worker : rx_buffer_(rx_buffer), 456*61c4878aSAndroid Build Coastguard Worker tx_buffer_(tx_buffer) { 457*61c4878aSAndroid Build Coastguard Worker // PROBLEM! parent_metrics may not be constructed if it's a reference 458*61c4878aSAndroid Build Coastguard Worker // to a static global. 459*61c4878aSAndroid Build Coastguard Worker parent_metrics.Add(tx_bytes_); 460*61c4878aSAndroid Build Coastguard Worker parent_metrics.Add(rx_bytes_); 461*61c4878aSAndroid Build Coastguard Worker } 462*61c4878aSAndroid Build Coastguard Worker 463*61c4878aSAndroid Build Coastguard Worker // Send/receive here which increment tx/rx_bytes. 464*61c4878aSAndroid Build Coastguard Worker 465*61c4878aSAndroid Build Coastguard Worker private: 466*61c4878aSAndroid Build Coastguard Worker pw::span<std::byte> rx_buffer; 467*61c4878aSAndroid Build Coastguard Worker pw::span<std::byte> tx_buffer; 468*61c4878aSAndroid Build Coastguard Worker 469*61c4878aSAndroid Build Coastguard Worker PW_METRIC(tx_bytes_, "tx_bytes", 0); 470*61c4878aSAndroid Build Coastguard Worker PW_METRIC(rx_bytes_, "rx_bytes", 0); 471*61c4878aSAndroid Build Coastguard Worker }; 472*61c4878aSAndroid Build Coastguard Worker 473*61c4878aSAndroid Build Coastguard Worker PW_METRIC_GROUP(global_metrics, "/"); 474*61c4878aSAndroid Build Coastguard Worker PW_METRIC_GROUP(global_metrics, uart1_metrics, "uart1"); 475*61c4878aSAndroid Build Coastguard Worker 476*61c4878aSAndroid Build Coastguard Worker std::array<std::byte, 512> uart_rx_buffer; 477*61c4878aSAndroid Build Coastguard Worker std::array<std::byte, 512> uart_tx_buffer; 478*61c4878aSAndroid Build Coastguard Worker Uart uart1(uart_rx_buffer, 479*61c4878aSAndroid Build Coastguard Worker uart_tx_buffer, 480*61c4878aSAndroid Build Coastguard Worker uart1_metrics); 481*61c4878aSAndroid Build Coastguard Worker 482*61c4878aSAndroid Build Coastguard WorkerHowever, this **is incorrect**, since the ``parent_metrics`` (pointing to 483*61c4878aSAndroid Build Coastguard Worker``uart1_metrics`` in this case) may not be constructed at the point of 484*61c4878aSAndroid Build Coastguard Worker``uart1`` getting constructed. Thankfully in the case of ``pw_metric`` this 485*61c4878aSAndroid Build Coastguard Workerwill result in an assertion failure (or it will work correctly if the 486*61c4878aSAndroid Build Coastguard Workerconstructors are called in a favorable order), so the problem will not go 487*61c4878aSAndroid Build Coastguard Workerunnoticed. Instead, consider using the ``Init()`` pattern for static objects, 488*61c4878aSAndroid Build Coastguard Workerwhere references to dependencies may only be stored during construction, but no 489*61c4878aSAndroid Build Coastguard Workermethods on the dependencies are called. 490*61c4878aSAndroid Build Coastguard Worker 491*61c4878aSAndroid Build Coastguard WorkerInstead, the ``Init()`` approach separates global object construction into two 492*61c4878aSAndroid Build Coastguard Workerphases: The constructor where references are stored, and a ``Init()`` function 493*61c4878aSAndroid Build Coastguard Workerwhich is called after all static constructors have run. This approach works 494*61c4878aSAndroid Build Coastguard Workercorrectly, even when the objects are allocated globally: 495*61c4878aSAndroid Build Coastguard Worker 496*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp 497*61c4878aSAndroid Build Coastguard Worker 498*61c4878aSAndroid Build Coastguard Worker class Uart { 499*61c4878aSAndroid Build Coastguard Worker public: 500*61c4878aSAndroid Build Coastguard Worker // Note that metrics is not passed in here at all. 501*61c4878aSAndroid Build Coastguard Worker Uart(span<std::byte> rx_buffer, 502*61c4878aSAndroid Build Coastguard Worker span<std::byte> tx_buffer) 503*61c4878aSAndroid Build Coastguard Worker : rx_buffer_(rx_buffer), 504*61c4878aSAndroid Build Coastguard Worker tx_buffer_(tx_buffer) {} 505*61c4878aSAndroid Build Coastguard Worker 506*61c4878aSAndroid Build Coastguard Worker // Precondition: parent_metrics is already constructed. 507*61c4878aSAndroid Build Coastguard Worker void Init(Group& parent_metrics) { 508*61c4878aSAndroid Build Coastguard Worker parent_metrics.Add(tx_bytes_); 509*61c4878aSAndroid Build Coastguard Worker parent_metrics.Add(rx_bytes_); 510*61c4878aSAndroid Build Coastguard Worker } 511*61c4878aSAndroid Build Coastguard Worker 512*61c4878aSAndroid Build Coastguard Worker // Send/receive here which increment tx/rx_bytes. 513*61c4878aSAndroid Build Coastguard Worker 514*61c4878aSAndroid Build Coastguard Worker private: 515*61c4878aSAndroid Build Coastguard Worker pw::span<std::byte> rx_buffer; 516*61c4878aSAndroid Build Coastguard Worker pw::span<std::byte> tx_buffer; 517*61c4878aSAndroid Build Coastguard Worker 518*61c4878aSAndroid Build Coastguard Worker PW_METRIC(tx_bytes_, "tx_bytes", 0); 519*61c4878aSAndroid Build Coastguard Worker PW_METRIC(rx_bytes_, "rx_bytes", 0); 520*61c4878aSAndroid Build Coastguard Worker }; 521*61c4878aSAndroid Build Coastguard Worker 522*61c4878aSAndroid Build Coastguard Worker PW_METRIC_GROUP(root_metrics, "/"); 523*61c4878aSAndroid Build Coastguard Worker PW_METRIC_GROUP(root_metrics, uart1_metrics, "uart1"); 524*61c4878aSAndroid Build Coastguard Worker 525*61c4878aSAndroid Build Coastguard Worker std::array<std::byte, 512> uart_rx_buffer; 526*61c4878aSAndroid Build Coastguard Worker std::array<std::byte, 512> uart_tx_buffer; 527*61c4878aSAndroid Build Coastguard Worker Uart uart1(uart_rx_buffer, 528*61c4878aSAndroid Build Coastguard Worker uart_tx_buffer); 529*61c4878aSAndroid Build Coastguard Worker 530*61c4878aSAndroid Build Coastguard Worker void main() { 531*61c4878aSAndroid Build Coastguard Worker // uart1_metrics is guaranteed to be initialized by this point, so it is 532*61c4878aSAndroid Build Coastguard Worker safe to pass it to Init(). 533*61c4878aSAndroid Build Coastguard Worker uart1.Init(uart1_metrics); 534*61c4878aSAndroid Build Coastguard Worker } 535*61c4878aSAndroid Build Coastguard Worker 536*61c4878aSAndroid Build Coastguard Worker.. attention:: 537*61c4878aSAndroid Build Coastguard Worker Be extra careful about **static global metric registration**. Consider using 538*61c4878aSAndroid Build Coastguard Worker the ``Init()`` pattern. 539*61c4878aSAndroid Build Coastguard Worker 540*61c4878aSAndroid Build Coastguard WorkerMetric member order matters in objects 541*61c4878aSAndroid Build Coastguard Worker-------------------------------------- 542*61c4878aSAndroid Build Coastguard WorkerThe order of declaring in-class groups and metrics matters if the metrics are 543*61c4878aSAndroid Build Coastguard Workerwithin a group declared inside the class. For example, the following class will 544*61c4878aSAndroid Build Coastguard Workerwork fine: 545*61c4878aSAndroid Build Coastguard Worker 546*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp 547*61c4878aSAndroid Build Coastguard Worker 548*61c4878aSAndroid Build Coastguard Worker #include "pw_metric/metric.h" 549*61c4878aSAndroid Build Coastguard Worker 550*61c4878aSAndroid Build Coastguard Worker class PowerSubsystem { 551*61c4878aSAndroid Build Coastguard Worker public: 552*61c4878aSAndroid Build Coastguard Worker Group& metrics() { return metrics_; } 553*61c4878aSAndroid Build Coastguard Worker const Group& metrics() const { return metrics_; } 554*61c4878aSAndroid Build Coastguard Worker 555*61c4878aSAndroid Build Coastguard Worker private: 556*61c4878aSAndroid Build Coastguard Worker PW_METRIC_GROUP(metrics_, "power"); // Note metrics_ declared first. 557*61c4878aSAndroid Build Coastguard Worker PW_METRIC(metrics_, foo, "foo", 0.2f); 558*61c4878aSAndroid Build Coastguard Worker PW_METRIC(metrics_, bar, "bar", 44000u); 559*61c4878aSAndroid Build Coastguard Worker }; 560*61c4878aSAndroid Build Coastguard Worker 561*61c4878aSAndroid Build Coastguard Workerbut the following one will not since the group is constructed after the metrics 562*61c4878aSAndroid Build Coastguard Worker(and will result in a compile error): 563*61c4878aSAndroid Build Coastguard Worker 564*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp 565*61c4878aSAndroid Build Coastguard Worker 566*61c4878aSAndroid Build Coastguard Worker #include "pw_metric/metric.h" 567*61c4878aSAndroid Build Coastguard Worker 568*61c4878aSAndroid Build Coastguard Worker class PowerSubsystem { 569*61c4878aSAndroid Build Coastguard Worker public: 570*61c4878aSAndroid Build Coastguard Worker Group& metrics() { return metrics_; } 571*61c4878aSAndroid Build Coastguard Worker const Group& metrics() const { return metrics_; } 572*61c4878aSAndroid Build Coastguard Worker 573*61c4878aSAndroid Build Coastguard Worker private: 574*61c4878aSAndroid Build Coastguard Worker PW_METRIC(metrics_, foo, "foo", 0.2f); 575*61c4878aSAndroid Build Coastguard Worker PW_METRIC(metrics_, bar, "bar", 44000u); 576*61c4878aSAndroid Build Coastguard Worker PW_METRIC_GROUP(metrics_, "power"); // Error: metrics_ must be first. 577*61c4878aSAndroid Build Coastguard Worker }; 578*61c4878aSAndroid Build Coastguard Worker 579*61c4878aSAndroid Build Coastguard Worker.. attention:: 580*61c4878aSAndroid Build Coastguard Worker 581*61c4878aSAndroid Build Coastguard Worker Put **groups before metrics** when declaring metrics members inside classes. 582*61c4878aSAndroid Build Coastguard Worker 583*61c4878aSAndroid Build Coastguard WorkerThread safety 584*61c4878aSAndroid Build Coastguard Worker------------- 585*61c4878aSAndroid Build Coastguard Worker``pw_metric`` has **no built-in synchronization for manipulating the tree** 586*61c4878aSAndroid Build Coastguard Workerstructure. Users are expected to either rely on shared global mutex when 587*61c4878aSAndroid Build Coastguard Workerconstructing the metric tree, or do the metric construction in a single thread 588*61c4878aSAndroid Build Coastguard Worker(e.g. a boot/init thread). The same applies for destruction, though we do not 589*61c4878aSAndroid Build Coastguard Workeradvise destructing metrics or groups. 590*61c4878aSAndroid Build Coastguard Worker 591*61c4878aSAndroid Build Coastguard WorkerIndividual metrics have atomic ``Increment()``, ``Set()``, and the value 592*61c4878aSAndroid Build Coastguard Workeraccessors ``as_float()`` and ``as_int()`` which don't require separate 593*61c4878aSAndroid Build Coastguard Workersynchronization, and can be used from ISRs. 594*61c4878aSAndroid Build Coastguard Worker 595*61c4878aSAndroid Build Coastguard Worker.. attention:: 596*61c4878aSAndroid Build Coastguard Worker 597*61c4878aSAndroid Build Coastguard Worker **You must synchronize access to metrics**. ``pw_metrics`` does not 598*61c4878aSAndroid Build Coastguard Worker internally synchronize access during construction. Metric Set/Increment are 599*61c4878aSAndroid Build Coastguard Worker safe. 600*61c4878aSAndroid Build Coastguard Worker 601*61c4878aSAndroid Build Coastguard WorkerLifecycle 602*61c4878aSAndroid Build Coastguard Worker--------- 603*61c4878aSAndroid Build Coastguard WorkerMetric objects are not designed to be destructed, and are expected to live for 604*61c4878aSAndroid Build Coastguard Workerthe lifetime of the program or application. If you need dynamic 605*61c4878aSAndroid Build Coastguard Workercreation/destruction of metrics, ``pw_metric`` does not attempt to cover that 606*61c4878aSAndroid Build Coastguard Workeruse case. Instead, ``pw_metric`` covers the case of products with two execution 607*61c4878aSAndroid Build Coastguard Workerphases: 608*61c4878aSAndroid Build Coastguard Worker 609*61c4878aSAndroid Build Coastguard Worker1. A boot phase where the metric tree is created. 610*61c4878aSAndroid Build Coastguard Worker2. A run phase where metrics are collected. The tree structure is fixed. 611*61c4878aSAndroid Build Coastguard Worker 612*61c4878aSAndroid Build Coastguard WorkerTechnically, it is possible to destruct metrics provided care is taken to 613*61c4878aSAndroid Build Coastguard Workerremove the given metric (or group) from the list it's contained in. However, 614*61c4878aSAndroid Build Coastguard Workerthere are no helper functions for this, so be careful. 615*61c4878aSAndroid Build Coastguard Worker 616*61c4878aSAndroid Build Coastguard WorkerBelow is an example that **is incorrect**. Don't do what follows! 617*61c4878aSAndroid Build Coastguard Worker 618*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp 619*61c4878aSAndroid Build Coastguard Worker 620*61c4878aSAndroid Build Coastguard Worker #include "pw_metric/metric.h" 621*61c4878aSAndroid Build Coastguard Worker 622*61c4878aSAndroid Build Coastguard Worker void main() { 623*61c4878aSAndroid Build Coastguard Worker PW_METRIC_GROUP(root, "/"); 624*61c4878aSAndroid Build Coastguard Worker { 625*61c4878aSAndroid Build Coastguard Worker // BAD! The metrics have a different lifetime than the group. 626*61c4878aSAndroid Build Coastguard Worker PW_METRIC(root, temperature, "temperature_f", 72.3f); 627*61c4878aSAndroid Build Coastguard Worker PW_METRIC(root, humidity, "humidity_relative_percent", 33.2f); 628*61c4878aSAndroid Build Coastguard Worker } 629*61c4878aSAndroid Build Coastguard Worker // OOPS! root now has a linked list that points to the destructed 630*61c4878aSAndroid Build Coastguard Worker // "humidity" object. 631*61c4878aSAndroid Build Coastguard Worker } 632*61c4878aSAndroid Build Coastguard Worker 633*61c4878aSAndroid Build Coastguard Worker.. attention:: 634*61c4878aSAndroid Build Coastguard Worker **Don't destruct metrics**. Metrics are designed to be registered / 635*61c4878aSAndroid Build Coastguard Worker structured upfront, then manipulated during a device's active phase. They do 636*61c4878aSAndroid Build Coastguard Worker not support destruction. 637*61c4878aSAndroid Build Coastguard Worker 638*61c4878aSAndroid Build Coastguard Worker.. _module-pw_metric-exporting: 639*61c4878aSAndroid Build Coastguard Worker 640*61c4878aSAndroid Build Coastguard Worker----------------- 641*61c4878aSAndroid Build Coastguard WorkerExporting metrics 642*61c4878aSAndroid Build Coastguard Worker----------------- 643*61c4878aSAndroid Build Coastguard WorkerCollecting metrics on a device is not useful without a mechanism to export 644*61c4878aSAndroid Build Coastguard Workerthose metrics for analysis and debugging. ``pw_metric`` offers optional RPC 645*61c4878aSAndroid Build Coastguard Workerservice libraries (``:metric_service_nanopb`` based on nanopb, and 646*61c4878aSAndroid Build Coastguard Worker``:metric_service_pwpb`` based on pw_protobuf) that enable exporting a 647*61c4878aSAndroid Build Coastguard Workeruser-supplied set of on-device metrics via RPC. This facility is intended to 648*61c4878aSAndroid Build Coastguard Workerfunction from the early stages of device bringup through production in the 649*61c4878aSAndroid Build Coastguard Workerfield. 650*61c4878aSAndroid Build Coastguard Worker 651*61c4878aSAndroid Build Coastguard WorkerThe metrics are fetched by calling the ``MetricService.Get`` RPC method, which 652*61c4878aSAndroid Build Coastguard Workerstreams all registered metrics to the caller in batches (server streaming RPC). 653*61c4878aSAndroid Build Coastguard WorkerBatching the returned metrics avoids requiring a large buffer or large RPC MTU. 654*61c4878aSAndroid Build Coastguard Worker 655*61c4878aSAndroid Build Coastguard WorkerThe returned metric objects have flattened paths to the root. For example, the 656*61c4878aSAndroid Build Coastguard Workerreturned metrics (post detokenization and jsonified) might look something like: 657*61c4878aSAndroid Build Coastguard Worker 658*61c4878aSAndroid Build Coastguard Worker.. code-block:: json 659*61c4878aSAndroid Build Coastguard Worker 660*61c4878aSAndroid Build Coastguard Worker { 661*61c4878aSAndroid Build Coastguard Worker "/i2c1/failed_txns": 17, 662*61c4878aSAndroid Build Coastguard Worker "/i2c1/total_txns": 2013, 663*61c4878aSAndroid Build Coastguard Worker "/i2c1/gyro/resets": 24, 664*61c4878aSAndroid Build Coastguard Worker "/i2c1/gyro/hangs": 1, 665*61c4878aSAndroid Build Coastguard Worker "/spi1/thermocouple/reads": 242, 666*61c4878aSAndroid Build Coastguard Worker "/spi1/thermocouple/temp_celsius": 34.52, 667*61c4878aSAndroid Build Coastguard Worker } 668*61c4878aSAndroid Build Coastguard Worker 669*61c4878aSAndroid Build Coastguard WorkerNote that there is no nesting of the groups; the nesting is implied from the 670*61c4878aSAndroid Build Coastguard Workerpath. 671*61c4878aSAndroid Build Coastguard Worker 672*61c4878aSAndroid Build Coastguard WorkerRPC service setup 673*61c4878aSAndroid Build Coastguard Worker----------------- 674*61c4878aSAndroid Build Coastguard WorkerTo expose a ``MetricService`` in your application, do the following: 675*61c4878aSAndroid Build Coastguard Worker 676*61c4878aSAndroid Build Coastguard Worker1. Define metrics around the system, and put them in a group or list of 677*61c4878aSAndroid Build Coastguard Worker metrics. Easy choices include for example the ``global_groups`` and 678*61c4878aSAndroid Build Coastguard Worker ``global_metrics`` variables; or creat your own. 679*61c4878aSAndroid Build Coastguard Worker2. Create an instance of ``pw::metric::MetricService``. 680*61c4878aSAndroid Build Coastguard Worker3. Register the service with your RPC server. 681*61c4878aSAndroid Build Coastguard Worker 682*61c4878aSAndroid Build Coastguard WorkerFor example: 683*61c4878aSAndroid Build Coastguard Worker 684*61c4878aSAndroid Build Coastguard Worker.. code-block:: cpp 685*61c4878aSAndroid Build Coastguard Worker 686*61c4878aSAndroid Build Coastguard Worker #include "pw_rpc/server.h" 687*61c4878aSAndroid Build Coastguard Worker #include "pw_metric/metric.h" 688*61c4878aSAndroid Build Coastguard Worker #include "pw_metric/global.h" 689*61c4878aSAndroid Build Coastguard Worker #include "pw_metric/metric_service_nanopb.h" 690*61c4878aSAndroid Build Coastguard Worker 691*61c4878aSAndroid Build Coastguard Worker // Note: You must customize the RPC server setup; see pw_rpc. 692*61c4878aSAndroid Build Coastguard Worker Channel channels[] = { 693*61c4878aSAndroid Build Coastguard Worker Channel::Create<1>(&uart_output), 694*61c4878aSAndroid Build Coastguard Worker }; 695*61c4878aSAndroid Build Coastguard Worker Server server(channels); 696*61c4878aSAndroid Build Coastguard Worker 697*61c4878aSAndroid Build Coastguard Worker // Metric service instance, pointing to the global metric objects. 698*61c4878aSAndroid Build Coastguard Worker // This could also point to custom per-product or application objects. 699*61c4878aSAndroid Build Coastguard Worker pw::metric::MetricService metric_service( 700*61c4878aSAndroid Build Coastguard Worker pw::metric::global_metrics, 701*61c4878aSAndroid Build Coastguard Worker pw::metric::global_groups); 702*61c4878aSAndroid Build Coastguard Worker 703*61c4878aSAndroid Build Coastguard Worker void RegisterServices() { 704*61c4878aSAndroid Build Coastguard Worker server.RegisterService(metric_service); 705*61c4878aSAndroid Build Coastguard Worker // Register other services here. 706*61c4878aSAndroid Build Coastguard Worker } 707*61c4878aSAndroid Build Coastguard Worker 708*61c4878aSAndroid Build Coastguard Worker void main() { 709*61c4878aSAndroid Build Coastguard Worker // ... system initialization ... 710*61c4878aSAndroid Build Coastguard Worker 711*61c4878aSAndroid Build Coastguard Worker RegisterServices(); 712*61c4878aSAndroid Build Coastguard Worker 713*61c4878aSAndroid Build Coastguard Worker // ... start your applcation ... 714*61c4878aSAndroid Build Coastguard Worker } 715*61c4878aSAndroid Build Coastguard Worker 716*61c4878aSAndroid Build Coastguard Worker.. attention:: 717*61c4878aSAndroid Build Coastguard Worker Take care when exporting metrics. Ensure **appropriate access control** is in 718*61c4878aSAndroid Build Coastguard Worker place. In some cases it may make sense to entirely disable metrics export for 719*61c4878aSAndroid Build Coastguard Worker production builds. Although reading metrics via RPC won't influence the 720*61c4878aSAndroid Build Coastguard Worker device, in some cases the metrics could expose sensitive information if 721*61c4878aSAndroid Build Coastguard Worker product owners are not careful. 722*61c4878aSAndroid Build Coastguard Worker 723*61c4878aSAndroid Build Coastguard Worker.. attention:: 724*61c4878aSAndroid Build Coastguard Worker **MetricService::Get is a synchronous RPC method** 725*61c4878aSAndroid Build Coastguard Worker 726*61c4878aSAndroid Build Coastguard Worker Calls to is ``MetricService::Get`` are blocking and will send all metrics 727*61c4878aSAndroid Build Coastguard Worker immediately, even though it is a server-streaming RPC. This will work fine if 728*61c4878aSAndroid Build Coastguard Worker the device doesn't have too many metrics, or doesn't have concurrent RPCs 729*61c4878aSAndroid Build Coastguard Worker like logging, but could be a problem in some cases. 730*61c4878aSAndroid Build Coastguard Worker 731*61c4878aSAndroid Build Coastguard Worker We plan to offer an async version where the application is responsible for 732*61c4878aSAndroid Build Coastguard Worker pumping the metrics into the streaming response. This gives flow control to 733*61c4878aSAndroid Build Coastguard Worker the application. 734*61c4878aSAndroid Build Coastguard Worker 735*61c4878aSAndroid Build Coastguard Worker----------- 736*61c4878aSAndroid Build Coastguard WorkerSize report 737*61c4878aSAndroid Build Coastguard Worker----------- 738*61c4878aSAndroid Build Coastguard WorkerThe below size report shows the cost in code and memory for a few examples of 739*61c4878aSAndroid Build Coastguard Workermetrics. This does not include the RPC service. 740*61c4878aSAndroid Build Coastguard Worker 741*61c4878aSAndroid Build Coastguard Worker.. include:: metric_size_report 742*61c4878aSAndroid Build Coastguard Worker 743*61c4878aSAndroid Build Coastguard Worker.. attention:: 744*61c4878aSAndroid Build Coastguard Worker At time of writing, **the above sizes show an unexpectedly large flash 745*61c4878aSAndroid Build Coastguard Worker impact**. We are investigating why GCC is inserting large global static 746*61c4878aSAndroid Build Coastguard Worker constructors per group, when all the logic should be reused across objects. 747*61c4878aSAndroid Build Coastguard Worker 748*61c4878aSAndroid Build Coastguard Worker------------- 749*61c4878aSAndroid Build Coastguard WorkerMetric Parser 750*61c4878aSAndroid Build Coastguard Worker------------- 751*61c4878aSAndroid Build Coastguard WorkerThe metric_parser Python Module requests the system metrics via RPC, then parses the 752*61c4878aSAndroid Build Coastguard Workerresponse while detokenizing the group and metrics names, and returns the metrics 753*61c4878aSAndroid Build Coastguard Workerin a dictionary organized by group and value. 754*61c4878aSAndroid Build Coastguard Worker 755*61c4878aSAndroid Build Coastguard Worker---------------- 756*61c4878aSAndroid Build Coastguard WorkerDesign tradeoffs 757*61c4878aSAndroid Build Coastguard Worker---------------- 758*61c4878aSAndroid Build Coastguard WorkerThere are many possible approaches to metrics collection and aggregation. We've 759*61c4878aSAndroid Build Coastguard Workerchosen some points on the tradeoff curve: 760*61c4878aSAndroid Build Coastguard Worker 761*61c4878aSAndroid Build Coastguard Worker- **Atomic-sized metrics** - Using simple metric objects with just uint32/float 762*61c4878aSAndroid Build Coastguard Worker enables atomic operations. While it might be nice to support larger types, it 763*61c4878aSAndroid Build Coastguard Worker is more useful to have safe metrics increment from interrupt subroutines. 764*61c4878aSAndroid Build Coastguard Worker 765*61c4878aSAndroid Build Coastguard Worker- **No aggregate metrics (yet)** - Aggregate metrics (e.g. average, max, min, 766*61c4878aSAndroid Build Coastguard Worker histograms) are not supported, and must be built on top of the simple base 767*61c4878aSAndroid Build Coastguard Worker metrics. By taking this route, we can considerably simplify the core metrics 768*61c4878aSAndroid Build Coastguard Worker system and have aggregation logic in separate modules. Those modules can then 769*61c4878aSAndroid Build Coastguard Worker feed into the metrics system - for example by creating multiple metrics for a 770*61c4878aSAndroid Build Coastguard Worker single underlying metric. For example: "foo", "foo_max", "foo_min" and so on. 771*61c4878aSAndroid Build Coastguard Worker 772*61c4878aSAndroid Build Coastguard Worker The other problem with automatic aggregation is that what period the 773*61c4878aSAndroid Build Coastguard Worker aggregation happens over is often important, and it can be hard to design 774*61c4878aSAndroid Build Coastguard Worker this cleanly into the API. Instead, this responsibility is pushed to the user 775*61c4878aSAndroid Build Coastguard Worker who must take more care. 776*61c4878aSAndroid Build Coastguard Worker 777*61c4878aSAndroid Build Coastguard Worker Note that we will add helpers for aggregated metrics. 778*61c4878aSAndroid Build Coastguard Worker 779*61c4878aSAndroid Build Coastguard Worker- **No virtual metrics** - An alternate approach to the concrete Metric class 780*61c4878aSAndroid Build Coastguard Worker in the current module is to have a virtual interface for metrics, and then 781*61c4878aSAndroid Build Coastguard Worker allow those metrics to have their own storage. This is attractive but can 782*61c4878aSAndroid Build Coastguard Worker lead to many vtables and excess memory use in simple one-metric use cases. 783*61c4878aSAndroid Build Coastguard Worker 784*61c4878aSAndroid Build Coastguard Worker- **Linked list registration** - Using linked lists for registration is a 785*61c4878aSAndroid Build Coastguard Worker tradeoff, accepting some memory overhead in exchange for flexibility. Other 786*61c4878aSAndroid Build Coastguard Worker alternatives include a global table of metrics, which has the disadvantage of 787*61c4878aSAndroid Build Coastguard Worker requiring centralizing the metrics -- an impossibility for middleware like 788*61c4878aSAndroid Build Coastguard Worker Pigweed. 789*61c4878aSAndroid Build Coastguard Worker 790*61c4878aSAndroid Build Coastguard Worker- **Synchronization** - The only synchronization guarantee provided by 791*61c4878aSAndroid Build Coastguard Worker pw_metric is that increment and set are atomic. Other than that, users are on 792*61c4878aSAndroid Build Coastguard Worker their own to synchonize metric collection and updating. 793*61c4878aSAndroid Build Coastguard Worker 794*61c4878aSAndroid Build Coastguard Worker- **No fast metric lookup** - The current design does not make it fast to 795*61c4878aSAndroid Build Coastguard Worker lookup a metric at runtime; instead, one must run a linear search of the tree 796*61c4878aSAndroid Build Coastguard Worker to find the matching metric. In most non-dynamic use cases, this is fine in 797*61c4878aSAndroid Build Coastguard Worker practice, and saves having a more involved hash table. Metric updates will be 798*61c4878aSAndroid Build Coastguard Worker through direct member or variable accesses. 799*61c4878aSAndroid Build Coastguard Worker 800*61c4878aSAndroid Build Coastguard Worker- **Relying on C++ static initialization** - In short, the convenience 801*61c4878aSAndroid Build Coastguard Worker outweighs the cost and risk. Without static initializers, it would be 802*61c4878aSAndroid Build Coastguard Worker impossible to automatically collect the metrics without post-processing the 803*61c4878aSAndroid Build Coastguard Worker C++ code to find the metrics; a huge and debatably worthwhile approach. We 804*61c4878aSAndroid Build Coastguard Worker have carefully analyzed the static initializer behaviour of Pigweed's 805*61c4878aSAndroid Build Coastguard Worker IntrusiveList and are confident it is correct. 806*61c4878aSAndroid Build Coastguard Worker 807*61c4878aSAndroid Build Coastguard Worker- **Both local & global support** - Potentially just one approach (the local or 808*61c4878aSAndroid Build Coastguard Worker global one) could be offered, making the module less complex. However, we 809*61c4878aSAndroid Build Coastguard Worker feel the additional complexity is worthwhile since there are legimitate use 810*61c4878aSAndroid Build Coastguard Worker cases for both e.g. ``PW_METRIC`` and ``PW_METRIC_GLOBAL``. We'd prefer to 811*61c4878aSAndroid Build Coastguard Worker have a well-tested upstream solution for these use cases rather than have 812*61c4878aSAndroid Build Coastguard Worker customers re-implement one of these. 813*61c4878aSAndroid Build Coastguard Worker 814*61c4878aSAndroid Build Coastguard Worker---------------- 815*61c4878aSAndroid Build Coastguard WorkerRoadmap & Status 816*61c4878aSAndroid Build Coastguard Worker---------------- 817*61c4878aSAndroid Build Coastguard Worker- **String metric names** - ``pw_metric`` stores metric names as tokens. On one 818*61c4878aSAndroid Build Coastguard Worker hand, this is great for production where having a compact binary is often a 819*61c4878aSAndroid Build Coastguard Worker requirement to fit the application in the given part. However, in early 820*61c4878aSAndroid Build Coastguard Worker development before flash is a constraint, string names are more convenient to 821*61c4878aSAndroid Build Coastguard Worker work with since there is no need for host-side detokenization. We plan to add 822*61c4878aSAndroid Build Coastguard Worker optional support for using supporting strings. 823*61c4878aSAndroid Build Coastguard Worker 824*61c4878aSAndroid Build Coastguard Worker- **Aggregate metrics** - We plan to add support for aggregate metrics on top 825*61c4878aSAndroid Build Coastguard Worker of the simple metric mechanism, either as another module or as additional 826*61c4878aSAndroid Build Coastguard Worker functionality inside this one. Likely examples include min/max, 827*61c4878aSAndroid Build Coastguard Worker 828*61c4878aSAndroid Build Coastguard Worker- **Selectively enable or disable metrics** - Currently the metrics are always 829*61c4878aSAndroid Build Coastguard Worker enabled once included. In practice this is not ideal since many times only a 830*61c4878aSAndroid Build Coastguard Worker few metrics are wanted in production, but having to strip all the metrics 831*61c4878aSAndroid Build Coastguard Worker code is error prone. Instead, we will add support for controlling what 832*61c4878aSAndroid Build Coastguard Worker metrics are enabled or disabled at compile time. This may rely on of C++20's 833*61c4878aSAndroid Build Coastguard Worker support for zero-sized members to fully remove the cost. 834*61c4878aSAndroid Build Coastguard Worker 835*61c4878aSAndroid Build Coastguard Worker- **Async RPC** - The current RPC service exports the metrics by streaming 836*61c4878aSAndroid Build Coastguard Worker them to the client in batches. However, the current solution streams all the 837*61c4878aSAndroid Build Coastguard Worker metrics to completion; this may block the RPC thread. In the future we will 838*61c4878aSAndroid Build Coastguard Worker have an async solution where the user is in control of flow priority. 839*61c4878aSAndroid Build Coastguard Worker 840*61c4878aSAndroid Build Coastguard Worker- **Timer integration** - We would like to add a stopwatch type mechanism to 841*61c4878aSAndroid Build Coastguard Worker time multiple in-flight events. 842*61c4878aSAndroid Build Coastguard Worker 843*61c4878aSAndroid Build Coastguard Worker- **C support** - In practice it's often useful or necessary to instrument 844*61c4878aSAndroid Build Coastguard Worker C-only code. While it will be impossible to support the global registration 845*61c4878aSAndroid Build Coastguard Worker system that the C++ version supports, we will figure out a solution to make 846*61c4878aSAndroid Build Coastguard Worker instrumenting C code relatively smooth. 847*61c4878aSAndroid Build Coastguard Worker 848*61c4878aSAndroid Build Coastguard Worker- **Global counter** - We may add a global metric counter to help detect cases 849*61c4878aSAndroid Build Coastguard Worker where post-initialization metrics manipulations are done. 850*61c4878aSAndroid Build Coastguard Worker 851*61c4878aSAndroid Build Coastguard Worker- **Proto structure** - It may be possible to directly map metrics to a custom 852*61c4878aSAndroid Build Coastguard Worker proto structure, where instead of a name or token field, a tag field is 853*61c4878aSAndroid Build Coastguard Worker provided. This could result in elegant export to an easily machine parsable 854*61c4878aSAndroid Build Coastguard Worker and compact representation on the host. We may investigate this in the 855*61c4878aSAndroid Build Coastguard Worker future. 856*61c4878aSAndroid Build Coastguard Worker 857*61c4878aSAndroid Build Coastguard Worker- **Safer data structures** - At a cost of 4B per metric and 4B per group, it 858*61c4878aSAndroid Build Coastguard Worker may be possible to make metric structure instantiation safe even in static 859*61c4878aSAndroid Build Coastguard Worker constructors, and also make it safe to remove metrics dynamically. We will 860*61c4878aSAndroid Build Coastguard Worker consider whether this tradeoff is the right one, since a 4B cost per metric 861*61c4878aSAndroid Build Coastguard Worker is substantial on projects with many metrics. 862