http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/receiver/htraced.c ---------------------------------------------------------------------- diff --git a/htrace-c/src/receiver/htraced.c b/htrace-c/src/receiver/htraced.c new file mode 100644 index 0000000..ea61541 --- /dev/null +++ b/htrace-c/src/receiver/htraced.c @@ -0,0 +1,649 @@ +/** + * 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/conf.h" +#include "core/htrace.h" +#include "core/htracer.h" +#include "core/span.h" +#include "receiver/curl.h" +#include "receiver/receiver.h" +#include "test/test.h" +#include "util/log.h" +#include "util/time.h" + +#include <errno.h> +#include <curl/curl.h> +#include <inttypes.h> +#include <pthread.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/** + * @file htraced.c + * + * The htraced span receiver which implements sending spans on to htraced. + */ + +/** + * The minimum buffer size to allow for the htraced circular buffer. + * + * This should hopefully allow at least a few spans to be buffered. + */ +#define HTRACED_MIN_BUFFER_SIZE (4ULL * 1024ULL * 1024ULL) + +/** + * The maximum buffer size to allow for the htraced circular buffer. + * This is mainly to avoid overflow. Of course, you couldn't allocate a buffer + * anywhere near this size anyway. + */ +#define HTRACED_MAX_BUFFER_SIZE 0x7ffffffffffffffLL + +/** + * The minimum number of milliseconds to allow for send_timeo_ms. + */ +#define HTRACED_SEND_TIMEO_MS_MIN 30000LL + +/** + * The maximum number of milliseconds to allow for send_timeo_ms. + * This is mainly to avoid overflow. + */ +#define HTRACED_SEND_TIMEO_MS_MAX 86400000LL + +/** + * The maximum size of the message we will send over the wire. + * This also sets the size of the transmission buffer. + * This constant must not be more than 2^^32 on 32-bit systems. + */ +#define HTRACED_MAX_MSG_LEN (8ULL * 1024ULL * 1024ULL) + +/** + * The maximum number of times we will try to add a span to the circular buffer + * before giving up. + */ +#define HTRACED_MAX_ADD_TRIES 3 + +/** + * The maximum number of times to try to send some spans to the htraced daemon + * before giving up. + */ +#define HTRACED_MAX_SEND_TRIES 3 + +/** + * The number of milliseconds to sleep after failing to send some spans to the + * htraced daemon. + */ +#define HTRACED_SEND_RETRY_SLEEP_MS 5000 + +/* + * A span receiver that writes spans to htraced. + */ +struct htraced_rcv { + struct htrace_rcv base; + + /** + * Nonzero if the receiver should shut down. + */ + int shutdown; + + /** + * The htracer object associated with this receciver. + */ + struct htracer *tracer; + + /** + * The HTraced server URL. + * Dynamically allocated. + */ + char *url; + + /** + * Buffered span data becomes eligible to be sent even if there isn't much + * in the buffer after this timeout elapses. + */ + uint64_t send_timeo_ms; + + /** + * The maximum number of bytes we will buffer before waking the sending + * thread. We may sometimes send slightly more than this amount if the + * thread takes a while to wake up. + */ + uint64_t send_threshold; + + /** + * The CURL handle. + */ + CURL *curl; + + /** + * Length of the circular buffer. + */ + uint64_t clen; + + /** + * A circular buffer containing span data. + */ + uint8_t *cbuf; + + /** + * 'start' pointer of the circular buffer. + */ + uint64_t cstart; + + /** + * 'end' pointer of the circular buffer. + */ + uint64_t cend; + + /** + * The monotonic-clock time at which we last did a send operation. + */ + uint64_t last_send_ms; + + /** + * RPC messages are copied into this buffer before being sent. + * Its length is HTRACED_MAX_MSG_LEN. + */ + uint8_t *sbuf; + + /** + * Lock protecting the circular buffer from concurrent writes. + */ + pthread_mutex_t lock; + + /** + * Condition variable used to wake up the background thread. + */ + pthread_cond_t cond; + + /** + * Background transmitter thread. + */ + pthread_t xmit_thread; +}; + +void* run_htraced_xmit_manager(void *data); +static int should_xmit(struct htraced_rcv *rcv, uint64_t now); +static void htraced_xmit(struct htraced_rcv *rcv, uint64_t now); +static uint64_t cbuf_used(const struct htraced_rcv *rcv); +static int32_t cbuf_to_sbuf(struct htraced_rcv *rcv); + +static struct htrace_rcv *htraced_rcv_create(struct htracer *tracer, + const struct htrace_conf *conf) +{ + struct htraced_rcv *rcv; + const char *url; + int ret; + + url = htrace_conf_get(conf, HTRACED_ADDRESS_KEY); + if (!url) { + htrace_log(tracer->lg, "htraced_rcv_create: no value found for %s. " + "You must set this configuration key to the " + "hostname:port identifying the htraced server.\n", + HTRACED_ADDRESS_KEY); + goto error; + } + rcv = malloc(sizeof(*rcv)); + if (!rcv) { + htrace_log(tracer->lg, "htraced_rcv_create: OOM while " + "allocating htraced_rcv.\n"); + goto error; + } + rcv->base.ty = &g_htraced_rcv_ty; + rcv->shutdown = 0; + rcv->tracer = tracer; + if (asprintf(&rcv->url, "%s/writeSpans", url) < 0) { + rcv->url = NULL; + goto error_free_rcv; + } + rcv->send_timeo_ms = htrace_conf_get_u64(tracer->lg, conf, + HTRACED_SEND_TIMEOUT_MS_KEY); + if (rcv->send_timeo_ms < HTRACED_SEND_TIMEO_MS_MIN) { + htrace_log(tracer->lg, "htraced_rcv_create: invalid send timeout of %" + PRId64 " ms. Setting the minimum timeout of %lld" + " ms instead.\n", rcv->send_timeo_ms, HTRACED_SEND_TIMEO_MS_MIN); + rcv->send_timeo_ms = HTRACED_SEND_TIMEO_MS_MIN; + } else if (rcv->send_timeo_ms > HTRACED_SEND_TIMEO_MS_MAX) { + htrace_log(tracer->lg, "htraced_rcv_create: invalid send timeout of %" + PRId64 " ms. Setting the maximum timeout of %lld" + " ms instead.\n", rcv->send_timeo_ms, HTRACED_SEND_TIMEO_MS_MAX); + rcv->send_timeo_ms = HTRACED_SEND_TIMEO_MS_MAX; + } + rcv->curl = htrace_curl_init(tracer->lg, conf); + if (!rcv->curl) { + goto error_free_url; + } + rcv->clen = htrace_conf_get_u64(tracer->lg, conf, HTRACED_BUFFER_SIZE_KEY); + if (rcv->clen < HTRACED_MIN_BUFFER_SIZE) { + htrace_log(tracer->lg, "htraced_rcv_create: invalid buffer size %" PRId64 + ". Setting the minimum buffer size of %llu" + " instead.\n", rcv->clen, HTRACED_MIN_BUFFER_SIZE); + rcv->clen = HTRACED_MIN_BUFFER_SIZE; + } else if (rcv->clen > HTRACED_MAX_BUFFER_SIZE) { + htrace_log(tracer->lg, "htraced_rcv_create: invalid buffer size %" PRId64 + ". Setting the maximum buffer size of %lld" + " instead.\n", rcv->clen, HTRACED_MAX_BUFFER_SIZE); + rcv->clen = HTRACED_MAX_BUFFER_SIZE; + } + rcv->cbuf = malloc(rcv->clen); + if (!rcv->cbuf) { + htrace_log(tracer->lg, "htraced_rcv_create: failed to malloc %"PRId64 + " bytes for the htraced circular buffer.\n", rcv->clen); + goto error_free_curl; + } + // Send when the buffer gets 1/4 full. + rcv->send_threshold = rcv->clen * 0.25; + rcv->cstart = 0; + rcv->cend = 0; + rcv->last_send_ms = monotonic_now_ms(tracer->lg); + rcv->sbuf = malloc(HTRACED_MAX_MSG_LEN); + if (!rcv->sbuf) { + goto error_free_cbuf; + } + ret = pthread_mutex_init(&rcv->lock, NULL); + if (ret) { + htrace_log(tracer->lg, "htraced_rcv_create: pthread_mutex_init " + "error %d: %s\n", ret, terror(ret)); + goto error_free_sbuf; + } + ret = pthread_cond_init(&rcv->cond, NULL); + if (ret) { + htrace_log(tracer->lg, "htraced_rcv_create: pthread_cond_init " + "error %d: %s\n", ret, terror(ret)); + goto error_free_lock; + } + ret = pthread_create(&rcv->xmit_thread, NULL, run_htraced_xmit_manager, rcv); + if (ret) { + htrace_log(tracer->lg, "htraced_rcv_create: failed to create xmit thread: " + "error %d: %s\n", ret, terror(ret)); + goto error_free_cvar; + } + htrace_log(tracer->lg, "Initialized htraced receiver with url=%s, " + "send_timeo_ms=%" PRId64 ", send_threshold=%" PRId64 ", clen=%" + PRId64 ".\n", rcv->url, rcv->send_timeo_ms, rcv->send_threshold, + rcv->clen); + return (struct htrace_rcv*)rcv; + +error_free_cvar: + pthread_cond_destroy(&rcv->cond); +error_free_lock: + pthread_mutex_destroy(&rcv->lock); +error_free_sbuf: + free(rcv->sbuf); +error_free_cbuf: + free(rcv->cbuf); +error_free_curl: + htrace_curl_free(tracer->lg, rcv->curl); +error_free_url: + free(rcv->url); +error_free_rcv: + free(rcv); +error: + return NULL; +} + +void* run_htraced_xmit_manager(void *data) +{ + struct htraced_rcv *rcv = data; + struct htrace_log *lg = rcv->tracer->lg; + uint64_t now, wakeup; + struct timespec wakeup_ts; + int ret; + + pthread_mutex_lock(&rcv->lock); + while (1) { + now = monotonic_now_ms(lg); + while (should_xmit(rcv, now)) { + htraced_xmit(rcv, now); + } + if (rcv->shutdown) { + while (cbuf_used(rcv) > 0) { + htraced_xmit(rcv, now); + } + break; + } + // Wait for one of a few things to happen: + // * Shutdown + // * The wakeup timer to elapse, leading us to check if we should send + // because of send_timeo_ms. + // * A writer to signal that we should wake up because enough bytes are + // buffered. + wakeup = now + (rcv->send_timeo_ms / 2); + ms_to_timespec(wakeup, &wakeup_ts); + ret = pthread_cond_timedwait(&rcv->cond, &rcv->lock, &wakeup_ts); + if ((ret != 0) && (ret != ETIMEDOUT)) { + htrace_log(lg, "run_htraced_xmit_manager: pthread_cond_timedwait " + "error: %d (%s)\n", ret, terror(ret)); + } + } + pthread_mutex_unlock(&rcv->lock); + htrace_log(lg, "run_htraced_xmit_manager: shutting down the transmission " + "manager thread.\n"); + return NULL; +} + +/** + * Determine if the xmit manager should send. + * This function must be called with the lock held. + * + * @param rcv The htraced receiver. + * @param now The current time in milliseconds. + * + * @return nonzero if we should send now. + */ +static int should_xmit(struct htraced_rcv *rcv, uint64_t now) +{ + uint64_t used; + + used = cbuf_used(rcv); + if (used > rcv->send_threshold) { + // We have buffered a lot of bytes, so let's send. + return 1; + } + if (now - rcv->last_send_ms > rcv->send_timeo_ms) { + // It's been too long since the last transmission, so let's send. + if (used > 0) { + return 1; + } + } + return 0; // Let's wait. +} + +/** + * Send all the spans which we have buffered. + * + * @param rcv The htraced receiver. + * @param slen The length of the buffer to send. + * + * @return 1 on success; 0 otherwise. + */ +static int htraced_xmit_impl(struct htraced_rcv *rcv, int32_t slen) +{ + struct htrace_log *lg = rcv->tracer->lg; + CURLcode res; + char *pid_header = NULL; + struct curl_slist *headers = NULL; + int ret = 0; + + // Disable the use of SIGALARM to interrupt DNS lookups. + curl_easy_setopt(rcv->curl, CURLOPT_NOSIGNAL, 1); + // Do not use a global DNS cache. + curl_easy_setopt(rcv->curl, CURLOPT_DNS_USE_GLOBAL_CACHE, 0); + // Disable verbosity. + curl_easy_setopt(rcv->curl, CURLOPT_VERBOSE, 0); + // The user agent is libhtraced. + curl_easy_setopt(rcv->curl, CURLOPT_USERAGENT, "libhtraced"); + // Set URL + curl_easy_setopt(rcv->curl, CURLOPT_URL, rcv->url); + // Set POST + curl_easy_setopt(rcv->curl, CURLOPT_POST, 1L); + // Set the size that we're copying from rcv->sbuf + curl_easy_setopt(rcv->curl, CURLOPT_POSTFIELDSIZE, (long)slen); + if (asprintf(&pid_header, "htrace-pid: %s", rcv->tracer->prid) < 0) { + htrace_log(lg, "htraced_xmit(%s) failed: OOM allocating htrace-pid\n", + rcv->url); + goto done; + } + curl_easy_setopt(rcv->curl, CURLOPT_POSTFIELDS, rcv->sbuf); + headers = curl_slist_append(headers, pid_header); + if (!headers) { + htrace_log(lg, "htraced_xmit(%s) failed: OOM allocating headers\n", + rcv->url); + return 0; + } + headers = curl_slist_append(headers, "Content-Type: application/json"); + if (!headers) { + htrace_log(lg, "htraced_xmit(%s) failed: OOM allocating headers\n", + rcv->url); + return 0; + } + curl_easy_setopt(rcv->curl, CURLOPT_HTTPHEADER, headers); + res = curl_easy_perform(rcv->curl); + if (res != CURLE_OK) { + htrace_log(lg, "htraced_xmit(%s) failed: error %lld (%s)\n", + rcv->url, (long long)res, curl_easy_strerror(res)); + } + ret = res == CURLE_OK; +done: + curl_easy_reset(rcv->curl); + free(pid_header); + curl_slist_free_all(headers); + return ret; +} + +static void htraced_xmit(struct htraced_rcv *rcv, uint64_t now) +{ + int32_t slen; + int tries = 0; + + // Move span data from the circular buffer into the transmission buffer. + slen = cbuf_to_sbuf(rcv); + + // Release the lock while doing network I/O, so that we don't block threads + // adding spans. + pthread_mutex_unlock(&rcv->lock); + while (1) { + int retry, success = htraced_xmit_impl(rcv, slen); + if (success) { + break; + } + tries++; + retry = (tries < HTRACED_MAX_SEND_TRIES); + htrace_log(rcv->tracer->lg, "htraced_xmit(%s) failed on try %d. %s\n", + rcv->url, tries, + (retry ? "Retrying after a delay." : "Giving up.")); + if (!retry) { + break; + } + } + pthread_mutex_lock(&rcv->lock); + rcv->last_send_ms = now; +} + +/** + * Move data from the circular buffer into the transmission buffer, advancing + * the circular buffer's start offset. + * + * This function must be called with the lock held. + * + * Note that we rely on HTRACED_MAX_MSG_LEN being < 4GB in this function for + * correctness on 32-bit systems. + * + * @param rcv The htraced receiver. + * + * @return The amount of data copied. + */ +static int32_t cbuf_to_sbuf(struct htraced_rcv *rcv) +{ + int32_t rem = HTRACED_MAX_MSG_LEN; + size_t amt; + + if (rcv->cstart < rcv->cend) { + amt = rcv->cend - rcv->cstart; + if (amt > rem) { + amt = rem; + } + memcpy(rcv->sbuf, rcv->cbuf + rcv->cstart, amt); + rem -= amt; + rcv->cstart += amt; + } else { + amt = rcv->clen - rcv->cstart; + if (amt > rem) { + amt = rem; + } + memcpy(rcv->sbuf, rcv->cbuf + rcv->cstart, amt); + rem -= amt; + rcv->cstart += amt; + if (rem > 0) { + amt = rcv->cend; + if (amt > rem) { + amt = rem; + } + memcpy(rcv->sbuf, rcv->cbuf, amt); + rem -= amt; + rcv->cstart = amt; + } + } + return HTRACED_MAX_MSG_LEN - rem; +} + +/** + * Returns the current number of bytes used in the htraced circular buffer. + * Must be called under the lock. + * + * @param rcv The htraced receiver. + * + * @return The number of bytes used. + */ +static uint64_t cbuf_used(const struct htraced_rcv *rcv) +{ + if (rcv->cstart <= rcv->cend) { + return rcv->cend - rcv->cstart; + } + return rcv->clen - (rcv->cstart - rcv->cend); +} + +static void htraced_rcv_add_span(struct htrace_rcv *r, + struct htrace_span *span) +{ + int json_len, tries, retry; + uint64_t used, rem; + struct htraced_rcv *rcv = (struct htraced_rcv *)r; + struct htrace_log *lg = rcv->tracer->lg; + + { + char buf[4096]; + span_json_sprintf(span, sizeof(buf), buf); + } + + json_len = span_json_size(span); + tries = 0; + do { + pthread_mutex_lock(&rcv->lock); + used = cbuf_used(rcv); + if (used + json_len >= rcv->clen) { + pthread_cond_signal(&rcv->cond); + pthread_mutex_unlock(&rcv->lock); + tries++; + retry = tries < HTRACED_MAX_ADD_TRIES; + htrace_log(lg, "htraced_rcv_add_span: not enough space in the " + "circular buffer. Have %" PRId64 ", need %d" + ". %s...\n", (rcv->clen - used), json_len, + (retry ? "Retrying" : "Giving up")); + if (retry) { + pthread_yield(); + continue; + } + return; + } + } while (0); + // OK, now we have the lock, and we know that there is enough space in the + // circular buffer. + rem = rcv->clen - rcv->cend; + if (rem < json_len) { + // Handle a 'torn write' where the circular buffer loops around to the + // beginning in the middle of the write. + char *temp = alloca(json_len); + span_json_sprintf(span, json_len, temp); + temp[json_len - 1] = '\n'; + memcpy(rcv->cbuf + rcv->cend, temp, rem); + memcpy(rcv->cbuf, temp + rem, json_len - rem); + rcv->cend = json_len - rem; + } else { + span_json_sprintf(span, json_len, rcv->cbuf + rcv->cend); + rcv->cbuf[rcv->cend + json_len - 1] = '\n'; + rcv->cend += json_len; + } + used += json_len; + if (used > rcv->send_threshold) { + pthread_cond_signal(&rcv->cond); + } + pthread_mutex_unlock(&rcv->lock); +} + +static void htraced_rcv_flush(struct htrace_rcv *r) +{ + struct htraced_rcv *rcv = (struct htraced_rcv *)r; + + while (1) { + pthread_mutex_lock(&rcv->lock); + if (cbuf_used(rcv) == 0) { + // If the buffer is empty, we're done. + // Note that there is no guarantee that we'll ever be done if spans + // are being added continuously throughout the flush. This is OK, + // since flush() is actually only used by unit tests. + // We could do something more clever here, but it would be a lot more + // complex. + pthread_mutex_unlock(&rcv->lock); + break; + } + // Get the xmit thread to send what it can, by resetting the "last send + // time" to the oldest possible monotonic time. + rcv->last_send_ms = 0; + pthread_cond_signal(&rcv->cond); + pthread_mutex_unlock(&rcv->lock); + } +} + +static void htraced_rcv_free(struct htrace_rcv *r) +{ + struct htraced_rcv *rcv = (struct htraced_rcv *)r; + struct htrace_log *lg; + int ret; + + if (!rcv) { + return; + } + lg = rcv->tracer->lg; + htrace_log(lg, "Shutting down htraced receiver with url=%s\n", rcv->url); + pthread_mutex_lock(&rcv->lock); + rcv->shutdown = 1; + pthread_cond_signal(&rcv->cond); + pthread_mutex_unlock(&rcv->lock); + ret = pthread_join(rcv->xmit_thread, NULL); + if (ret) { + htrace_log(lg, "htraced_rcv_free: pthread_join " + "error %d: %s\n", ret, terror(ret)); + } + free(rcv->url); + free(rcv->cbuf); + free(rcv->sbuf); + htrace_curl_free(lg, rcv->curl); + ret = pthread_mutex_destroy(&rcv->lock); + if (ret) { + htrace_log(lg, "htraced_rcv_free: pthread_mutex_destroy " + "error %d: %s\n", ret, terror(ret)); + } + ret = pthread_cond_destroy(&rcv->cond); + if (ret) { + htrace_log(lg, "htraced_rcv_free: pthread_cond_destroy " + "error %d: %s\n", ret, terror(ret)); + } + free(rcv); +} + +const struct htrace_rcv_ty g_htraced_rcv_ty = { + "htraced", + htraced_rcv_create, + htraced_rcv_add_span, + htraced_rcv_flush, + htraced_rcv_free, +}; + +// vim:ts=4:sw=4:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/receiver/local_file.c ---------------------------------------------------------------------- diff --git a/htrace-c/src/receiver/local_file.c b/htrace-c/src/receiver/local_file.c new file mode 100644 index 0000000..667da29 --- /dev/null +++ b/htrace-c/src/receiver/local_file.c @@ -0,0 +1,184 @@ +/** + * 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/conf.h" +#include "core/htrace.h" +#include "core/htracer.h" +#include "core/span.h" +#include "receiver/receiver.h" +#include "util/log.h" + +#include <errno.h> +#include <pthread.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/* + * A span receiver that writes spans to a local file. + */ +struct local_file_rcv { + struct htrace_rcv base; + + /** + * The htracer object associated with this receciver. + */ + struct htracer *tracer; + + /** + * The local file. + */ + FILE *fp; + + /** + * Path to the local file. Dynamically allocated. + */ + char *path; + + /** + * Lock protecting the local file from concurrent writes. + */ + pthread_mutex_t lock; +}; + +static void local_file_rcv_free(struct htrace_rcv *r); + +static struct htrace_rcv *local_file_rcv_create(struct htracer *tracer, + const struct htrace_conf *conf) +{ + struct local_file_rcv *rcv; + const char *path; + int ret; + + path = htrace_conf_get(conf, HTRACE_LOCAL_FILE_RCV_PATH_KEY); + if (!path) { + htrace_log(tracer->lg, "local_file_rcv_create: no value found for %s. " + "You must set this configuration key to the path you wish " + "to write spans to.\n", HTRACE_LOCAL_FILE_RCV_PATH_KEY); + return NULL; + } + rcv = calloc(1, sizeof(*rcv)); + if (!rcv) { + htrace_log(tracer->lg, "local_file_rcv_create: OOM while " + "allocating local_file_rcv.\n"); + return NULL; + } + ret = pthread_mutex_init(&rcv->lock, NULL); + if (ret) { + htrace_log(tracer->lg, "local_file_rcv_create: failed to " + "create mutex while setting up local_file_rcv: " + "error %d (%s)\n", ret, terror(ret)); + free(rcv); + return NULL; + } + rcv->base.ty = &g_local_file_rcv_ty; + rcv->path = strdup(path); + if (!rcv->path) { + local_file_rcv_free((struct htrace_rcv*)rcv); + return NULL; + } + rcv->tracer = tracer; + rcv->fp = fopen(path, "a"); + if (!rcv->fp) { + ret = errno; + htrace_log(tracer->lg, "local_file_rcv_create: failed to " + "open '%s' for write: error %d (%s)\n", + path, ret, terror(ret)); + local_file_rcv_free((struct htrace_rcv*)rcv); + } + htrace_log(tracer->lg, "Initialized local_file receiver with path=%s.\n", + rcv->path); + return (struct htrace_rcv*)rcv; +} + +static void local_file_rcv_add_span(struct htrace_rcv *r, + struct htrace_span *span) +{ + int len, res, err; + char *buf; + struct local_file_rcv *rcv = (struct local_file_rcv *)r; + + span->prid = rcv->tracer->prid; + len = span_json_size(span); + buf = malloc(len + 1); + if (!buf) { + span->prid = NULL; + htrace_log(rcv->tracer->lg, "local_file_rcv_add_span: OOM\n"); + return; + } + span_json_sprintf(span, len, buf); + span->prid = NULL; + buf[len - 1] = '\n'; + buf[len] = '\0'; + pthread_mutex_lock(&rcv->lock); + res = fwrite(buf, 1, len, rcv->fp); + err = errno; + pthread_mutex_unlock(&rcv->lock); + if (res < len) { + htrace_log(rcv->tracer->lg, "local_file_rcv_add_span(%s): fwrite error: " + "%d (%s)\n", rcv->path, err, terror(err)); + } + free(buf); +} + +static void local_file_rcv_flush(struct htrace_rcv *r) +{ + struct local_file_rcv *rcv = (struct local_file_rcv *)r; + if (fflush(rcv->fp) < 0) { + int e = errno; + htrace_log(rcv->tracer->lg, "local_file_rcv_flush(path=%s): fflush " + "error: %s\n", rcv->path, terror(e)); + } +} + +static void local_file_rcv_free(struct htrace_rcv *r) +{ + struct local_file_rcv *rcv = (struct local_file_rcv *)r; + int ret; + struct htrace_log *lg; + + if (!rcv) { + return; + } + lg = rcv->tracer->lg; + htrace_log(lg, "Shutting down local_file receiver with path=%s\n", + rcv->path); + ret = pthread_mutex_destroy(&rcv->lock); + if (ret) { + htrace_log(lg, "local_file_rcv_free: pthread_mutex_destroy " + "error %d: %s\n", ret, terror(ret)); + } + ret = fclose(rcv->fp); + if (ret) { + htrace_log(lg, "local_file_rcv_free: fclose error " + "%d: %s\n", ret, terror(ret)); + } + free(rcv->path); + free(rcv); +} + +const struct htrace_rcv_ty g_local_file_rcv_ty = { + "local.file", + local_file_rcv_create, + local_file_rcv_add_span, + local_file_rcv_flush, + local_file_rcv_free, +}; + +// vim:ts=4:sw=4:et http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/receiver/noop.c ---------------------------------------------------------------------- diff --git a/htrace-c/src/receiver/noop.c b/htrace-c/src/receiver/noop.c new file mode 100644 index 0000000..980854b --- /dev/null +++ b/htrace-c/src/receiver/noop.c @@ -0,0 +1,65 @@ +/** + * 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/htracer.h" +#include "receiver/receiver.h" +#include "util/log.h" + +/** + * A span receiver that does nothing but discard all spans. + */ +struct noop_rcv { + struct htrace_rcv base; +}; + +struct noop_rcv g_noop_rcv = { + { &g_noop_rcv_ty }, +}; + +static struct htrace_rcv *noop_rcv_create(struct htracer *tracer, + const struct htrace_conf *conf) +{ + htrace_log(tracer->lg, "Using no-op htrace span receiver.\n"); + return (struct htrace_rcv *)&g_noop_rcv; +} + +static void noop_rcv_add_span(struct htrace_rcv *rcv, + struct htrace_span *span) +{ + // do nothing +} + +static void noop_rcv_flush(struct htrace_rcv *rcv) +{ + // do nothing +} + +static void noop_rcv_free(struct htrace_rcv *rcv) +{ + // do nothing +} + +const struct htrace_rcv_ty g_noop_rcv_ty = { + "noop", + noop_rcv_create, + noop_rcv_add_span, + noop_rcv_flush, + noop_rcv_free, +}; + +// vim:ts=4:sw=4:et http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/receiver/receiver.c ---------------------------------------------------------------------- diff --git a/htrace-c/src/receiver/receiver.c b/htrace-c/src/receiver/receiver.c new file mode 100644 index 0000000..7fd3669 --- /dev/null +++ b/htrace-c/src/receiver/receiver.c @@ -0,0 +1,75 @@ +/** + * 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/conf.h" +#include "core/htrace.h" +#include "core/htracer.h" +#include "receiver/receiver.h" +#include "util/log.h" + +#include <stdint.h> +#include <string.h> + +const struct htrace_rcv_ty * const g_rcv_tys[] = { + &g_noop_rcv_ty, + &g_local_file_rcv_ty, + &g_htraced_rcv_ty, + NULL, +}; + +static const struct htrace_rcv_ty *select_rcv_ty(struct htracer *tracer, + const struct htrace_conf *conf) +{ + const char *tstr; + const char *prefix = ""; + size_t i; + char buf[256] = { 0 }; + + tstr = htrace_conf_get(conf, HTRACE_SPAN_RECEIVER_KEY); + if (!tstr) { + htrace_log(tracer->lg, "No %s configured.\n", HTRACE_SPAN_RECEIVER_KEY); + return &g_noop_rcv_ty; + } + for (i = 0; g_rcv_tys[i]; i++) { + if (strcmp(g_rcv_tys[i]->name, tstr) == 0) { + return g_rcv_tys[i]; + } + } + for (i = 0; g_rcv_tys[i]; i++) { + if ((strlen(buf) + strlen(prefix) + + strlen(g_rcv_tys[i]->name)) < sizeof(buf)) { + strcat(buf, prefix); + strcat(buf, g_rcv_tys[i]->name); + prefix = ", "; + } + } + htrace_log(tracer->lg, "Unknown span receiver type as '%s'. Valid " + "span receiver types are: %s\n", tstr, buf); + return &g_noop_rcv_ty; +} + +struct htrace_rcv *htrace_rcv_create(struct htracer *tracer, + const struct htrace_conf *conf) +{ + const struct htrace_rcv_ty *ty; + + ty = select_rcv_ty(tracer, conf); + return ty->create(tracer, conf); +} + +// vim:ts=4:sw=4:et http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/receiver/receiver.h ---------------------------------------------------------------------- diff --git a/htrace-c/src/receiver/receiver.h b/htrace-c/src/receiver/receiver.h new file mode 100644 index 0000000..1e01486 --- /dev/null +++ b/htrace-c/src/receiver/receiver.h @@ -0,0 +1,114 @@ +/** + * 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. + */ + +#ifndef APACHE_HTRACE_RECEIVER_RECEIVER_H +#define APACHE_HTRACE_RECEIVER_RECEIVER_H + +/** + * @file rcv.h + * + * Functions related to HTrace receivers. + * + * This is an internal header, not intended for external use. + */ + +struct htrace_conf; +struct htrace_span; +struct htracer; + +/** + * Base class for an HTrace span receiver. + * + * Implementations should begin with this class as the first member. + */ +struct htrace_rcv { + /** + * The type of the receiver. + */ + const struct htrace_rcv_ty *ty; +}; + +/** + * A table of callbacks that implements an HTrace span receiver. + */ +struct htrace_rcv_ty { + /** + * The name of this HTrace span receiver type. + */ + const char * const name; + + /** + * Create an HTrace span receiver of this type. + * + * @param tracer The HTrace context to use. The span receiver may + * hold on to this pointer. + * @param conf The HTrace configuration to use. The span + * receiver must not hold on to this pointer. + * + * @return The HTrace span receciver. + */ + struct htrace_rcv *(*create)(struct htracer *tracer, + const struct htrace_conf *conf); + + /** + * Callback to add a new span. + * + * @param rcv The HTrace span receiver. + * @param span The trace span to add. + */ + void (*add_span)(struct htrace_rcv *rcv, struct htrace_span *span); + + /** + * Flush all buffered spans to the backing store used by this receiver. + * + * @param rcv The HTrace span receiver. + */ + void (*flush)(struct htrace_rcv *rcv); + + /** + * Frees this HTrace span receiver. + * + * @param rcv The HTrace span receiver. + */ + void (*free)(struct htrace_rcv *rcv); +}; + +/** + * Create an HTrace span receiver. + * + * @param tracer The HTrace context to use. The newly created + * span receiver may hold on to this pointer. + * @param conf The HTrace configuration to use. The newly + * created span receiver will not hold on to this + * pointer. + * + * @return The HTrace span receciver. + */ +struct htrace_rcv *htrace_rcv_create(struct htracer *tracer, + const struct htrace_conf *conf); + +/* + * HTrace span receiver types. + */ +const struct htrace_rcv_ty g_noop_rcv_ty; +const struct htrace_rcv_ty g_local_file_rcv_ty; +const struct htrace_rcv_ty g_htraced_rcv_ty; + +#endif + +// vim: ts=4: sw=4: et http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/sampler/always.c ---------------------------------------------------------------------- diff --git a/htrace-c/src/sampler/always.c b/htrace-c/src/sampler/always.c new file mode 100644 index 0000000..a570e79 --- /dev/null +++ b/htrace-c/src/sampler/always.c @@ -0,0 +1,71 @@ +/** + * 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 "sampler/sampler.h" + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/** + * A sampler that always fires. + */ +struct always_sampler { + struct htrace_sampler base; +}; + +static struct htrace_sampler *always_sampler_create(struct htracer *tracer, + const struct htrace_conf *conf); +static const char *always_sampler_to_str(struct htrace_sampler *sampler); +static int always_sampler_next(struct htrace_sampler *sampler); +static void always_sampler_free(struct htrace_sampler *sampler); + +const struct htrace_sampler_ty g_always_sampler_ty = { + "always", + always_sampler_create, + always_sampler_to_str, + always_sampler_next, + always_sampler_free, +}; + +const struct always_sampler g_always_sampler = { + { (struct htrace_sampler_ty*) &g_always_sampler_ty }, +}; + +static struct htrace_sampler *always_sampler_create(struct htracer *tracer, + const struct htrace_conf *conf) +{ + return (struct htrace_sampler*)&g_always_sampler; +} + +static const char *always_sampler_to_str(struct htrace_sampler *sampler) +{ + return "AlwaysSampler"; +} + +static int always_sampler_next(struct htrace_sampler *sampler) +{ + return 1; +} + +static void always_sampler_free(struct htrace_sampler *sampler) +{ + // do nothing. +} + +// vim: ts=4:sw=4:tw=79:et http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/sampler/never.c ---------------------------------------------------------------------- diff --git a/htrace-c/src/sampler/never.c b/htrace-c/src/sampler/never.c new file mode 100644 index 0000000..cf25bfa --- /dev/null +++ b/htrace-c/src/sampler/never.c @@ -0,0 +1,71 @@ +/** + * 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 "sampler/sampler.h" + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/** + * A sampler that never fires. + */ +struct never_sampler { + struct htrace_sampler base; +}; + +static struct htrace_sampler *never_sampler_create(struct htracer *tracer, + const struct htrace_conf *conf); +static const char *never_sampler_to_str(struct htrace_sampler *sampler); +static int never_sampler_next(struct htrace_sampler *sampler); +static void never_sampler_free(struct htrace_sampler *sampler); + +const struct htrace_sampler_ty g_never_sampler_ty = { + "never", + never_sampler_create, + never_sampler_to_str, + never_sampler_next, + never_sampler_free, +}; + +struct never_sampler g_never_sampler = { + { (struct htrace_sampler_ty*) &g_never_sampler_ty }, +}; + +static struct htrace_sampler *never_sampler_create(struct htracer *tracer, + const struct htrace_conf *conf) +{ + return (struct htrace_sampler*)&g_never_sampler; +} + +static const char *never_sampler_to_str(struct htrace_sampler *sampler) +{ + return "NeverSampler"; +} + +static int never_sampler_next(struct htrace_sampler *sampler) +{ + return 0; +} + +static void never_sampler_free(struct htrace_sampler *sampler) +{ + // do nothing. +} + +// vim: ts=4:sw=4:tw=79:et http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/sampler/prob.c ---------------------------------------------------------------------- diff --git a/htrace-c/src/sampler/prob.c b/htrace-c/src/sampler/prob.c new file mode 100644 index 0000000..789a2eb --- /dev/null +++ b/htrace-c/src/sampler/prob.c @@ -0,0 +1,137 @@ +/** + * 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/conf.h" +#include "core/htrace.h" +#include "core/htracer.h" +#include "sampler/sampler.h" +#include "util/log.h" +#include "util/rand.h" + +#include <errno.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/** + * A sampler that fires with a certain chance. + */ +struct prob_sampler { + struct htrace_sampler base; + + /** + * A random source. + */ + struct random_src *rnd; + + /** + * The name of this probability sampler. + */ + char *name; + + /** + * The threshold at which we should sample. + */ + uint32_t threshold; +}; + +static double get_prob_sampler_threshold(struct htrace_log *lg, + const struct htrace_conf *conf); +static struct htrace_sampler *prob_sampler_create(struct htracer *tracer, + const struct htrace_conf *conf); +static const char *prob_sampler_to_str(struct htrace_sampler *s); +static int prob_sampler_next(struct htrace_sampler *s); +static void prob_sampler_free(struct htrace_sampler *s); + +const struct htrace_sampler_ty g_prob_sampler_ty = { + "prob", + prob_sampler_create, + prob_sampler_to_str, + prob_sampler_next, + prob_sampler_free, +}; + +static double get_prob_sampler_threshold(struct htrace_log *lg, + const struct htrace_conf *conf) +{ + double fraction = + htrace_conf_get_double(lg, conf, HTRACE_PROB_SAMPLER_FRACTION_KEY); + if (fraction < 0) { + htrace_log(lg, "sampler_create: can't have a sampling fraction " + "less than 0. Setting fraction to 0.\n"); + fraction = 0.0; + } else if (fraction > 1.0) { + htrace_log(lg, "sampler_create: can't have a sampling fraction " + "greater than 1. Setting fraction to 1.\n"); + fraction = 1.0; + } + return fraction; +} + +static struct htrace_sampler *prob_sampler_create(struct htracer *tracer, + const struct htrace_conf *conf) +{ + struct prob_sampler *smp; + double fraction; + + smp = calloc(1, sizeof(*smp)); + if (!smp) { + htrace_log(tracer->lg, "prob_sampler_create: OOM\n"); + return NULL; + } + smp->base.ty = &g_prob_sampler_ty; + smp->rnd = random_src_alloc(tracer->lg); + if (!smp->rnd) { + htrace_log(tracer->lg, "random_src_alloc failed.\n"); + free(smp); + return NULL; + } + fraction = get_prob_sampler_threshold(tracer->lg, conf); + smp->threshold = 0xffffffffLU * fraction; + if (asprintf(&smp->name, "ProbabilitySampler(fraction=%.03g)", + fraction) < 0) { + smp->name = NULL; + random_src_free(smp->rnd); + free(smp); + } + return (struct htrace_sampler *)smp; +} + +static const char *prob_sampler_to_str(struct htrace_sampler *s) +{ + struct prob_sampler *smp = (struct prob_sampler *)s; + return smp->name; +} + +static int prob_sampler_next(struct htrace_sampler *s) +{ + struct prob_sampler *smp = (struct prob_sampler *)s; + return random_u32(smp->rnd) < smp->threshold; +} + +static void prob_sampler_free(struct htrace_sampler *s) +{ + struct prob_sampler *smp = (struct prob_sampler *)s; + random_src_free(smp->rnd); + free(smp->name); + free(smp); +} + +// vim: ts=4:sw=4:tw=79:et http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/sampler/sampler.c ---------------------------------------------------------------------- diff --git a/htrace-c/src/sampler/sampler.c b/htrace-c/src/sampler/sampler.c new file mode 100644 index 0000000..81aef94 --- /dev/null +++ b/htrace-c/src/sampler/sampler.c @@ -0,0 +1,88 @@ +/** + * 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/conf.h" +#include "core/htrace.h" +#include "core/htracer.h" +#include "sampler/sampler.h" +#include "util/log.h" + +#include <errno.h> +#include <inttypes.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +const struct htrace_sampler_ty * const g_sampler_tys[] = { + &g_never_sampler_ty, + &g_always_sampler_ty, + &g_prob_sampler_ty, + NULL, +}; + +static const struct htrace_sampler_ty *select_sampler_ty( + struct htracer *tracer, const struct htrace_conf *cnf) +{ + const char *tstr; + const char *prefix = ""; + size_t i; + char buf[256] = { 0 }; + + tstr = htrace_conf_get(cnf, HTRACE_SAMPLER_KEY); + if (!tstr) { + htrace_log(tracer->lg, "No %s configured.\n", HTRACE_SAMPLER_KEY); + return &g_never_sampler_ty; + } + for (i = 0; g_sampler_tys[i]; i++) { + if (strcmp(g_sampler_tys[i]->name, tstr) == 0) { + return g_sampler_tys[i]; + } + } + for (i = 0; g_sampler_tys[i]; i++) { + if ((strlen(buf) + strlen(prefix) + + strlen(g_sampler_tys[i]->name)) < sizeof(buf)) { + strcat(buf, prefix); + strcat(buf, g_sampler_tys[i]->name); + prefix = ", "; + } + } + htrace_log(tracer->lg, "Unknown sampler type '%s'. Valid " + "sampler types are: %s\n", tstr, buf); + return &g_never_sampler_ty; +} + +struct htrace_sampler *htrace_sampler_create(struct htracer *tracer, + struct htrace_conf *cnf) +{ + const struct htrace_sampler_ty *ty; + + ty = select_sampler_ty(tracer, cnf); + return ty->create(tracer, cnf); +} + +const char *htrace_sampler_to_str(struct htrace_sampler *smp) +{ + return smp->ty->to_str(smp); +} + +void htrace_sampler_free(struct htrace_sampler *smp) +{ + return smp->ty->free(smp); +} + +// vim: ts=4:sw=4:tw=79:et http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/sampler/sampler.h ---------------------------------------------------------------------- diff --git a/htrace-c/src/sampler/sampler.h b/htrace-c/src/sampler/sampler.h new file mode 100644 index 0000000..1c423e6 --- /dev/null +++ b/htrace-c/src/sampler/sampler.h @@ -0,0 +1,121 @@ +/** + * 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. + */ + +#ifndef APACHE_HTRACE_SAMPLER_SAMPLER_H +#define APACHE_HTRACE_SAMPLER_SAMPLER_H + +/** + * @file sampler.h + * + * Functions related to HTrace samplers. + * + * This is an internal header, not intended for external use. + */ + +#include <stdint.h> + +struct htrace_conf; +struct htrace_log; +struct htracer; + +/** + * Base class for an HTrace sampler. + * + * Implementations should begin with this class as the first member. + */ +struct htrace_sampler { + /** + * The type of the sampler. + */ + const struct htrace_sampler_ty *ty; +}; + +/** + * A table of callbacks that implements an HTrace sampler. + */ +struct htrace_sampler_ty { + /** + * The name of this probability sampler type. + * + * This is used to select the sampler via the configuration. + */ + const char *name; + + /** + * Create an HTrace sampler of this type. + * + * @param tracer The HTrace context to use. The sampler may + * hold on to this pointer. + * @param conf The HTrace configuration to use. The sampler + * must not hold on to this pointer. + * + * @return The HTrace span receciver. + */ + struct htrace_sampler *(*create)(struct htracer *tracer, + const struct htrace_conf *conf); + + /** + * Get the name of this HTrace sampler. + * + * @param smp The sampler. + * + * @return A description of this sampler object. This string + * must remain valid at least until the sampler + * is freed. + */ + const char*(*to_str)(struct htrace_sampler *smp); + + /** + * Sampler callback. + * + * This callback must be able to be safely called by multiple threads + * simultaneously. + * + * @param smp The HTrace sampler. + * + * @return 1 to begin a new span; 0 otherwise. + */ + int (*next)(struct htrace_sampler *smp); + + /** + * Frees this HTrace sampler. + * + * @param rcv The HTrace sampler. + */ + void (*free)(struct htrace_sampler *smp); +}; + +/** + * Get the configured fraction for the probability sampler. + * + * @param log A log to send parse error messages to. + * @param conf The configuration to use. + * + * @return A double between 0.0 and 1.0, inclusive. + */ +double get_prob_sampler_fraction(struct htrace_log *lg, + struct htrace_conf *conf); + +extern const struct htrace_sampler_ty g_never_sampler_ty; +extern const struct htrace_sampler_ty g_always_sampler_ty; +extern const struct htrace_sampler_ty g_prob_sampler_ty; +extern const struct always_sampler g_always_sampler; + +#endif + +// vim: ts=4:sw=4:et http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/test/conf-unit.c ---------------------------------------------------------------------- diff --git a/htrace-c/src/test/conf-unit.c b/htrace-c/src/test/conf-unit.c new file mode 100644 index 0000000..debc866 --- /dev/null +++ b/htrace-c/src/test/conf-unit.c @@ -0,0 +1,95 @@ +/** + * 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/conf.h" +#include "test/test.h" +#include "util/log.h" + +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static int test_simple_conf(void) +{ + struct htrace_conf *conf; + struct htrace_log *lg; + conf = htrace_conf_from_strs("foo=bar;foo2=baz;foo3=quux;foo5=123", + "foo3=default3;foo4=default4"); + lg = htrace_log_alloc(conf); + EXPECT_NONNULL(conf); + EXPECT_STR_EQ("bar", htrace_conf_get(conf, "foo")); + EXPECT_STR_EQ("quux", htrace_conf_get(conf, "foo3")); + EXPECT_STR_EQ("default4", htrace_conf_get(conf, "foo4")); + EXPECT_UINT64_EQ((uint64_t)123, htrace_conf_get_u64(lg, conf, "foo5")); + EXPECT_UINT64_EQ((uint64_t)123, htrace_conf_get_u64(lg, conf, "foo5")); + EXPECT_NULL(htrace_conf_get(conf, "unknown")); + + htrace_log_free(lg); + htrace_conf_free(conf); + return EXIT_SUCCESS; +} + +static int test_double_conf(void) +{ + struct htrace_conf *conf; + struct htrace_log *lg; + double d; + + conf = htrace_conf_from_strs("my.double=5.4;bozo=wakkawakkaa", + "my.double=1.1;bozo=2.0"); + EXPECT_NONNULL(conf); + lg = htrace_log_alloc(conf); + d = htrace_conf_get_double(lg, conf, "my.double"); + // Do a sloppy comparison to avoid thinking about IEEE float precision + // issues + if ((d > 5.401) || (d < 5.399)) { + htrace_log(lg, "failed to parse my.double... expected 5.4, " + "got %g\n", d); + return EXIT_FAILURE; + } + // 'bozo' should fall back on the default, since the configured value + // cannot be parsed. + d = htrace_conf_get_double(lg, conf, "bozo"); + if ((d > 2.001) || (d < 1.999)) { + htrace_log(lg, "failed to parse bozo... expected 2.0, " + "got %g\n", d); + return EXIT_FAILURE; + } + // 'unknown' should get 0.0, since there is no value or default. + d = htrace_conf_get_double(lg, conf, "unknown"); + if ((d > 0.001) || (d < -0.001)) { + htrace_log(lg, "failed to parse unknown... expected 0.0, " + "got %g\n", d); + return EXIT_FAILURE; + } + + htrace_log_free(lg); + htrace_conf_free(conf); + return EXIT_SUCCESS; +} + +int main(void) +{ + test_simple_conf(); + test_double_conf(); + + return EXIT_SUCCESS; +} + +// vim: ts=4:sw=4:tw=79:et http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/test/htable-unit.c ---------------------------------------------------------------------- diff --git a/htrace-c/src/test/htable-unit.c b/htrace-c/src/test/htable-unit.c new file mode 100644 index 0000000..a656fcc --- /dev/null +++ b/htrace-c/src/test/htable-unit.c @@ -0,0 +1,92 @@ +/** + * 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 "test/test.h" +#include "util/htable.h" + +#include <errno.h> +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static uint32_t simple_hash(const void *key, uint32_t size) +{ + uintptr_t k = (uintptr_t)key; + return ((13 + k) * 6367) % size; +} + +static int simple_compare(const void *a, const void *b) +{ + return a == b; +} + +static void expect_102(void *f, void *k, void *v) +{ + int *found_102 = f; + uintptr_t key = (uintptr_t)k; + uintptr_t val = (uintptr_t)v; + + if ((key == 2) && (val == 102)) { + *found_102 = 1; + } else { + abort(); + } +} + +static void *htable_pop_val(struct htable *ht, void *key) +{ + void *old_key, *old_val; + + htable_pop(ht, key, &old_key, &old_val); + return old_val; +} + +int main(void) +{ + struct htable *ht; + int found_102 = 0; + + ht = htable_alloc(4, simple_hash, simple_compare); + EXPECT_INT_EQ(0, htable_used(ht)); + EXPECT_INT_EQ(4, htable_capacity(ht)); + EXPECT_NULL(htable_get(ht, (void*)123)); + EXPECT_NULL(htable_pop_val(ht, (void*)123)); + EXPECT_INT_ZERO(htable_put(ht, (void*)123, (void*)456)); + EXPECT_UINTPTR_EQ(456L, (uintptr_t)htable_get(ht, (void*)123)); + EXPECT_UINTPTR_EQ(456L, (uintptr_t)htable_pop_val(ht, (void*)123)); + EXPECT_NULL(htable_pop_val(ht, (void*)123)); + + // Enlarge the hash table + EXPECT_INT_ZERO(htable_put(ht, (void*)1, (void*)101)); + EXPECT_INT_ZERO(htable_put(ht, (void*)2, (void*)102)); + EXPECT_INT_ZERO(htable_put(ht, (void*)3, (void*)103)); + EXPECT_INT_EQ(3, htable_used(ht)); + EXPECT_INT_EQ(8, htable_capacity(ht)); + EXPECT_UINTPTR_EQ(102L, (uintptr_t)htable_get(ht, (void*)2)); + EXPECT_UINTPTR_EQ(101L, (uintptr_t)htable_pop_val(ht, (void*)1)); + EXPECT_UINTPTR_EQ(103L, (uintptr_t)htable_pop_val(ht, (void*)3)); + EXPECT_INT_EQ(1, htable_used(ht)); + htable_visit(ht, expect_102, &found_102); + EXPECT_INT_EQ(1, found_102); + htable_free(ht); + + return EXIT_SUCCESS; +} + +// vim: ts=4:sw=4:tw=79:et http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/test/htraced_rcv-unit.c ---------------------------------------------------------------------- diff --git a/htrace-c/src/test/htraced_rcv-unit.c b/htrace-c/src/test/htraced_rcv-unit.c new file mode 100644 index 0000000..29f62d1 --- /dev/null +++ b/htrace-c/src/test/htraced_rcv-unit.c @@ -0,0 +1,110 @@ +/** + * 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/conf.h" +#include "core/htrace.h" +#include "test/mini_htraced.h" +#include "test/rtest.h" +#include "test/span_table.h" +#include "test/span_util.h" +#include "test/temp_dir.h" +#include "test/test.h" +#include "util/log.h" +#include "util/time.h" + +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +static int htraced_rcv_test(struct rtest *rt) +{ + char err[512], *conf_str, *json_path; + size_t err_len = sizeof(err); + struct mini_htraced_params params; + struct mini_htraced *ht = NULL; + struct span_table *st; + uint64_t start_ms; + + params.name = rt->name; + params.confstr = ""; + mini_htraced_build(¶ms, &ht, err, err_len); + EXPECT_STR_EQ("", err); + + EXPECT_INT_GE(0, asprintf(&json_path, "%s/%s", + ht->root_dir, "spans.json")); + EXPECT_INT_GE(0, asprintf(&conf_str, "%s=%s;%s=%s", + HTRACE_SPAN_RECEIVER_KEY, "htraced", + HTRACED_ADDRESS_KEY, ht->htraced_http_addr)); + EXPECT_INT_ZERO(rt->run(rt, conf_str)); + start_ms = monotonic_now_ms(NULL); + // + // It may take a little while for htraced to commit the incoming spans sent + // via RPC to its data store. htraced does not have read-after-write + // consistency, in other words. This isn't normally an issue since trace + // collection is done in the background. + // + // For this unit test, it means that we want to retry if we find too few + // spans the first time we dump the htraced data store contents. + // + while (1) { + int nspans; + + // This uses the bin/htrace program to dump the spans to a json file. + mini_htraced_dump_spans(ht, err, err_len, json_path); + EXPECT_STR_EQ("", err); + st = span_table_alloc(); + EXPECT_NONNULL(st); + nspans = load_trace_span_file(json_path, st); + EXPECT_INT_GE(0, nspans); + if (nspans >= rt->spans_created) { + break; + } + span_table_free(st); + st = NULL; + EXPECT_UINT64_GE(start_ms, monotonic_now_ms(NULL) + 30000); + sleep_ms(100); + fprintf(stderr, "htraced_test_app1: retrying htrace dumpAll...\n"); + } + EXPECT_INT_ZERO(rt->verify(rt, st)); + free(conf_str); + free(json_path); + span_table_free(st); + mini_htraced_stop(ht); + mini_htraced_free(ht); + + return EXIT_SUCCESS; +} + +int main(void) +{ + int i; + + for (i = 0; g_rtests[i]; i++) { + struct rtest *rtest = g_rtests[i]; + if (htraced_rcv_test(rtest) != EXIT_SUCCESS) { + fprintf(stderr, "rtest %s failed\n", rtest->name); + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} + +// vim: ts=4:sw=4:tw=79:et http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/test/linkage-unit.c ---------------------------------------------------------------------- diff --git a/htrace-c/src/test/linkage-unit.c b/htrace-c/src/test/linkage-unit.c new file mode 100644 index 0000000..f3c5eba --- /dev/null +++ b/htrace-c/src/test/linkage-unit.c @@ -0,0 +1,105 @@ +/** + * 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/htrace.h" +#include "test/test_config.h" + +#include <dlfcn.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +/** + * @file linkage-unit.c + * + * Tests the linkage of libhtrace.so. + * Verifies that the functions in htrace.h are publicly visible, and that + * functions not included in that header are not. + * + * This unit test links against the production libhtrace.so, not the test + * library. This also verifies that we can run a program compiled against the + * production library without encountering unresolved symbols or similar. + */ + +static const char * const PUBLIC_SYMS[] = { + "htrace_conf_free", + "htrace_conf_from_str", + "htrace_restart_span", + "htrace_sampler_create", + "htrace_sampler_free", + "htrace_sampler_to_str", + "htrace_scope_close", + "htrace_scope_detach", + "htrace_scope_get_span_id", + "htrace_start_span", + "htracer_create", + "htracer_free", + "htracer_tname" +}; + +#define PUBLIC_SYMS_SIZE (sizeof(PUBLIC_SYMS) / sizeof(PUBLIC_SYMS[0])) + +/** + * Test that we can call the htrace_conf_from_strs function without aborting at + * runtime. This may seem like a trivial test, but keep in mind this is the + * only unit test that uses the real libhtrace.so, not the testing library. + */ +static int test_call_htrace_conf_from_strs(void) +{ + struct htrace_conf *cnf; + + cnf = htrace_conf_from_str("foo=bar"); + htrace_conf_free(cnf); + return EXIT_SUCCESS; +} + +/** + * Test that we can find all the public symbols in the library. + */ +static int find_public_symbols(void) +{ + int i; + void *sym; + + for (i = 0; i < PUBLIC_SYMS_SIZE; i++) { + sym = dlsym(RTLD_DEFAULT, PUBLIC_SYMS[i]); + if (!sym) { + fprintf(stderr, "Failed to find %s, or its value was NULL.\n", + PUBLIC_SYMS[i]); + return EXIT_FAILURE; + } + } + if (dlsym(RTLD_DEFAULT, "htrace_span_alloc")) { + fprintf(stderr, "Found non-public symbol htrace_span_alloc.\n"); + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} + +int main(void) +{ + if (test_call_htrace_conf_from_strs()) { + abort(); + } + if (find_public_symbols()) { + abort(); + } + return EXIT_SUCCESS; +} + +// vim: ts=4:sw=4:tw=79:et http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/test/local_file_rcv-unit.c ---------------------------------------------------------------------- diff --git a/htrace-c/src/test/local_file_rcv-unit.c b/htrace-c/src/test/local_file_rcv-unit.c new file mode 100644 index 0000000..8d518a9 --- /dev/null +++ b/htrace-c/src/test/local_file_rcv-unit.c @@ -0,0 +1,74 @@ +/** + * 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/conf.h" +#include "core/htrace.h" +#include "test/rtest.h" +#include "test/span_table.h" +#include "test/span_util.h" +#include "test/temp_dir.h" +#include "test/test.h" +#include "util/log.h" + +#include <inttypes.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static int local_file_rcv_test(struct rtest *rt) +{ + char err[512]; + size_t err_len = sizeof(err); + char *local_path, *tdir, *conf_str = NULL; + struct span_table *st; + + st = span_table_alloc(); + tdir = create_tempdir("local_file_rcv-unit", 0777, err, err_len); + EXPECT_STR_EQ("", err); + register_tempdir_for_cleanup(tdir); + EXPECT_INT_GE(0, asprintf(&local_path, "%s/%s", tdir, "spans.json")); + EXPECT_INT_GE(0, asprintf(&conf_str, "%s=%s;%s=%s", + HTRACE_SPAN_RECEIVER_KEY, "local.file", + HTRACE_LOCAL_FILE_RCV_PATH_KEY, local_path)); + EXPECT_INT_ZERO(rt->run(rt, conf_str)); + EXPECT_INT_GE(0, load_trace_span_file(local_path, st)); + EXPECT_INT_ZERO(rt->verify(rt, st)); + free(conf_str); + free(local_path); + free(tdir); + span_table_free(st); + + return EXIT_SUCCESS; +} + +int main(void) +{ + int i; + + for (i = 0; g_rtests[i]; i++) { + struct rtest *rtest = g_rtests[i]; + if (local_file_rcv_test(rtest) != EXIT_SUCCESS) { + fprintf(stderr, "rtest %s failed\n", rtest->name); + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} + +// vim: ts=4:sw=4:tw=79:et http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/test/log-unit.c ---------------------------------------------------------------------- diff --git a/htrace-c/src/test/log-unit.c b/htrace-c/src/test/log-unit.c new file mode 100644 index 0000000..af7de48 --- /dev/null +++ b/htrace-c/src/test/log-unit.c @@ -0,0 +1,97 @@ +/** + * 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/conf.h" +#include "test/temp_dir.h" +#include "test/test.h" +#include "util/log.h" + +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static int verify_log_file(const char *path) +{ + FILE *fp; + char contents[4096]; + size_t res; + const char * const expected_contents = + "foo 2, bar, and baz.\nquux as well.\n"; + + fp = fopen(path, "r"); + if (!fp) { + int e = errno; + fprintf(stderr, "failed to open %s: error %d (%s)\n", + path, e, terror(e)); + return EXIT_FAILURE; + } + memset(contents, 0, sizeof(contents)); + res = fread(contents, 1, sizeof(contents), fp); + if (res < strlen(expected_contents)) { + int e = errno; + if (feof(fp)) { + fprintf(stderr, "fread(%s): unexpected eof.\n", path); + return EXIT_FAILURE; + } + fprintf(stderr, "fread(%s): error %d (%s)\n", + path, e, terror(e)); + return EXIT_FAILURE; + } + fclose(fp); + EXPECT_STR_EQ(expected_contents, contents); + + return EXIT_SUCCESS; +} + +static int verify_log_to_file(void) +{ + struct htrace_conf *conf; + struct htrace_log *lg; + char *tdir, log_path[PATH_MAX], conf_str[PATH_MAX]; + char err[128]; + size_t err_len = sizeof(err); + + tdir = create_tempdir("verify_log_to_file", 0775, err, err_len); + EXPECT_NONNULL(tdir); + EXPECT_INT_ZERO(register_tempdir_for_cleanup(tdir)); + snprintf(log_path, sizeof(log_path), "%s/log.txt", tdir); + snprintf(conf_str, sizeof(conf_str), "log.path=%s", log_path); + conf = htrace_conf_from_strs(conf_str, ""); + EXPECT_NONNULL(conf); + lg = htrace_log_alloc(conf); + EXPECT_NONNULL(lg); + htrace_log(lg, "foo %d, bar, and baz.\n", 2); + htrace_log(lg, "quux as well.\n"); + htrace_log_free(lg); + EXPECT_INT_ZERO(verify_log_file(log_path)); + htrace_conf_free(conf); + free(tdir); + + return EXIT_SUCCESS; +} + +int main(void) +{ + EXPECT_INT_ZERO(verify_log_to_file()); + + return EXIT_SUCCESS; +} + +// vim: ts=4:sw=4:tw=79:et http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/test/mini_htraced-unit.c ---------------------------------------------------------------------- diff --git a/htrace-c/src/test/mini_htraced-unit.c b/htrace-c/src/test/mini_htraced-unit.c new file mode 100644 index 0000000..bfcd2ef --- /dev/null +++ b/htrace-c/src/test/mini_htraced-unit.c @@ -0,0 +1,48 @@ +/** + * 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 "test/mini_htraced.h" +#include "test/test.h" + +#include <stdlib.h> + +static int test_mini_htraced_start_stop(void) +{ + char err[128]; + size_t err_len = sizeof(err); + struct mini_htraced_params params; + struct mini_htraced *ht = NULL; + + err[0] = '\0'; + params.name = "test_mini_htraced_start_stop"; + params.confstr = ""; + mini_htraced_build(¶ms, &ht, err, err_len); + EXPECT_STR_EQ("", err); + mini_htraced_stop(ht); + mini_htraced_free(ht); + + return 0; +} + +int main(void) +{ + EXPECT_INT_ZERO(test_mini_htraced_start_stop()); + return EXIT_SUCCESS; +} + +// vim: ts=4:sw=4:tw=79:et
