| /** |
| * 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 "core/span.h" |
| #include "test/span_util.h" |
| #include "util/cmp.h" |
| #include "util/log.h" |
| |
| #include <errno.h> |
| #include <json_object.h> |
| #include <json_tokener.h> |
| #include <stdint.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| |
| static void span_json_parse_parents(struct json_object *root, |
| struct htrace_span *span, char *err, size_t err_len) |
| { |
| char err2[128]; |
| size_t err2_len = sizeof(err2); |
| struct json_object *p = NULL, *e = NULL; |
| int i, np; |
| |
| err2[0] = '\0'; |
| if (!json_object_object_get_ex(root, "p", &p)) { |
| return; // no parents |
| } |
| if (json_object_get_type(p) != json_type_array) { |
| snprintf(err, err_len, "p element was not an array.."); |
| return; |
| } |
| np = json_object_array_length(p); |
| if (np == 1) { |
| span->num_parents = 1; |
| e = json_object_array_get_idx(p, 0); |
| htrace_span_id_parse(&span->parent.single, |
| json_object_get_string(e), err2, err2_len); |
| if (err2[0]) { |
| snprintf(err, err_len, "failed to parse parent ID 1/1: %s.", err2); |
| return; |
| } |
| } else if (np > 1) { |
| span->parent.list = malloc(sizeof(struct htrace_span_id) * np); |
| if (!span->parent.list) { |
| snprintf(err, err_len, "failed to allocate parent ID array of " |
| "%d elements", np); |
| return; |
| } |
| span->num_parents = np; |
| for (i = 0; i < np; i++) { |
| e = json_object_array_get_idx(p, i); |
| htrace_span_id_parse(span->parent.list + i, |
| json_object_get_string(e), err2, err2_len); |
| if (err2[0]) { |
| snprintf(err, err_len, "failed to parse parent ID %d/%d: %s", |
| i + 1, np, err2); |
| return; |
| } |
| } |
| } |
| } |
| |
| static void span_json_parse_impl(struct json_object *root, |
| struct htrace_span *span, char *err, size_t err_len) |
| { |
| char err2[128]; |
| struct json_object *d = NULL, *b = NULL, *e = NULL, *s = NULL, *r = NULL; |
| int res; |
| |
| err[0] = '\0'; |
| err2[0] = '\0'; |
| if (!json_object_object_get_ex(root, "d", &d)) { |
| d = NULL; |
| } |
| span->desc = strdup(d ? json_object_get_string(d) : ""); |
| if (!span->desc) { |
| snprintf(err, err_len, "out of memory allocating description"); |
| return; |
| } |
| if (json_object_object_get_ex(root, "b", &b)) { |
| errno = 0; |
| span->begin_ms = json_object_get_int64(b); |
| res = errno; |
| if (res) { |
| snprintf(err, err_len, "error parsing begin_ms: %s", terror(res)); |
| return; |
| } |
| } |
| if (json_object_object_get_ex(root, "e", &e)) { |
| errno = 0; |
| span->end_ms = json_object_get_int64(e); |
| res = errno; |
| if (res) { |
| snprintf(err, err_len, "error parsing end_ms: %s", terror(res)); |
| return; |
| } |
| } |
| if (json_object_object_get_ex(root, "a", &s)) { |
| htrace_span_id_parse(&span->span_id, json_object_get_string(s), |
| err2, sizeof(err2)); |
| if (err2[0]) { |
| snprintf(err, err_len, "error parsing span_id: %s", err2); |
| return; |
| } |
| } |
| if (json_object_object_get_ex(root, "r", &r)) { |
| span->trid = strdup(json_object_get_string(r)); |
| } else { |
| span->trid = strdup(""); |
| } |
| if (!span->trid) { |
| snprintf(err, err_len, "out of memory allocating process id"); |
| return; |
| } |
| span_json_parse_parents(root, span, err, err_len); |
| if (err[0]) { |
| return; |
| } |
| } |
| |
| void span_json_parse(const char *in, struct htrace_span **rspan, |
| char *err, size_t err_len) |
| { |
| struct json_object *root = NULL; |
| enum json_tokener_error jerr; |
| struct htrace_span *span = NULL; |
| |
| err[0] = '\0'; |
| root = json_tokener_parse_verbose(in, &jerr); |
| if (!root) { |
| snprintf(err, err_len, "json_tokener_parse_verbose failed: %s", |
| json_tokener_error_desc(jerr)); |
| goto done; |
| } |
| span = calloc(1, sizeof(*span)); |
| if (!span) { |
| snprintf(err, err_len, "failed to malloc span."); |
| goto done; |
| } |
| span_json_parse_impl(root, span, err, err_len); |
| |
| done: |
| if (root) { |
| json_object_put(root); |
| } |
| if (err[0]) { |
| htrace_span_free(span); |
| *rspan = NULL; |
| } else { |
| *rspan = span; |
| } |
| } |
| |
| /** |
| * Compare two 64-bit numbers. |
| * |
| * We don't use subtraction here in order to avoid numeric overflow. |
| */ |
| static int uint64_cmp(uint64_t a, uint64_t b) |
| { |
| if (a < b) { |
| return -1; |
| } else if (a > b) { |
| return 1; |
| } else { |
| return 0; |
| } |
| } |
| |
| static int strcmp_handle_null(const char *a, const char *b) |
| { |
| if (a == NULL) { |
| a = ""; |
| } |
| if (b == NULL) { |
| b = ""; |
| } |
| return strcmp(a, b); |
| } |
| |
| static int compare_parents(struct htrace_span *a, struct htrace_span *b) |
| { |
| int na, nb, i, cmp; |
| |
| htrace_span_sort_and_dedupe_parents(a); |
| na = a->num_parents; |
| htrace_span_sort_and_dedupe_parents(b); |
| nb = b->num_parents; |
| |
| for (i = 0; ; i++) { |
| struct htrace_span_id sa, sb; |
| |
| if (i >= na) { |
| if (i >= nb) { |
| return 0; |
| } else { |
| return -1; |
| } |
| } else if (i >= nb) { |
| return 1; |
| } |
| if ((i == 0) && (na == 1)) { |
| htrace_span_id_copy(&sa, &a->parent.single); |
| } else { |
| htrace_span_id_copy(&sa, a->parent.list + i); |
| } |
| if ((i == 0) && (nb == 1)) { |
| htrace_span_id_copy(&sb, &b->parent.single); |
| } else { |
| htrace_span_id_copy(&sb, b->parent.list + i); |
| } |
| cmp = htrace_span_id_compare(&sa, &sb); |
| if (cmp) { |
| return cmp; |
| } |
| } |
| } |
| |
| int span_compare(struct htrace_span *a, struct htrace_span *b) |
| { |
| int c; |
| |
| c = htrace_span_id_compare(&a->span_id, &b->span_id); |
| if (c) { |
| return c; |
| } |
| c = strcmp(a->desc, b->desc); |
| if (c) { |
| return c; |
| } |
| c = uint64_cmp(a->begin_ms, b->begin_ms); |
| if (c) { |
| return c; |
| } |
| c = uint64_cmp(a->end_ms, b->end_ms); |
| if (c) { |
| return c; |
| } |
| c = strcmp_handle_null(a->trid, b->trid); |
| if (c) { |
| return c; |
| } |
| return compare_parents(a, b); |
| } |
| |
| static int span_read_key_str(struct cmp_ctx_s *ctx, char *out, |
| uint32_t out_len, char *err, size_t err_len) |
| { |
| uint32_t size = 0; |
| |
| err[0] = '\0'; |
| if (!cmp_read_str_size(ctx, &size)) { |
| snprintf(err, err_len, "span_read_key_str: cmp_read_str_size failed."); |
| return 0; |
| } |
| if (size >= out_len) { |
| snprintf(err, err_len, "span_read_key_str: size of key string was " |
| "%"PRId32", but we can only handle key strings less than " |
| "%"PRId32" bytes.", size, out_len); |
| return 0; |
| } |
| if (!ctx->read(ctx, out, size)) { |
| snprintf(err, err_len, "span_read_key_str: ctx->read failed for " |
| "%"PRId32"-byte key string.", size); |
| return 0; |
| } |
| out[size] = '\0'; |
| return 1; |
| } |
| |
| static char *cmp_read_malloced_string(struct cmp_ctx_s *ctx, const char *what, |
| char *err, size_t err_len) |
| { |
| uint32_t size = 0; |
| char *str; |
| |
| err[0] = '\0'; |
| if (!cmp_read_str_size(ctx, &size)) { |
| snprintf(err, err_len, "cmp_read_malloced_string: cmp_read_str_size " |
| "failed for %s.", what); |
| return NULL; |
| } |
| str = malloc(size + 1); |
| if (!str) { |
| snprintf(err, err_len, "cmp_read_malloced_string: failed to malloc " |
| "failed for %d-byte string for %s.", size + 1, what); |
| return NULL; |
| } |
| if (!ctx->read(ctx, str, size)) { |
| snprintf(err, err_len, "cmp_read_malloced_string: failed to read " |
| "%"PRId32"-byte string for %s.", size, what); |
| free(str); |
| return NULL; |
| } |
| str[size] = '\0'; |
| return str; |
| } |
| |
| static void span_parse_msgpack_parents(struct cmp_ctx_s *ctx, |
| struct htrace_span *span, char *err, size_t err_len) |
| { |
| uint32_t i, size; |
| |
| err[0] = '\0'; |
| if (span->num_parents > 1) { |
| free(span->parent.list); |
| span->parent.list = NULL; |
| } |
| htrace_span_id_clear(&span->parent.single); |
| span->num_parents = 0; |
| if (!cmp_read_array(ctx, &size)) { |
| snprintf(err, err_len, "span_parse_msgpack_parents: cmp_read_array " |
| "failed."); |
| return; |
| } |
| if (size == 1) { |
| if (!htrace_span_id_read_msgpack(&span->parent.single, ctx)) { |
| snprintf(err, err_len, "span_parse_msgpack_parents: cmp_read_u64 " |
| "for single child ID failed"); |
| return; |
| } |
| } else if (size > 1) { |
| span->parent.list = malloc(sizeof(struct htrace_span_id) * size); |
| if (!span->parent.list) { |
| snprintf(err, err_len, "span_parse_msgpack_parents: failed to " |
| "malloc %"PRId32"-entry parent array.", size); |
| return; |
| } |
| for (i = 0; i < size; i++) { |
| if (!htrace_span_id_read_msgpack(span->parent.list + i, ctx)) { |
| snprintf(err, err_len, "span_parse_msgpack_parents: " |
| "htrace_span_id_read_msgpack for child %d ID failed", i); |
| free(span->parent.list); |
| span->parent.list = NULL; |
| return; |
| } |
| } |
| } |
| span->num_parents = size; |
| } |
| |
| struct htrace_span *span_read_msgpack(struct cmp_ctx_s *ctx, |
| char *err, size_t err_len) |
| { |
| struct htrace_span *span = NULL; |
| uint32_t map_size = 0; |
| char key[8]; |
| |
| err[0] = '\0'; |
| span = calloc(1, sizeof(*span)); |
| if (!span) { |
| snprintf(err, err_len, "span_read_msgpack: OOM allocating " |
| "htrace_span."); |
| goto error; |
| } |
| if (!cmp_read_map(ctx, &map_size)) { |
| snprintf(err, err_len, "span_read_msgpack: cmp_read_map failed to " |
| "read enclosing map object.\n"); |
| goto error; |
| } |
| while (map_size > 0) { |
| if (!span_read_key_str(ctx, key, sizeof(key), err, err_len)) { |
| goto error; |
| } |
| switch (key[0]) { |
| case 'a': |
| if (!htrace_span_id_read_msgpack(&span->span_id, ctx)) { |
| snprintf(err, err_len, "span_read_msgpack: " |
| "htrace_span_id_read_msgpack failed for span->span_id"); |
| goto error; |
| } |
| break; |
| case 'd': |
| if (span->desc) { |
| free(span->desc); |
| } |
| span->desc = cmp_read_malloced_string(ctx, "description", |
| err, err_len); |
| if (err[0]) { |
| goto error; |
| } |
| break; |
| case 'b': |
| if (!cmp_read_u64(ctx, &span->begin_ms)) { |
| snprintf(err, err_len, "span_read_msgpack: cmp_read_u64 " |
| "failed for span->begin_ms."); |
| goto error; |
| } |
| break; |
| case 'e': |
| if (!cmp_read_u64(ctx, &span->end_ms)) { |
| snprintf(err, err_len, "span_read_msgpack: cmp_read_u64 " |
| "failed for span->end_ms."); |
| goto error; |
| } |
| break; |
| case 'r': |
| if (span->trid) { |
| free(span->trid); |
| } |
| span->trid = cmp_read_malloced_string(ctx, "tracer_id", |
| err, err_len); |
| if (err[0]) { |
| goto error; |
| } |
| break; |
| case 'p': |
| span_parse_msgpack_parents(ctx, span, err, err_len); |
| if (err[0]) { |
| goto error; |
| } |
| break; |
| default: |
| snprintf(err, err_len, "span_read_msgpack: can't understand key " |
| "'%s'.\n", key); |
| goto error; |
| } |
| map_size--; |
| } |
| // Description cannot be NULL. |
| if (!span->desc) { |
| span->desc = strdup(""); |
| if (!span->desc) { |
| snprintf(err, err_len, "span_read_msgpack: OOM allocating empty " |
| "description string."); |
| goto error; |
| } |
| } |
| return span; |
| |
| error: |
| htrace_span_free(span); |
| return NULL; |
| } |
| |
| // vim:ts=4:sw=4:et |