blob: 04490a9d6d85f861bdf1ba43635a2790c485084c [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 "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