--- /dev/null
+/*
+ * Copyright (C) 2020 Francis Deslauriers <francis.deslauriers@efficios.com>
+ *
+ * SPDX-License-Identifier: LGPL-2.1-only
+ *
+ */
+
+#include <assert.h>
+#include <string.h>
+
+#include <common/error.h>
+#include <common/macros.h>
+#include <common/optional.h>
+#include <common/payload.h>
+
+#include <lttng/lttng.h>
+#include <lttng/map/map-internal.h>
+
+enum lttng_map_status lttng_map_set_name(struct lttng_map *map,
+ const char *name)
+{
+ char *name_copy = NULL;
+ enum lttng_map_status status;
+
+ if (!map || !name || strlen(name) == 0) {
+ status = LTTNG_MAP_STATUS_INVALID;
+ goto end;
+ }
+
+ name_copy = strdup(name);
+ if (!name_copy) {
+ status = LTTNG_MAP_STATUS_ERROR;
+ goto end;
+ }
+
+ free(map->name);
+
+ map->name = name_copy;
+ name_copy = NULL;
+
+ status = LTTNG_MAP_STATUS_OK;
+end:
+ return status;
+}
+
+enum lttng_map_status lttng_map_get_name(const struct lttng_map *map,
+ const char **name)
+{
+ enum lttng_map_status status;
+
+ if (!map || !name) {
+ status = LTTNG_MAP_STATUS_INVALID;
+ goto end;
+ }
+
+ if (!map->name) {
+ status = LTTNG_MAP_STATUS_UNSET;
+ }
+
+ *name = map->name;
+ status = LTTNG_MAP_STATUS_OK;
+end:
+ return status;
+}
+
+enum lttng_map_status lttng_map_create(const char *name,
+ unsigned int dimension_count,
+ uint64_t *dimension_sizes,
+ enum lttng_domain_type domain,
+ enum lttng_buffer_type buffer_type,
+ enum lttng_map_bitness bitness,
+ enum lttng_map_boundary_policy boundary_policy,
+ bool coalesce_hits,
+ struct lttng_map **map_out)
+{
+ enum lttng_map_status status;
+ struct lttng_map *map;
+
+ if (dimension_count != 1) {
+ status = LTTNG_MAP_STATUS_INVALID;
+ goto end;
+ }
+
+ map = zmalloc(sizeof(struct lttng_map));
+ if (!map) {
+ status = LTTNG_MAP_STATUS_ERROR;
+ goto end;
+ }
+
+ if (name) {
+ status = lttng_map_set_name(map, name);
+ if (status != LTTNG_MAP_STATUS_OK) {
+ goto free_map;
+ }
+ } else {
+ map->name = NULL;
+ }
+
+ map->dimension_count = dimension_count;
+ map->dimension_sizes = zmalloc(
+ sizeof(*map->dimension_sizes) * dimension_count);
+ if (!map->dimension_sizes) {
+ status = LTTNG_MAP_STATUS_ERROR;
+ goto free_name;
+ }
+
+ memcpy(map->dimension_sizes, dimension_sizes,
+ sizeof(*map->dimension_sizes) * dimension_count);
+
+ map->domain = domain;
+ map->buffer_type = buffer_type;
+ map->bitness = bitness;
+ map->boundary_policy = boundary_policy;
+ map->coalesce_hits = coalesce_hits;
+
+ lttng_map_set_is_enabled(map, true);
+
+ urcu_ref_init(&map->ref);
+
+ *map_out = map;
+
+ status = LTTNG_MAP_STATUS_OK;
+
+ goto end;
+free_name:
+ free(map->name);
+free_map:
+ free(map);
+end:
+ return status;
+}
+
+unsigned int lttng_map_get_dimension_count(
+ const struct lttng_map *map)
+{
+ assert(map);
+
+ return map->dimension_count;
+}
+
+enum lttng_map_status lttng_map_get_dimension_length(
+ const struct lttng_map *map, unsigned int dimension,
+ uint64_t *dimension_length)
+{
+ enum lttng_map_status status;
+
+ assert(map);
+
+ if (dimension >= map->dimension_count) {
+ status = LTTNG_MAP_STATUS_INVALID;
+ goto end;
+ }
+
+ *dimension_length = map->dimension_sizes[dimension];
+
+ status = LTTNG_MAP_STATUS_OK;
+end:
+ return status;
+}
+
+enum lttng_map_bitness lttng_map_get_bitness(
+ const struct lttng_map *map)
+{
+ assert(map);
+
+ return map->bitness;
+}
+
+enum lttng_domain_type lttng_map_get_domain(
+ const struct lttng_map *map)
+{
+ assert(map);
+
+ return map->domain;
+}
+
+enum lttng_buffer_type lttng_map_get_buffer_type(
+ const struct lttng_map *map)
+{
+ assert(map);
+
+ return map->buffer_type;
+}
+
+enum lttng_map_boundary_policy lttng_map_get_boundary_policy(
+ const struct lttng_map *map)
+{
+ assert(map);
+
+ return map->boundary_policy;
+}
+
+bool lttng_map_get_coalesce_hits(const struct lttng_map *map)
+{
+ assert(map);
+
+ return map->coalesce_hits;
+}
+
+LTTNG_HIDDEN
+int lttng_map_serialize(const struct lttng_map *map,
+ struct lttng_payload *payload)
+{
+ int ret;
+ size_t header_offset, size_before_payload, size_name;
+ struct lttng_map_comm map_comm = {};
+ struct lttng_map_comm *header;
+
+ if (map->name != NULL) {
+ size_name = strlen(map->name) + 1;
+ } else {
+ size_name = 0;
+ }
+
+ map_comm.name_length = size_name;
+ map_comm.is_enabled = LTTNG_OPTIONAL_GET(map->is_enabled);
+ map_comm.bitness = map->bitness;
+ map_comm.domain = map->domain;
+ map_comm.buffer_type = map->buffer_type;
+ map_comm.boundary_policy = map->boundary_policy;
+ map_comm.dimension_count = map->dimension_count;
+ map_comm.coalesce_hits = map->coalesce_hits;
+
+ header_offset = payload->buffer.size;
+
+ ret = lttng_dynamic_buffer_append(&payload->buffer, &map_comm,
+ sizeof(map_comm));
+ if (ret) {
+ goto end;
+ }
+
+ size_before_payload = payload->buffer.size;
+
+ /* map name */
+ ret = lttng_dynamic_buffer_append(
+ &payload->buffer, map->name, size_name);
+ if (ret) {
+ goto end;
+ }
+
+ ret = lttng_dynamic_buffer_append(
+ &payload->buffer, map->dimension_sizes,
+ sizeof(*map->dimension_sizes) * map->dimension_count);
+ if (ret) {
+ goto end;
+ }
+
+ /* Update payload size. */
+ header = (typeof(header)) (payload->buffer.data + header_offset);
+ header->length = payload->buffer.size - size_before_payload;
+
+end:
+ return ret;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_map_create_from_payload(
+ struct lttng_payload_view *src_view,
+ struct lttng_map **map)
+{
+ ssize_t ret, offset = 0;
+ const struct lttng_map_comm *map_comm;
+ enum lttng_map_status status;
+ unsigned int dimension_count;
+ uint64_t *dimension_sizes = NULL;
+ bool coalesce_hits;
+ const char *name = NULL;
+ enum lttng_domain_type domain;
+ enum lttng_buffer_type buffer_type;
+ enum lttng_map_bitness bitness;
+ enum lttng_map_boundary_policy boundary_policy;
+
+ if (!src_view || !map) {
+ ret = -1;
+ goto end;
+ }
+
+ map_comm = (typeof(map_comm)) src_view->buffer.data;
+ offset += sizeof(*map_comm);
+
+ domain = map_comm->domain;
+ buffer_type = map_comm->buffer_type;
+ bitness = map_comm->bitness;
+ boundary_policy = map_comm->boundary_policy;
+ dimension_count = map_comm->dimension_count;
+ coalesce_hits = map_comm->coalesce_hits;
+
+ if (map_comm->name_length != 0) {
+ struct lttng_payload_view name_view =
+ lttng_payload_view_from_view(
+ src_view, offset,
+ map_comm->name_length);
+
+ name = name_view.buffer.data;
+ if (!lttng_buffer_view_contains_string(&name_view.buffer,
+ name, map_comm->name_length)){
+ ret = -1;
+ goto end;
+ }
+ offset += map_comm->name_length;
+ }
+
+ size_t map_dim_sizes_len = sizeof(*(*map)->dimension_sizes) * dimension_count;
+
+ struct lttng_payload_view dimension_sizes_view =
+ lttng_payload_view_from_view(src_view, offset,
+ map_dim_sizes_len);
+
+ dimension_sizes = zmalloc(map_dim_sizes_len);
+ if (!dimension_sizes) {
+ ret = -1;
+ goto end;
+ }
+
+ memcpy(dimension_sizes, dimension_sizes_view.buffer.data,
+ map_dim_sizes_len);
+
+ offset += map_dim_sizes_len;
+
+ status = lttng_map_create(name, dimension_count,
+ dimension_sizes, domain, buffer_type, bitness,
+ boundary_policy, coalesce_hits, map);
+ if (status != LTTNG_MAP_STATUS_OK) {
+ ret = -1;
+ goto end;
+ }
+
+ lttng_map_set_is_enabled(*map, map_comm->is_enabled);
+
+ ret = offset;
+
+end:
+ free(dimension_sizes);
+ return ret;
+}
+
+LTTNG_HIDDEN
+void lttng_map_set_is_enabled(struct lttng_map *map, bool enabled)
+{
+ assert(map);
+
+ LTTNG_OPTIONAL_SET(&map->is_enabled, enabled);
+}
+
+int lttng_map_get_is_enabled(const struct lttng_map *map)
+{
+ assert(map);
+ return (int) LTTNG_OPTIONAL_GET(map->is_enabled);
+}
+
+LTTNG_HIDDEN
+void lttng_map_get(struct lttng_map *map)
+{
+ urcu_ref_get(&map->ref);
+}
+
+static void map_destroy_ref(struct urcu_ref *ref)
+{
+ struct lttng_map *map = container_of(ref, struct lttng_map, ref);
+
+ free(map->dimension_sizes);
+ free(map->name);
+ free(map);
+
+}
+
+LTTNG_HIDDEN
+void lttng_map_put(struct lttng_map *map)
+{
+ if (!map) {
+ return;
+ }
+
+ urcu_ref_put(&map->ref , map_destroy_ref);
+}
+
+
+void lttng_map_destroy(struct lttng_map *map)
+{
+ lttng_map_put(map);
+}
+
+static void delete_map_array_element(void *ptr)
+{
+ struct lttng_map *map = ptr;
+
+ lttng_map_put(map);
+}
+
+LTTNG_HIDDEN
+struct lttng_map_list *lttng_map_list_create(void)
+{
+ struct lttng_map_list *map_list = NULL;
+
+ map_list = zmalloc(sizeof(*map_list));
+ if (!map_list) {
+ goto end;
+ }
+
+ lttng_dynamic_pointer_array_init(&map_list->array,
+ delete_map_array_element);
+
+end:
+ return map_list;
+}
+
+LTTNG_HIDDEN
+enum lttng_map_status lttng_map_list_add(struct lttng_map_list *map_list,
+ struct lttng_map *map)
+{
+ enum lttng_map_status status;
+ int ret;
+
+ assert(map_list);
+ assert(map);
+
+ lttng_map_get(map);
+
+ ret = lttng_dynamic_pointer_array_add_pointer(&map_list->array, map);
+ if (ret) {
+ lttng_map_put(map);
+ status = LTTNG_MAP_STATUS_ERROR;
+ goto end;
+ }
+ status = LTTNG_MAP_STATUS_OK;
+end:
+ return status;
+
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_map_list_create_from_payload(struct lttng_payload_view *src_view,
+ struct lttng_map_list **map_list)
+{
+ unsigned int i;
+ ssize_t ret, offset = 0;
+ const struct lttng_map_list_comm *map_list_comm;
+ struct lttng_map_list *local_map_list = NULL;
+
+ map_list_comm = (typeof(map_list_comm)) src_view->buffer.data;
+ offset += sizeof(*map_list_comm);
+
+ local_map_list = lttng_map_list_create();
+ if (!local_map_list) {
+ ret = -1;
+ goto end;
+ }
+
+ for (i = 0; i < map_list_comm->count; i++) {
+ struct lttng_map *map = NULL;
+ struct lttng_payload_view map_view =
+ lttng_payload_view_from_view(src_view, offset, -1);
+ ssize_t map_size;
+
+ map_size = lttng_map_create_from_payload(&map_view, &map);
+ if (map_size < 0) {
+ ret = map_size;
+ goto end;
+ }
+
+ /* Transfer ownership of the map to the collection. */
+ ret = lttng_map_list_add(local_map_list, map);
+ lttng_map_put(map);
+ if (ret < 0) {
+ ret = -1;
+ goto end;
+ }
+
+ offset += map_size;
+ }
+
+ /* Pass ownership to caller. */
+ *map_list = local_map_list;
+ local_map_list = NULL;
+
+ ret = offset;
+end:
+ lttng_map_list_destroy(local_map_list);
+ return ret;
+}
+
+LTTNG_HIDDEN
+int lttng_map_list_serialize(const struct lttng_map_list *map_list,
+ struct lttng_payload *payload)
+{
+ int ret;
+ unsigned int i, count;
+ enum lttng_map_status status;
+ struct lttng_map_list_comm map_list_comm = {};
+
+ status = lttng_map_list_get_count(map_list, &count);
+ if (status != LTTNG_MAP_STATUS_OK) {
+ ret = LTTNG_ERR_INVALID;
+ goto end;
+ }
+
+ map_list_comm.count = count;
+ ret = lttng_dynamic_buffer_append(&payload->buffer, &map_list_comm,
+ sizeof(map_list_comm));
+ if (ret) {
+ goto end;
+ }
+ for (i = 0; i < count; i++) {
+ const struct lttng_map *map =
+ lttng_map_list_get_at_index(map_list, i);
+
+ assert(map);
+
+ ret = lttng_map_serialize(map, payload);
+ if (ret) {
+ goto end;
+ }
+ }
+
+end:
+ return ret;
+}
+
+const struct lttng_map *lttng_map_list_get_at_index(
+ const struct lttng_map_list *map_list, unsigned int index)
+{
+ struct lttng_map *map = NULL;
+
+ assert(map_list);
+ if (index >= lttng_dynamic_pointer_array_get_count(&map_list->array)) {
+ goto end;
+ }
+
+ map = (struct lttng_map *)
+ lttng_dynamic_pointer_array_get_pointer(
+ &map_list->array, index);
+end:
+ return map;
+}
+
+enum lttng_map_status lttng_map_list_get_count(
+ const struct lttng_map_list *map_list, unsigned int *count)
+{
+ enum lttng_map_status status = LTTNG_MAP_STATUS_OK;
+
+ if (!map_list || !count) {
+ status = LTTNG_MAP_STATUS_INVALID;
+ goto end;
+ }
+
+ *count = lttng_dynamic_pointer_array_get_count(&map_list->array);
+ status = LTTNG_MAP_STATUS_OK;
+end:
+ return status;
+}
+
+void lttng_map_list_destroy(struct lttng_map_list *map_list)
+{
+ if (!map_list) {
+ return;
+ }
+
+ lttng_dynamic_pointer_array_reset(&map_list->array);
+ free(map_list);
+}
+
+struct lttng_map_key_value_pair *lttng_map_key_value_pair_create(const char *key,
+ int64_t value)
+{
+ struct lttng_map_key_value_pair *key_value;
+
+ key_value = zmalloc(sizeof(struct lttng_map_key_value_pair));
+ if (!key_value) {
+ goto end;
+ }
+
+ key_value->key = strdup(key);
+ if (!key_value->key) {
+ free(key_value);
+ key_value = NULL;
+ goto end;
+ }
+ key_value->value = value;
+
+end:
+ return key_value;
+}
+
+enum lttng_map_status lttng_map_key_value_pair_get_key(
+ const struct lttng_map_key_value_pair *key_value,
+ const char **key)
+{
+ assert(key_value);
+ assert(key_value->key);
+
+ *key = key_value->key;
+ return LTTNG_MAP_STATUS_OK;
+}
+
+enum lttng_map_status lttng_map_key_value_pair_get_value(
+ const struct lttng_map_key_value_pair *key_value,
+ int64_t *value)
+{
+ assert(key_value);
+ *value = key_value->value;
+ return LTTNG_MAP_STATUS_OK;
+}
+
+LTTNG_HIDDEN
+void lttng_map_key_value_pair_set_has_overflowed(
+ struct lttng_map_key_value_pair *key_value)
+{
+ assert(key_value);
+
+ key_value->has_overflowed = true;
+}
+
+LTTNG_HIDDEN
+void lttng_map_key_value_pair_set_has_underflowed(
+ struct lttng_map_key_value_pair *key_value)
+{
+ assert(key_value);
+
+ key_value->has_underflowed = true;
+}
+
+bool lttng_map_key_value_pair_get_has_overflowed(
+ const struct lttng_map_key_value_pair *key_value)
+{
+ assert(key_value);
+
+ return key_value->has_overflowed;
+}
+
+bool lttng_map_key_value_pair_get_has_underflowed(
+ const struct lttng_map_key_value_pair *key_value)
+{
+ assert(key_value);
+
+ return key_value->has_underflowed;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_map_key_value_pair_create_from_payload(
+ struct lttng_payload_view *src_view,
+ struct lttng_map_key_value_pair **key_value)
+{
+ const struct lttng_map_key_value_pair_comm *kv_pair_comm;
+ struct lttng_map_key_value_pair *kv_pair;
+ ssize_t ret, offset = 0;
+ const char *key;
+ int64_t value;
+
+ if (!src_view || !key_value) {
+ ret = -1;
+ goto end;
+ }
+
+ kv_pair_comm = (typeof(kv_pair_comm)) src_view->buffer.data;
+ offset += sizeof(*kv_pair_comm);
+
+ if (kv_pair_comm->key_length == 0) {
+ ret = -1;
+ goto end;
+ }
+
+ value = kv_pair_comm->value;
+
+ struct lttng_payload_view key_view =
+ lttng_payload_view_from_view(src_view, offset,
+ kv_pair_comm->key_length);
+ key = key_view.buffer.data;
+ if (!lttng_buffer_view_contains_string(&key_view.buffer,
+ key, kv_pair_comm->key_length)) {
+ ret = -1;
+ goto end;
+ }
+
+ offset += kv_pair_comm->key_length;
+
+ kv_pair = lttng_map_key_value_pair_create(key, value);
+ if (!kv_pair) {
+ ret = -1;
+ goto end;
+ }
+
+ kv_pair->has_overflowed = kv_pair_comm->has_overflowed;
+ kv_pair->has_underflowed = kv_pair_comm->has_underflowed;
+
+ *key_value = kv_pair;
+
+ ret = offset;
+
+end:
+ return ret;
+}
+
+LTTNG_HIDDEN
+int lttng_map_key_value_pair_serialize(
+ const struct lttng_map_key_value_pair *key_value,
+ struct lttng_payload *payload)
+{
+ int ret;
+ size_t key_len;
+ struct lttng_map_key_value_pair_comm kv_pair_comm = {0};
+
+ assert(key_value);
+ assert(key_value->key);
+
+ key_len = strlen(key_value->key) + 1;
+
+ kv_pair_comm.key_length = key_len;
+ kv_pair_comm.value = key_value->value;
+ kv_pair_comm.has_overflowed = key_value->has_overflowed;
+ kv_pair_comm.has_underflowed = key_value->has_underflowed;
+
+ ret = lttng_dynamic_buffer_append(&payload->buffer, &kv_pair_comm,
+ sizeof(kv_pair_comm));
+ if (ret) {
+ goto end;
+ }
+
+ /* Append key.*/
+ ret = lttng_dynamic_buffer_append(
+ &payload->buffer, key_value->key, key_len);
+ if (ret) {
+ goto end;
+ }
+
+end:
+ return ret;
+}
+
+void lttng_map_key_value_pair_destroy(struct lttng_map_key_value_pair *key_value)
+{
+ if (!key_value) {
+ return;
+ }
+
+ free(key_value->key);
+ free(key_value);
+}
+
+static void delete_map_key_value_pair_array_element(void *ptr)
+{
+ struct lttng_map_key_value_pair *key_value = ptr;
+ lttng_map_key_value_pair_destroy(key_value);
+}
+
+LTTNG_HIDDEN
+struct lttng_map_key_value_pair_list *lttng_map_key_value_pair_list_create(
+ enum lttng_map_key_value_pair_list_type type,
+ bool summed_all_cpus)
+{
+ struct lttng_map_key_value_pair_list *map_key_values = NULL;
+
+ map_key_values = zmalloc(sizeof(*map_key_values));
+ if (!map_key_values) {
+ goto end;
+ }
+
+ map_key_values->type = type;
+ map_key_values->summed_all_cpus = summed_all_cpus;
+
+ lttng_dynamic_pointer_array_init(&map_key_values->array,
+ delete_map_key_value_pair_array_element);
+
+end:
+ return map_key_values;
+}
+
+LTTNG_HIDDEN
+enum lttng_map_status lttng_map_key_value_pair_list_set_identifier(
+ struct lttng_map_key_value_pair_list *kv_pair_list,
+ uint64_t identifier)
+{
+ enum lttng_map_status status;
+ assert(kv_pair_list);
+
+ switch (kv_pair_list->type) {
+ case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID:
+ case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_UID:
+ kv_pair_list->id = identifier;
+ status = LTTNG_MAP_STATUS_OK;
+ break;
+ case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID_AGGREGATED:
+ ERR("Cannot set an identifier for an UST per-pid aggregation key value pair list");
+ status = LTTNG_MAP_STATUS_INVALID;
+ break;
+ case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_KERNEL:
+ ERR("Cannot set an identifier for a kernel key value pair list");
+ status = LTTNG_MAP_STATUS_INVALID;
+ break;
+ default:
+ ERR("Unknown key value par list type %d", kv_pair_list->type);
+ abort();
+ }
+
+ return status;
+}
+
+bool lttng_map_key_value_pair_list_get_summed_all_cpu(
+ const struct lttng_map_key_value_pair_list *kv_pair_list)
+{
+ assert(kv_pair_list);
+
+ return kv_pair_list->summed_all_cpus;
+}
+
+LTTNG_HIDDEN
+enum lttng_map_status lttng_map_key_value_pair_list_set_cpu(
+ struct lttng_map_key_value_pair_list *kv_pair_list,
+ uint64_t cpu)
+{
+ assert(kv_pair_list);
+
+ kv_pair_list->cpu = cpu;
+
+ return LTTNG_MAP_STATUS_OK;
+}
+
+uint64_t lttng_map_key_value_pair_list_get_cpu(
+ const struct lttng_map_key_value_pair_list *kv_pair_list)
+{
+ assert(kv_pair_list);
+
+ return kv_pair_list->cpu;
+}
+
+enum lttng_map_key_value_pair_list_type lttng_map_key_value_pair_list_get_type(
+ const struct lttng_map_key_value_pair_list *kv_pair_list)
+{
+ assert(kv_pair_list);
+
+ return kv_pair_list->type;
+}
+
+LTTNG_HIDDEN
+enum lttng_map_status lttng_map_key_value_pair_list_append_key_value(
+ struct lttng_map_key_value_pair_list *kv_pair_list,
+ struct lttng_map_key_value_pair *key_value)
+{
+ int ret;
+ enum lttng_map_status status;
+
+ assert(kv_pair_list);
+ assert(key_value);
+
+ ret = lttng_dynamic_pointer_array_add_pointer(&kv_pair_list->array,
+ key_value);
+ if (ret) {
+ status = LTTNG_MAP_STATUS_ERROR;
+ goto end;
+ }
+
+ status = LTTNG_MAP_STATUS_OK;
+
+end:
+ return status;
+}
+
+uint64_t lttng_map_key_value_pair_list_get_identifer(
+ const struct lttng_map_key_value_pair_list *kv_pair_list)
+{
+ assert(kv_pair_list);
+
+ switch (kv_pair_list->type) {
+ case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID:
+ case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_UID:
+ break;
+ case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID_AGGREGATED:
+ ERR("No identifier for UST per-pid aggregation key value pair lists");
+ abort();
+ break;
+ case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_KERNEL:
+ ERR("No identifier for kernel key value pair lists");
+ abort();
+ break;
+ default:
+ ERR("Unknown key value par list type %d", kv_pair_list->type);
+ abort();
+ }
+
+ return kv_pair_list->id;
+}
+
+const struct lttng_map_key_value_pair *lttng_map_key_value_pair_list_get_at_index(
+ const struct lttng_map_key_value_pair_list *kv_pair_list,
+ unsigned int index)
+{
+ struct lttng_map_key_value_pair *key_value = NULL;
+
+ assert(kv_pair_list);
+ if (index >= lttng_dynamic_pointer_array_get_count(&kv_pair_list->array)) {
+ goto end;
+ }
+
+ key_value = (struct lttng_map_key_value_pair *)
+ lttng_dynamic_pointer_array_get_pointer(
+ &kv_pair_list->array, index);
+end:
+ return key_value;
+}
+
+enum lttng_map_status lttng_map_key_value_pair_list_get_count(
+ const struct lttng_map_key_value_pair_list *kv_pair_list,
+ unsigned int *count)
+{
+ enum lttng_map_status status;
+
+ if (!kv_pair_list || !count) {
+ status = LTTNG_MAP_STATUS_INVALID;;
+ goto end;
+ }
+
+ *count = lttng_dynamic_pointer_array_get_count(&kv_pair_list->array);
+
+ status = LTTNG_MAP_STATUS_OK;
+end:
+ return status;
+}
+
+void lttng_map_key_value_pair_list_destroy(struct lttng_map_key_value_pair_list *kv_pair_list)
+{
+ if (!kv_pair_list) {
+ return;
+ }
+
+ lttng_dynamic_pointer_array_reset(&kv_pair_list->array);
+ free(kv_pair_list);
+}
+
+int lttng_map_key_value_pair_list_serialize(
+ const struct lttng_map_key_value_pair_list *kv_pair_list,
+ struct lttng_payload *payload)
+{
+ int ret;
+ unsigned int i, count;
+ enum lttng_map_status status;
+ struct lttng_map_key_value_pair_list_comm kv_pair_list_comm = {};
+
+ kv_pair_list_comm.id = kv_pair_list->id;
+ kv_pair_list_comm.cpu = kv_pair_list->cpu;
+ kv_pair_list_comm.type = (uint8_t) kv_pair_list->type;
+ kv_pair_list_comm.summed_all_cpus = (uint8_t) kv_pair_list->summed_all_cpus;
+
+ status = lttng_map_key_value_pair_list_get_count(kv_pair_list, &count);
+ if (status != LTTNG_MAP_STATUS_OK) {
+ ret = LTTNG_ERR_INVALID;
+ goto end;
+ }
+
+ kv_pair_list_comm.count = count;
+ ret = lttng_dynamic_buffer_append(&payload->buffer, &kv_pair_list_comm,
+ sizeof(kv_pair_list_comm));
+ if (ret) {
+ goto end;
+ }
+ for (i = 0; i < count; i++) {
+ const struct lttng_map_key_value_pair *kv_pair =
+ lttng_map_key_value_pair_list_get_at_index(kv_pair_list, i);
+
+ assert(kv_pair);
+
+ ret = lttng_map_key_value_pair_serialize(kv_pair, payload);
+ if (ret) {
+ goto end;
+ }
+ }
+
+end:
+ return ret;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_map_key_value_pair_list_create_from_payload(
+ struct lttng_payload_view *src_view,
+ struct lttng_map_key_value_pair_list **kv_pair_list)
+{
+ ssize_t ret, offset = 0;
+ unsigned int i;
+ enum lttng_map_status status;
+ const struct lttng_map_key_value_pair_list_comm *kv_pair_list_comm;
+ struct lttng_map_key_value_pair_list *local_key_values = NULL;
+
+ kv_pair_list_comm = (typeof(kv_pair_list_comm)) src_view->buffer.data;
+ offset += sizeof(*kv_pair_list_comm);
+
+ local_key_values = lttng_map_key_value_pair_list_create(
+ kv_pair_list_comm->type, kv_pair_list_comm->summed_all_cpus);
+ if (!local_key_values) {
+ ret = -1;
+ goto end;
+ }
+
+ local_key_values->cpu = kv_pair_list_comm->cpu;
+
+ switch (lttng_map_key_value_pair_list_get_type(local_key_values)) {
+ case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_PID:
+ case LTTNG_MAP_KEY_VALUE_PAIR_LIST_TYPE_UST_PER_UID:
+ status = lttng_map_key_value_pair_list_set_identifier(local_key_values,
+ kv_pair_list_comm->id);
+ if (status != LTTNG_MAP_STATUS_OK) {
+ ret = -1;
+ goto end;
+ }
+ break;
+ default:
+ break;
+ }
+
+ for (i = 0; i < kv_pair_list_comm->count; i++) {
+ struct lttng_map_key_value_pair *kv_pair = NULL;
+ struct lttng_payload_view kv_view =
+ lttng_payload_view_from_view(src_view, offset, -1);
+ ssize_t kv_size;
+
+ kv_size = lttng_map_key_value_pair_create_from_payload(
+ &kv_view, &kv_pair);
+ if (kv_size < 0) {
+ ret = kv_size;
+ goto end;
+ }
+
+ /* Transfer ownership of the key-value to the collection. */
+ status = lttng_map_key_value_pair_list_append_key_value(local_key_values,
+ kv_pair);
+ if (status != LTTNG_MAP_STATUS_OK) {
+ ret = -1;
+ goto end;
+ }
+
+ offset += kv_size;
+ }
+
+ /* Pass ownership to caller. */
+ *kv_pair_list = local_key_values;
+ local_key_values = NULL;
+
+ ret = offset;
+end:
+ lttng_map_key_value_pair_list_destroy(local_key_values);
+ return ret;
+}
+
+static void delete_map_key_value_pair_list_array_element(void *ptr)
+{
+ struct lttng_map_key_value_pair_list *kv_list = ptr;
+ lttng_map_key_value_pair_list_destroy(kv_list);
+}
+
+LTTNG_HIDDEN
+struct lttng_map_content *lttng_map_content_create(
+ enum lttng_buffer_type type)
+{
+ struct lttng_map_content *map_content = NULL;
+
+ map_content = zmalloc(sizeof(*map_content));
+ if (!map_content) {
+ goto end;
+ }
+
+ map_content->type = type;
+
+ lttng_dynamic_pointer_array_init(&map_content->array,
+ delete_map_key_value_pair_list_array_element);
+
+end:
+ return map_content;
+}
+
+enum lttng_map_status lttng_map_content_get_count(
+ const struct lttng_map_content *map_content,
+ unsigned int *count)
+{
+ enum lttng_map_status status = LTTNG_MAP_STATUS_OK;
+
+ if (!map_content || !count) {
+ status = LTTNG_MAP_STATUS_INVALID;
+ goto end;
+ }
+
+ *count = lttng_dynamic_pointer_array_get_count(&map_content->array);
+ status = LTTNG_MAP_STATUS_OK;
+end:
+ return status;
+}
+
+enum lttng_buffer_type lttng_map_content_get_buffer_type(
+ const struct lttng_map_content *map_content)
+{
+ assert(map_content);
+
+ return map_content->type;
+}
+
+LTTNG_HIDDEN
+enum lttng_map_status lttng_map_content_append_key_value_list(
+ struct lttng_map_content *map_content,
+ struct lttng_map_key_value_pair_list *kv_list)
+{
+ int ret;
+ enum lttng_map_status status;
+
+ assert(map_content);
+ assert(kv_list);
+
+ ret = lttng_dynamic_pointer_array_add_pointer(&map_content->array,
+ kv_list);
+ if (ret) {
+ status = LTTNG_MAP_STATUS_ERROR;
+ goto end;
+ }
+
+ status = LTTNG_MAP_STATUS_OK;
+
+end:
+ return status;
+}
+
+const struct lttng_map_key_value_pair_list *lttng_map_content_get_at_index(
+ const struct lttng_map_content *map_content,
+ unsigned int index)
+{
+ struct lttng_map_key_value_pair_list *kv_pair_list = NULL;
+
+ assert(map_content);
+ if (index >= lttng_dynamic_pointer_array_get_count(&map_content->array)) {
+ goto end;
+ }
+
+ kv_pair_list = (struct lttng_map_key_value_pair_list *)
+ lttng_dynamic_pointer_array_get_pointer(
+ &map_content->array, index);
+end:
+ return kv_pair_list;
+}
+
+LTTNG_HIDDEN
+ssize_t lttng_map_content_create_from_payload(
+ struct lttng_payload_view *src_view,
+ struct lttng_map_content **map_content)
+{
+ ssize_t ret, offset = 0;
+ unsigned int i;
+ struct lttng_map_content_comm *map_content_comm;
+ struct lttng_map_content *local_map_content;
+
+ map_content_comm = (typeof(map_content_comm)) src_view->buffer.data;
+ offset += sizeof(*map_content_comm);
+
+ local_map_content = lttng_map_content_create(map_content_comm->type);
+ if (!local_map_content) {
+ ret = -1;
+ goto end;
+ }
+
+ for (i = 0; i < map_content_comm->count; i++) {
+ struct lttng_map_key_value_pair_list *kv_pair_list = NULL;
+ struct lttng_payload_view kv_list_view =
+ lttng_payload_view_from_view(src_view, offset, -1);
+ ssize_t kv_list_size;
+
+ kv_list_size = lttng_map_key_value_pair_list_create_from_payload(
+ &kv_list_view, &kv_pair_list);
+ if (kv_list_size < 0) {
+ ret = kv_list_size;
+ goto end;
+ }
+
+ /* Transfer ownership of the key-value to the collection. */
+ ret = lttng_map_content_append_key_value_list(local_map_content,
+ kv_pair_list);
+ if (ret < 0) {
+ ret = -1;
+ goto end;
+ }
+
+ offset += kv_list_size;
+ }
+
+ /* Pass ownership to caller. */
+ *map_content = local_map_content;
+ local_map_content = NULL;
+
+ ret = offset;
+end:
+ lttng_map_content_destroy(local_map_content);
+ return ret;
+}
+
+LTTNG_HIDDEN
+int lttng_map_content_serialize(
+ const struct lttng_map_content *map_content,
+ struct lttng_payload *payload)
+{
+ int ret;
+ unsigned int i, count;
+ enum lttng_map_status status;
+ struct lttng_map_content_comm map_content_comm = {};
+
+ status = lttng_map_content_get_count(map_content, &count);
+ if (status != LTTNG_MAP_STATUS_OK) {
+ ret = LTTNG_ERR_INVALID;
+ goto end;
+ }
+
+ map_content_comm.count = count;
+ map_content_comm.type = lttng_map_content_get_buffer_type(map_content);
+
+ ret = lttng_dynamic_buffer_append(&payload->buffer, &map_content_comm,
+ sizeof(map_content_comm));
+ if (ret) {
+ goto end;
+ }
+ for (i = 0; i < count; i++) {
+ const struct lttng_map_key_value_pair_list *kv_pair_list =
+ lttng_map_content_get_at_index(map_content, i);
+
+ assert(kv_pair_list);
+
+ ret = lttng_map_key_value_pair_list_serialize(kv_pair_list, payload);
+ if (ret) {
+ goto end;
+ }
+ }
+
+end:
+ return ret;
+}
+
+void lttng_map_content_destroy(struct lttng_map_content *map_content)
+{
+ if (!map_content) {
+ return;
+ }
+
+ lttng_dynamic_pointer_array_reset(&map_content->array);
+ free(map_content);
+}