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(&params, &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(&params, &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