You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@htrace.apache.org by cm...@apache.org on 2015/04/20 02:50:21 UTC
[3/4] incubator-htrace git commit: HTRACE-106: htrace: add C / C++
native client (cmccabe)
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