blob: 008241dfd85e4055c27f856d1cd1b8ff9dcae1f4 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include "sysinit/sysinit.h"
#include "syscfg/syscfg.h"
#include "os/os.h"
#include "metrics/metrics.h"
#include "tinycbor/cbor.h"
#include "tinycbor/cbor_mbuf_writer.h"
#include "log/log.h"
#include "metrics_priv.h"
#define MBUF_MEMBLOCK_OVERHEAD \
(sizeof(struct os_mbuf))
#define MEMPOOL_COUNT MYNEWT_VAL(METRICS_POOL_COUNT)
#define MEMPOOL_SIZE MYNEWT_VAL(METRICS_POOL_SIZE)
#define METRICS_TYPE_SERIES_MASK 0x80
#define METRICS_TYPE_SIGNED_MASK 0x40
#define METRICS_TYPE_SIZE_MASK 0x0f
union metrics_metric_val {
uintptr_t notused;
uint32_t val;
struct os_mbuf *series;
};
struct metrics_event {
struct metrics_event_hdr hdr;
union metrics_metric_val vals[0];
};
/*
* This is aligned size for mempool block to make sure we do not write series
* data across mbuf boundary in chain - it simplifies code a lot.
*/
#define MEMPOOL_SIZE_ALIGNED \
OS_ALIGN(MBUF_MEMBLOCK_OVERHEAD + MEMPOOL_SIZE, sizeof(uint32_t))
static os_membuf_t event_metric_data[ OS_MEMPOOL_SIZE(MEMPOOL_COUNT, MEMPOOL_SIZE) ];
static struct os_mbuf_pool event_metric_mbuf_pool;
static struct os_mempool event_metric_mempool;
int
metrics_event_init(struct metrics_event_hdr *hdr,
const struct metrics_metric_def *metrics, uint8_t count,
const char *name)
{
assert((count > 0) && (count <= 32));
memset(hdr, 0, sizeof(*hdr) + count * sizeof(union metrics_metric_val));
hdr->name = name;
hdr->count = count;
hdr->defs = metrics;
hdr->enabled = UINT32_MAX;
return 0;
}
int
metrics_event_register(struct metrics_event_hdr *hdr)
{
return metrics_cli_register_event(hdr);
}
int
metrics_event_set_log(struct metrics_event_hdr *hdr, struct log *log,
int module, int level)
{
hdr->log = log;
hdr->log_module = module;
hdr->log_level = level;
return 0;
}
int
metrics_event_start(struct metrics_event_hdr *hdr, uint32_t timestamp)
{
if (hdr->set) {
metrics_event_end(hdr);
}
hdr->timestamp = timestamp;
return 0;
}
int
metrics_event_end(struct metrics_event_hdr *hdr)
{
struct metrics_event *em = (struct metrics_event *)hdr;
const struct metrics_metric_def *def;
union metrics_metric_val *v;
struct os_mbuf *om;
int ret;
int i;
if (hdr->log) {
om = metrics_get_mbuf();
if (om) {
os_mbuf_extend(om, sizeof(struct log_entry_hdr));
if (!metrics_event_to_cbor(hdr, om)) {
log_append_mbuf_typed(hdr->log, hdr->log_module, hdr->log_level,
LOG_ETYPE_CBOR, om);
} else {
ret = -1;
}
} else {
ret = -1;
}
}
hdr->set = 0;
for (i = 0; i < hdr->count; i++) {
def = &hdr->defs[i];
v = &em->vals[i];
if (def->type & METRICS_TYPE_SERIES_MASK) {
if (v->series) {
os_mbuf_free_chain(v->series);
}
v->series = NULL;
} else {
v->val = 0;
}
}
return ret;
}
int
metrics_set_state(struct metrics_event_hdr *hdr, uint8_t metric, bool state)
{
assert(metric < hdr->count);
if (state) {
hdr->enabled |= (1 << metric);
} else {
hdr->enabled &= ~(1 << metric);
}
return 0;
}
int
metrics_set_state_mask(struct metrics_event_hdr *hdr, uint32_t mask)
{
hdr->enabled |= mask;
return 0;
}
int
metrics_clr_state_mask(struct metrics_event_hdr *hdr, uint32_t mask)
{
hdr->enabled &= ~mask;
return 0;
}
uint32_t
metrics_get_state_mask(struct metrics_event_hdr *hdr)
{
return hdr->enabled;
}
static int
set_single_value(struct metrics_event_hdr *hdr, uint8_t metric,
uint32_t val)
{
struct metrics_event *em = (struct metrics_event *)hdr;
union metrics_metric_val *v;
v = &em->vals[metric];
v->val = val;
hdr->set |= (1 << metric);
return 0;
}
static int
set_series_value(struct metrics_event_hdr *hdr, uint8_t metric,
uint32_t val, uint8_t type)
{
struct metrics_event *em = (struct metrics_event *)hdr;
union metrics_metric_val *v;
uint16_t type_len;
v = &em->vals[metric];
if (!v->series) {
v->series = os_mbuf_get(&event_metric_mbuf_pool, 0);
}
val = htole32(val);
type_len = type & METRICS_TYPE_SIZE_MASK;
assert((type_len == 1) || (type_len == 2) || (type_len == 4));
os_mbuf_append(v->series, &val, type_len);
hdr->set |= (1 << metric);
return 0;
}
int
metrics_set_value(struct metrics_event_hdr *hdr, uint8_t metric,
uint32_t val)
{
const struct metrics_metric_def *def;
assert(metric < hdr->count);
if ((hdr->enabled & (1 << metric)) == 0) {
return 0;
}
def = &hdr->defs[metric];
if (def->type & METRICS_TYPE_SERIES_MASK) {
return set_series_value(hdr, metric, val, def->type);
} else {
return set_single_value(hdr, metric, val);
}
return -1;
}
int
metrics_set_single_value(struct metrics_event_hdr *hdr, uint8_t metric, uint32_t val)
{
const struct metrics_metric_def *def;
assert(metric < hdr->count);
def = &hdr->defs[metric];
if (def->type & METRICS_TYPE_SERIES_MASK) {
return -1;
}
if ((hdr->enabled & (1 << metric)) == 0) {
return 0;
}
return set_single_value(hdr, metric, val);
}
int
metrics_set_series_value(struct metrics_event_hdr *hdr, uint8_t metric, uint32_t val)
{
const struct metrics_metric_def *def;
assert(metric < hdr->count);
def = &hdr->defs[metric];
if ((def->type & METRICS_TYPE_SERIES_MASK) == 0) {
return -1;
}
if ((hdr->enabled & (1 << metric)) == 0) {
return 0;
}
return set_series_value(hdr, metric, val, def->type);
}
static int
append_series_u8_to_cbor(CborEncoder *encoder, struct os_mbuf *om)
{
uint8_t *ptr;
while (om) {
ptr = om->om_data;
while (ptr < om->om_data + om->om_len) {
if (cbor_encode_uint(encoder, *ptr)) {
return -1;
}
ptr += sizeof(uint8_t);
}
om = SLIST_NEXT(om, om_next);
}
return 0;
}
static int
append_series_s8_to_cbor(CborEncoder *encoder, struct os_mbuf *om)
{
uint8_t *ptr;
while (om) {
ptr = om->om_data;
while (ptr < om->om_data + om->om_len) {
if (cbor_encode_int(encoder, (int8_t)*ptr)) {
return -1;
}
ptr += sizeof(uint8_t);
}
om = SLIST_NEXT(om, om_next);
}
return 0;
}
static int
append_series_u16_to_cbor(CborEncoder *encoder, struct os_mbuf *om)
{
uint16_t u16;
uint8_t *ptr;
while (om) {
ptr = om->om_data;
while (ptr < om->om_data + om->om_len) {
memcpy(&u16, ptr, sizeof(uint16_t));
u16 = le16toh(u16);
if (cbor_encode_uint(encoder, u16)) {
return -1;
}
ptr += sizeof(uint16_t);
}
om = SLIST_NEXT(om, om_next);
}
return 0;
}
static int
append_series_s16_to_cbor(CborEncoder *encoder, struct os_mbuf *om)
{
int16_t s16;
uint8_t *ptr;
while (om) {
ptr = om->om_data;
while (ptr < om->om_data + om->om_len) {
memcpy(&s16, ptr, sizeof(int16_t));
s16 = le16toh(s16);
if (cbor_encode_int(encoder, s16)) {
return -1;
}
ptr += sizeof(int16_t);
}
om = SLIST_NEXT(om, om_next);
}
return 0;
}
static int
append_series_u32_to_cbor(CborEncoder *encoder, struct os_mbuf *om)
{
uint32_t u32;
uint8_t *ptr;
while (om) {
ptr = om->om_data;
while (ptr < om->om_data + om->om_len) {
memcpy(&u32, ptr, sizeof(uint32_t));
u32 = le32toh(u32);
if (cbor_encode_uint(encoder, u32)) {
return -1;
}
ptr += sizeof(uint32_t);
}
om = SLIST_NEXT(om, om_next);
}
return 0;
}
static int
append_series_s32_to_cbor(CborEncoder *encoder, struct os_mbuf *om)
{
int32_t s32;
uint8_t *ptr;
while (om) {
ptr = om->om_data;
while (ptr < om->om_data + om->om_len) {
memcpy(&s32, ptr, sizeof(int32_t));
s32 = le32toh(s32);
if (cbor_encode_int(encoder, s32)) {
return -1;
}
ptr += sizeof(int32_t);
}
om = SLIST_NEXT(om, om_next);
}
return 0;
}
int
metrics_event_to_cbor(struct metrics_event_hdr *hdr, struct os_mbuf *om)
{
struct metrics_event *em = (struct metrics_event *)hdr;
const struct metrics_metric_def *def;
union metrics_metric_val *v;
struct cbor_mbuf_writer writer;
struct CborEncoder encoder;
struct CborEncoder map;
struct CborEncoder arr;
int i;
int rc;
cbor_mbuf_writer_init(&writer, om);
cbor_encoder_init(&encoder, &writer.enc, 0);
rc = cbor_encoder_create_map(&encoder, &map, CborIndefiniteLength);
if (rc != 0) {
return -1;
}
cbor_encode_text_stringz(&map, "ev");
cbor_encode_text_stringz(&map, hdr->name);
cbor_encode_text_stringz(&map, "ts");
cbor_encode_uint(&map, hdr->timestamp);
for (i = 0; i < hdr->count; i++) {
/* Skip if not enabled */
if ((hdr->enabled & (1 << i)) == 0) {
continue;
}
def = &hdr->defs[i];
v = &em->vals[i];
cbor_encode_text_stringz(&map, def->name);
/* Write as null if value was not set */
if ((hdr->set & (1 << i)) == 0) {
cbor_encode_null(&map);
continue;
}
if ((def->type & METRICS_TYPE_SERIES_MASK) == 0) {
if (def->type & METRICS_TYPE_SIGNED_MASK) {
cbor_encode_int(&map, (int32_t)v->val);
} else {
cbor_encode_uint(&map, v->val);
}
continue;
}
rc = cbor_encoder_create_array(&map, &arr, CborIndefiniteLength);
if (rc != 0) {
return -1;
}
switch (def->type) {
case METRICS_TYPE_SERIES_U8:
append_series_u8_to_cbor(&arr, v->series);
break;
case METRICS_TYPE_SERIES_S8:
append_series_s8_to_cbor(&arr, v->series);
break;
case METRICS_TYPE_SERIES_U16:
append_series_u16_to_cbor(&arr, v->series);
break;
case METRICS_TYPE_SERIES_S16:
append_series_s16_to_cbor(&arr, v->series);
break;
case METRICS_TYPE_SERIES_U32:
append_series_u32_to_cbor(&arr, v->series);
break;
case METRICS_TYPE_SERIES_S32:
append_series_s32_to_cbor(&arr, v->series);
break;
default:
assert(0);
}
rc = cbor_encoder_close_container(&map, &arr);
if (rc != 0) {
return -1;
}
/*
* Let's remove existing series chain to free space in case om is from
* event_metric pool - assume we do this at the end of event so will
* start over anyway.
*/
if (om->om_omp == &event_metric_mbuf_pool) {
os_mbuf_free_chain(v->series);
v->series = NULL;
}
}
rc = cbor_encoder_close_container(&encoder, &map);
if (rc != 0) {
return -1;
}
return 0;
}
struct os_mbuf *
metrics_get_mbuf(void)
{
return os_mbuf_get(&event_metric_mbuf_pool, 0);
}
void
metrics_pkg_init(void)
{
int rc;
SYSINIT_ASSERT_ACTIVE();
rc = os_mempool_init(&event_metric_mempool, MEMPOOL_COUNT, MEMPOOL_SIZE,
event_metric_data, "event_metric");
assert(rc == 0);
rc = os_mbuf_pool_init(&event_metric_mbuf_pool, &event_metric_mempool,
MEMPOOL_SIZE, MEMPOOL_COUNT);
assert(rc == 0);
#if MYNEWT_VAL(METRICS_CLI)
metrics_cli_init();
#endif
}