Repository: incubator-htrace
Updated Branches:
  refs/heads/master 20f00f9c2 -> a9d85254b


http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/test/test.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/test/test.c b/htrace-c/src/test/test.c
new file mode 100644
index 0000000..7a04006
--- /dev/null
+++ b/htrace-c/src/test/test.c
@@ -0,0 +1,158 @@
+/**
+ * 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/string.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+void fail(const char *fmt, ...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    vfprintf(stderr, fmt, ap);
+    va_end(ap);
+    exit(1);
+}
+
+static int vexpect_decode(const char *expected, const char *text, int ty,
+        const char *str)
+{
+    switch (ty) {
+    case TEST_ERROR_EQ:
+        if (strcmp(expected, str) != 0) {
+            fprintf(stderr, "error: expected '%s', but got '%s', which "
+                   "is not equal. %s\n", expected, str, text);
+            return -1;
+        }
+        break;
+    case TEST_ERROR_GE:
+        if (strcmp(expected, str) > 0) {
+            fprintf(stderr, "error: expected '%s', but got '%s'. "
+                   "Expected something greater or equal.  %s\n",
+                   expected, str, text);
+            return -1;
+        }
+        break;
+    case TEST_ERROR_GT:
+        if (strcmp(expected, str) >= 0) {
+            fprintf(stderr, "error: expected '%s', but got '%s'. "
+                   "Expected something greater.  %s\n",
+                   expected, str, text);
+            return -1;
+        }
+        break;
+    case TEST_ERROR_LE:
+        if (strcmp(expected, str) < 0) {
+            fprintf(stderr, "error: expected '%s', but got '%s'. "
+                   "Expected something less or equal.  %s\n",
+                   expected, str, text);
+            return -1;
+        }
+        break;
+    case TEST_ERROR_LT:
+        if (strcmp(expected, str) <= 0) {
+            fprintf(stderr, "error: expected '%s', but got '%s'. "
+                   "Expected something less.  %s\n",
+                   expected, str, text);
+            return -1;
+        }
+        break;
+    case TEST_ERROR_NE:
+        if (strcmp(expected, str) == 0) {
+            fprintf(stderr, "error: did not expect '%s': '%s'\n",
+                   expected, text);
+            return -1;
+        }
+        break;
+    default:
+        fprintf(stderr, "runtime error: invalid type %d passed in: '%s'\n",
+                ty, text);
+        return -1;
+    }
+    return 0;
+}
+
+int vexpect(const char *expected, const char *text, int ty,
+        const char *fmt, va_list ap)
+{
+    char *str = NULL;
+    int ret;
+
+    if (vasprintf(&str, fmt, ap) < 0) { // TODO: portability
+        fprintf(stderr, "error: vasprintf failed: %s\n", text);
+        return -1;
+    }
+    ret = vexpect_decode(expected, text, ty, str);
+    free(str);
+    return ret;
+}
+
+int expect(const char *expected, const char *text, int ty,
+        const char *fmt, ...)
+{
+    int ret;
+    va_list ap;
+
+    va_start(ap, fmt);
+    ret = vexpect(expected, text, ty, fmt, ap);
+    va_end(ap);
+    return ret;
+}
+
+void *xcalloc(size_t len)
+{
+    void *v = calloc(1, len);
+    if (!v) {
+        abort();
+    }
+    return v;
+}
+
+char *xstrdup(const char *in)
+{
+    char *out = strdup(in);
+    if (!out) {
+        abort();
+    }
+    return out;
+}
+
+void hexdump(void *in, int in_len, char *out, int out_len)
+{
+    int i;
+    uint8_t *b = in;
+    const char *prefix = "";
+
+    for (i = 0; i < in_len; i++) {
+        fwdprintf(&out, &out_len, "%s%02x", prefix, b[i]);
+        prefix = " ";
+    }
+}
+
+// vim: ts=4:sw=4:tw=79:et

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/test/test.h
----------------------------------------------------------------------
diff --git a/htrace-c/src/test/test.h b/htrace-c/src/test/test.h
new file mode 100644
index 0000000..3edd2bc
--- /dev/null
+++ b/htrace-c/src/test/test.h
@@ -0,0 +1,211 @@
+/**
+ * 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_TEST_TEST_H
+#define APACHE_HTRACE_TEST_TEST_H
+
+#include <inttypes.h> /* for PRIdPTR */
+#include <stdarg.h> /* for va_list */
+#include <unistd.h> /* for size_t */
+
+#define TEST_ERROR_EQ 0
+#define TEST_ERROR_GE 1
+#define TEST_ERROR_GT 2
+#define TEST_ERROR_LE 3
+#define TEST_ERROR_LT 4
+#define TEST_ERROR_NE 5
+
+/**
+ * Fail with an error message.
+ *
+ * @param fmt       printf-style format string.
+ * @param ...       printf-style arguments.
+ */
+void fail(const char *fmt, ...);
+
+/**
+ * Check for a test condition.
+ *
+ * @param expected  The string which is expected.
+ * @param text      Some text that will be printed out if the test
+ *                      condition fails.
+ * @param ty        Comparison type.  See TEST_ERROR_* constants.
+ * @param fmt       printf-style format string.
+ * @param ap        printf-style arguments.
+ *
+ * @return      0 on success; 1 on failure.
+ */
+int vexpect(const char *expected, const char *text, int ty,
+        const char *fmt, va_list ap);
+
+/**
+ * Check for a test condition.
+ *
+ * @param expected  The string which is expected.
+ * @param text      Some text that will be printed out if the test
+ *                      condition fails.
+ * @param ty        Comparison type.  See TEST_ERROR_* constants.
+ * @param fmt       printf-style format string.
+ * @param ...       printf-style arguments.
+ *
+ * @return          0 on success; 1 on failure.
+ */
+int expect(const char *expected, const char *text, int ty,
+        const char *fmt, ...) __attribute__((format(printf, 4, 5)));
+
+/**
+ * Allocate a zero-initialized region of memory, or die.
+ *
+ * @param len       The length
+ *
+ * @return          A pointer to a zero-initialized malloc'ed region.
+ */
+void *xcalloc(size_t len);
+
+/**
+ * Copy a string, or die.
+ *
+ * @param in        The string to copy
+ *
+ * @return          A dynamically allocated copy of the input string.
+ */
+char *xstrdup(const char *in);
+
+/**
+ * Dump a set of bytes to a buffer in hexadecimal form.
+ *
+ * @param in        The input.
+ * @param buf_len   Length of the input buffer.
+ * @param out       The buffer to dump to.
+ * @param out_len   Length of the buffer to dump to.  If the output buffer is
+ *                      not long enough, not all the output will be written.
+ */
+void hexdump(void *in, int in_len, char *buf, int buf_len);
+
+#define TEST_ERROR_GET_LINE_HELPER2(x) #x
+#define TEST_ERROR_GET_LINE_HELPER(x) TEST_ERROR_GET_LINE_HELPER2(x)
+#define TEST_ERROR_LOCATION_TEXT __FILE__ " at line " \
+    TEST_ERROR_GET_LINE_HELPER(__LINE__)
+
+#define EXPECT(...) do { if (expect(__VA_ARGS__)) return 1; } while (0);
+
+#define EXPECT_EQ(expected, fmt, ...) \
+    EXPECT(expected, TEST_ERROR_LOCATION_TEXT, TEST_ERROR_EQ, \
+           fmt, __VA_ARGS__);
+
+#define EXPECT_STR_EQ(expected, str) \
+    EXPECT_EQ(expected, "%s", str)
+
+#define EXPECT_GE(expected, fmt, ...) \
+    EXPECT(expected, TEST_ERROR_LOCATION_TEXT, TEST_ERROR_GE, \
+           fmt, __VA_ARGS__);
+
+#define EXPECT_GT(expected, fmt, ...) \
+    EXPECT(expected, TEST_ERROR_LOCATION_TEXT, TEST_ERROR_GT, \
+           fmt, __VA_ARGS__);
+
+#define EXPECT_LE(expected, fmt, ...) \
+    EXPECT(expected, TEST_ERROR_LOCATION_TEXT, TEST_ERROR_LE, \
+           fmt, __VA_ARGS__);
+
+#define EXPECT_LT(expected, fmt, ...) \
+    EXPECT(expected, TEST_ERROR_LOCATION_TEXT, TEST_ERROR_LT, \
+           fmt, __VA_ARGS__);
+
+#define EXPECT_NE(expected, fmt, ...) \
+    EXPECT(expected, TEST_ERROR_LOCATION_TEXT, TEST_ERROR_NE, \
+           fmt, __VA_ARGS__);
+
+#define COMMON_TEST__TO_STR(x) #x
+#define COMMON_TEST__TO_STR2(x) COMMON_TEST__TO_STR(x)
+
+#define EXPECT_INT_EQ(expected, x) do { \
+  char expected_buf[16] = { 0 }; \
+  snprintf(expected_buf, sizeof(expected_buf), "%d", expected); \
+  EXPECT(expected_buf, TEST_ERROR_LOCATION_TEXT, TEST_ERROR_EQ, "%d", x); \
+} while(0);
+
+#define EXPECT_INT_GE(expected, x) do { \
+  char expected_buf[16] = { 0 }; \
+  snprintf(expected_buf, sizeof(expected_buf), "%d", expected); \
+  EXPECT(expected_buf, TEST_ERROR_LOCATION_TEXT, TEST_ERROR_GE, "%d", x); \
+} while(0);
+
+#define EXPECT_INT_GT(expected, x) do { \
+  char expected_buf[16] = { 0 }; \
+  snprintf(expected_buf, sizeof(expected_buf), "%d", expected); \
+  EXPECT(expected_buf, TEST_ERROR_LOCATION_TEXT, TEST_ERROR_GT, "%d", x); \
+} while(0);
+
+#define EXPECT_UINT64_EQ(expected, x) do { \
+  char expected_buf[32] = { 0 }; \
+  snprintf(expected_buf, sizeof(expected_buf), "%" PRIu64, expected); \
+  EXPECT(expected_buf, TEST_ERROR_LOCATION_TEXT, TEST_ERROR_EQ, \
+         "%" PRIu64, x); \
+} while(0);
+
+#define EXPECT_UINT64_GE(expected, x) do { \
+  char expected_buf[32] = { 0 }; \
+  snprintf(expected_buf, sizeof(expected_buf), "%" PRIu64, expected); \
+  EXPECT(expected_buf, TEST_ERROR_LOCATION_TEXT, TEST_ERROR_GE, \
+         "%" PRIu64, x); \
+} while(0);
+
+#define EXPECT_UINT64_GT(expected, x) do { \
+  char expected_buf[32] = { 0 }; \
+  snprintf(expected_buf, sizeof(expected_buf), "%" PRIu64, expected); \
+  EXPECT(expected_buf, TEST_ERROR_LOCATION_TEXT, TEST_ERROR_GT, \
+         "%" PRIu64, x); \
+} while(0);
+
+#define EXPECT_INT64_EQ(expected, x) do { \
+  char expected_buf[32] = { 0 }; \
+  snprintf(expected_buf, sizeof(expected_buf), "%" PRId64, expected); \
+  EXPECT(expected_buf, TEST_ERROR_LOCATION_TEXT, TEST_ERROR_EQ, \
+         "%" PRId64, x); \
+} while(0);
+
+#define EXPECT_UINTPTR_EQ(expected, x) do { \
+  char expected_buf[32] = { 0 }; \
+  snprintf(expected_buf, sizeof(expected_buf), "%" PRIuPTR, expected); \
+  EXPECT(expected_buf, TEST_ERROR_LOCATION_TEXT, TEST_ERROR_EQ, \
+         "%" PRIuPTR, x); \
+} while(0);
+
+#define EXPECT_INT_ZERO(x) \
+    EXPECT("0", TEST_ERROR_LOCATION_TEXT, TEST_ERROR_EQ, \
+           "%d", x);
+
+#define EXPECT_TRUE(x) \
+    EXPECT("1", TEST_ERROR_LOCATION_TEXT, TEST_ERROR_EQ, \
+           "%d", (!!x));
+
+#define EXPECT_FALSE(x) \
+    EXPECT("0", TEST_ERROR_LOCATION_TEXT, TEST_ERROR_EQ, \
+           "%d", (!!x));
+
+#define EXPECT_NONNULL(x) \
+    EXPECT("0", TEST_ERROR_LOCATION_TEXT, TEST_ERROR_NE, \
+           "%"PRIuPTR, (uintptr_t)x);
+
+#define EXPECT_NULL(x) \
+    EXPECT("0", TEST_ERROR_LOCATION_TEXT, TEST_ERROR_EQ, \
+           "%"PRIuPTR, (uintptr_t)x);
+#endif
+
+// vim: ts=4:sw=4:tw=79:et

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/test/test_config.h.cmake
----------------------------------------------------------------------
diff --git a/htrace-c/src/test/test_config.h.cmake 
b/htrace-c/src/test/test_config.h.cmake
new file mode 100644
index 0000000..62b1744
--- /dev/null
+++ b/htrace-c/src/test/test_config.h.cmake
@@ -0,0 +1,28 @@
+/**
+* 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_TEST_TEST_CONFIG_H
+#define APACHE_HTRACE_TEST_TEST_CONFIG_H
+
+// The absolute path to the htrace binary, for use in unit tests.
+#cmakedefine HTRACE_ABSPATH "@HTRACE_ABSPATH@"
+
+// The absolute path to the htraced binary, for use in unit tests.
+#cmakedefine HTRACED_ABSPATH "@HTRACED_ABSPATH@"
+
+#endif

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/test/time-unit.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/test/time-unit.c b/htrace-c/src/test/time-unit.c
new file mode 100644
index 0000000..acdd14f
--- /dev/null
+++ b/htrace-c/src/test/time-unit.c
@@ -0,0 +1,84 @@
+/**
+ * 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 "util/time.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define TEST_STR1 "This is a test.  "
+#define TEST_STR1_LEN (sizeof(TEST_STR1) - 1)
+
+static struct htrace_conf *g_test_conf;
+
+static struct htrace_log *g_test_lg;
+
+static int test_timespec_conversions(void)
+{
+    uint64_t ms;
+    struct timespec ts, ts2;
+    ts.tv_sec = 1;
+    ts.tv_nsec = 999999999;
+    ms = timespec_to_ms(&ts);
+    EXPECT_UINT64_EQ((uint64_t)1999, ms);
+    ts2.tv_sec = 0;
+    ts2.tv_nsec = 0;
+    ms_to_timespec(ms, &ts2);
+    EXPECT_UINT64_EQ(1LU, ts2.tv_sec);
+    EXPECT_UINT64_EQ((uint64_t)999000000ULL, ts2.tv_nsec);
+    return EXIT_SUCCESS;
+}
+
+static int test_now_increases(int monotonic)
+{
+    int i;
+    uint64_t first = now_ms(g_test_lg);
+
+    for (i = 0; i < 10; i++) {
+        uint64_t next;
+        sleep_ms(2); // At least 2 ms.
+        if (monotonic) {
+            next = monotonic_now_ms(g_test_lg);
+        } else {
+            next = now_ms(g_test_lg);
+        }
+        EXPECT_UINT64_GT(first, next);
+    }
+    return EXIT_SUCCESS;
+}
+
+int main(void)
+{
+    g_test_conf = htrace_conf_from_strs("", "");
+    g_test_lg = htrace_log_alloc(g_test_conf);
+
+    test_timespec_conversions();
+
+    test_now_increases(0);
+    test_now_increases(1);
+
+    htrace_log_free(g_test_lg);
+    htrace_conf_free(g_test_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/util/build.h.cmake
----------------------------------------------------------------------
diff --git a/htrace-c/src/util/build.h.cmake b/htrace-c/src/util/build.h.cmake
new file mode 100644
index 0000000..ad04882
--- /dev/null
+++ b/htrace-c/src/util/build.h.cmake
@@ -0,0 +1,23 @@
+/**
+* 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_UTIL_BUILD_H
+#define APACHE_HTRACE_UTIL_BUILD_H
+
+#cmakedefine HAVE_IMPROVED_TLS
+
+#endif

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/util/htable.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/util/htable.c b/htrace-c/src/util/htable.c
new file mode 100644
index 0000000..d38462e
--- /dev/null
+++ b/htrace-c/src/util/htable.c
@@ -0,0 +1,290 @@
+/**
+ * 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 "util/htable.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/**
+ * @file htable.c
+ *
+ * Implements a hash table that uses probing.
+ */
+
+struct htable_pair {
+    void *key;
+    void *val;
+};
+
+/**
+ * A hash table which uses linear probing.
+ */
+struct htable {
+    uint32_t capacity;
+    uint32_t used;
+    htable_hash_fn_t hash_fun;
+    htable_eq_fn_t eq_fun;
+    struct htable_pair *elem;
+};
+
+/**
+ * An internal function for inserting a value into the hash table.
+ *
+ * Note: this function assumes that you have made enough space in the table.
+ *
+ * @param nelem         The new element to insert.
+ * @param capacity      The capacity of the hash table.
+ * @param hash_fun      The hash function to use.
+ * @param key           The key to insert.
+ * @param val           The value to insert.
+ */
+static void htable_insert_internal(struct htable_pair *nelem,
+        uint32_t capacity, htable_hash_fn_t hash_fun, void *key,
+        void *val)
+{
+    uint32_t i;
+
+    i = hash_fun(key, capacity);
+    while (1) {
+        if (!nelem[i].key) {
+            nelem[i].key = key;
+            nelem[i].val = val;
+            return;
+        }
+        i++;
+        if (i == capacity) {
+            i = 0;
+        }
+    }
+}
+
+static int htable_realloc(struct htable *htable, uint32_t new_capacity)
+{
+    struct htable_pair *nelem;
+    uint32_t i, old_capacity = htable->capacity;
+    htable_hash_fn_t hash_fun = htable->hash_fun;
+
+    nelem = calloc(new_capacity, sizeof(struct htable_pair));
+    if (!nelem) {
+        return ENOMEM;
+    }
+    for (i = 0; i < old_capacity; i++) {
+        struct htable_pair *pair = htable->elem + i;
+        if (pair->key) {
+            htable_insert_internal(nelem, new_capacity, hash_fun,
+                                   pair->key, pair->val);
+        }
+    }
+    free(htable->elem);
+    htable->elem = nelem;
+    htable->capacity = new_capacity;
+    return 0;
+}
+
+static uint32_t round_up_to_power_of_2(uint32_t i)
+{
+    i--;
+    i |= i >> 1;
+    i |= i >> 2;
+    i |= i >> 4;
+    i |= i >> 8;
+    i |= i >> 16;
+    i++;
+    return i;
+}
+
+struct htable *htable_alloc(uint32_t size,
+                htable_hash_fn_t hash_fun, htable_eq_fn_t eq_fun)
+{
+    struct htable *htable;
+
+    htable = calloc(1, sizeof(*htable));
+    if (!htable) {
+        return NULL;
+    }
+    size = round_up_to_power_of_2(size);
+    if (size < HTABLE_MIN_SIZE) {
+        size = HTABLE_MIN_SIZE;
+    }
+    htable->hash_fun = hash_fun;
+    htable->eq_fun = eq_fun;
+    htable->used = 0;
+    if (htable_realloc(htable, size)) {
+        free(htable);
+        return NULL;
+    }
+    return htable;
+}
+
+void htable_visit(struct htable *htable, visitor_fn_t fun, void *ctx)
+{
+    uint32_t i;
+
+    for (i = 0; i != htable->capacity; ++i) {
+        struct htable_pair *elem = htable->elem + i;
+        if (elem->key) {
+            fun(ctx, elem->key, elem->val);
+        }
+    }
+}
+
+void htable_free(struct htable *htable)
+{
+    if (htable) {
+        free(htable->elem);
+        free(htable);
+    }
+}
+
+int htable_put(struct htable *htable, void *key, void *val)
+{
+    int ret;
+    uint32_t nused;
+
+    // NULL is not a valid key value.
+    // This helps us implement htable_get_internal efficiently, since we know
+    // that we can stop when we encounter the first NULL key.
+    if (!key) {
+        return EINVAL;
+    }
+    // NULL is not a valid value.  Otherwise the results of htable_get would
+    // be confusing (does a NULL return mean entry not found, or that the
+    // entry was found and was NULL?)
+    if (!val) {
+        return EINVAL;
+    }
+    // Re-hash if we have used more than half of the hash table
+    nused = htable->used + 1;
+    if (nused >= (htable->capacity / 2)) {
+        ret = htable_realloc(htable, htable->capacity * 2);
+        if (ret)
+            return ret;
+    }
+    htable_insert_internal(htable->elem, htable->capacity,
+                                htable->hash_fun, key, val);
+    htable->used++;
+    return 0;
+}
+
+static int htable_get_internal(const struct htable *htable,
+                               const void *key, uint32_t *out)
+{
+    uint32_t start_idx, idx;
+
+    start_idx = htable->hash_fun(key, htable->capacity);
+    idx = start_idx;
+    while (1) {
+        struct htable_pair *pair = htable->elem + idx;
+        if (!pair->key) {
+            // We always maintain the invariant that the entries corresponding
+            // to a given key are stored in a contiguous block, not separated
+            // by any NULLs.  So if we encounter a NULL, our search is over.
+            return ENOENT;
+        } else if (htable->eq_fun(pair->key, key)) {
+            *out = idx;
+            return 0;
+        }
+        idx++;
+        if (idx == htable->capacity) {
+            idx = 0;
+        }
+        if (idx == start_idx) {
+            return ENOENT;
+        }
+    }
+}
+
+void *htable_get(const struct htable *htable, const void *key)
+{
+    uint32_t idx;
+
+    if (htable_get_internal(htable, key, &idx)) {
+        return NULL;
+    }
+    return htable->elem[idx].val;
+}
+
+void htable_pop(struct htable *htable, const void *key,
+                void **found_key, void **found_val)
+{
+    uint32_t hole, i;
+    const void *nkey;
+
+    if (htable_get_internal(htable, key, &hole)) {
+        *found_key = NULL;
+        *found_val = NULL;
+        return;
+    }
+    i = hole;
+    htable->used--;
+    // We need to maintain the compactness invariant used in
+    // htable_get_internal.  This invariant specifies that the entries for any
+    // given key are never separated by NULLs (although they may be separated
+    // by entries for other keys.)
+    while (1) {
+        i++;
+        if (i == htable->capacity) {
+            i = 0;
+        }
+        nkey = htable->elem[i].key;
+        if (!nkey) {
+            *found_key = htable->elem[hole].key;
+            *found_val = htable->elem[hole].val;
+            htable->elem[hole].key = NULL;
+            htable->elem[hole].val = NULL;
+            return;
+        } else if (htable->eq_fun(key, nkey)) {
+            htable->elem[hole].key = htable->elem[i].key;
+            htable->elem[hole].val = htable->elem[i].val;
+            hole = i;
+        }
+    }
+}
+
+uint32_t htable_used(const struct htable *htable)
+{
+    return htable->used;
+}
+
+uint32_t htable_capacity(const struct htable *htable)
+{
+    return htable->capacity;
+}
+
+uint32_t ht_hash_string(const void *str, uint32_t max)
+{
+    const char *s = str;
+    uint32_t hash = 0;
+
+    while (*s) {
+        hash = (hash * 31) + *s;
+        s++;
+    }
+    return hash % max;
+}
+
+int ht_compare_string(const void *a, const void *b)
+{
+    return strcmp(a, b) == 0;
+}
+
+// vim: ts=4:sw=4:tw=79:et

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/util/htable.h
----------------------------------------------------------------------
diff --git a/htrace-c/src/util/htable.h b/htrace-c/src/util/htable.h
new file mode 100644
index 0000000..9715efa
--- /dev/null
+++ b/htrace-c/src/util/htable.h
@@ -0,0 +1,169 @@
+/**
+ * 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_HTABLE_H
+#define APACHE_HTRACE_HTABLE_H
+
+/**
+ * @file htable.h
+ *
+ * Interfaces for a hash table that uses probing.
+ *
+ * This is an internal header, not intended for external use.
+ */
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdint.h>
+
+#define HTABLE_MIN_SIZE 4
+
+struct htable;
+
+/**
+ * An HTable hash function.
+ *
+ * @param key       The key.
+ * @param capacity  The total capacity.
+ *
+ * @return          The hash slot.  Must be less than the capacity.
+ */
+typedef uint32_t (*htable_hash_fn_t)(const void *key, uint32_t capacity);
+
+/**
+ * An HTable equality function.  Compares two keys.
+ *
+ * @param a         First key.
+ * @param b         Second key.
+ *
+ * @return          nonzero if the keys are equal.
+ */
+typedef int (*htable_eq_fn_t)(const void *a, const void *b);
+
+/**
+ * Allocate a new hash table.
+ *
+ * @param capacity  The minimum suggested starting capacity.
+ * @param hash_fun  The hash function to use in this hash table.
+ * @param eq_fun    The equals function to use in this hash table.
+ *
+ * @return          The new hash table on success; NULL on OOM.
+ */
+struct htable *htable_alloc(uint32_t capacity, htable_hash_fn_t hash_fun,
+                            htable_eq_fn_t eq_fun);
+
+typedef void (*visitor_fn_t)(void *ctx, void *key, void *val);
+
+/**
+ * Visit all of the entries in the hash table.
+ *
+ * @param htable    The hash table.
+ * @param fun       The callback function to invoke on each key and value.
+ * @param ctx       Context pointer to pass to the callback.
+ */
+void htable_visit(struct htable *htable, visitor_fn_t fun, void *ctx);
+
+/**
+ * Free the hash table.
+ *
+ * It is up the calling code to ensure that the keys and values inside the
+ * table are de-allocated, if that is necessary.
+ *
+ * @param htable    The hash table.
+ */
+void htable_free(struct htable *htable);
+
+/**
+ * Add an entry to the hash table.
+ *
+ * @param htable    The hash table.
+ * @param key       The key to add.  This cannot be NULL.
+ * @param fun       The value to add.  This cannot be NULL.
+ *
+ * @return          0 on success;
+ *                  EINVAL if we're trying to insert a NULL key or value.
+ *                  ENOMEM if there is not enough memory to add the element.
+ *                  EFBIG if the hash table has too many entries to fit in 32
+ *                      bits.
+ */
+int htable_put(struct htable *htable, void *key, void *val);
+
+/**
+ * Get an entry from the hash table.
+ *
+ * @param htable    The hash table.
+ * @param key       The key to find.
+ *
+ * @return          NULL if there is no such entry; the entry otherwise.
+ */
+void *htable_get(const struct htable *htable, const void *key);
+
+/**
+ * Get an entry from the hash table and remove it.
+ *
+ * @param htable    The hash table.
+ * @param key       The key for the entry find and remove.
+ * @param found_key (out param) NULL if the entry was not found; the found key
+ *                      otherwise.
+ * @param found_val (out param) NULL if the entry was not found; the found
+ *                      value otherwise.
+ */
+void htable_pop(struct htable *htable, const void *key,
+                void **found_key, void **found_val);
+
+/**
+ * Get the number of entries used in the hash table.
+ *
+ * @param htable    The hash table.
+ *
+ * @return          The number of entries used in the hash table.
+ */
+uint32_t htable_used(const struct htable *htable);
+
+/**
+ * Get the capacity of the hash table.
+ *
+ * @param htable    The hash table.
+ *
+ * @return          The capacity of the hash table.
+ */
+uint32_t htable_capacity(const struct htable *htable);
+
+/**
+ * Hash a string.
+ *
+ * @param str       The string.
+ * @param max       Maximum hash value
+ *
+ * @return          A number less than max.
+ */
+uint32_t ht_hash_string(const void *str, uint32_t max);
+
+/**
+ * Compare two strings.
+ *
+ * @param a         The first string.
+ * @param b         The second string.
+ *
+ * @return          1 if the strings are identical; 0 otherwise.
+ */
+int ht_compare_string(const void *a, const void *b);
+
+#endif
+
+// vim: ts=4:sw=4:tw=79:et

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/util/log.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/util/log.c b/htrace-c/src/util/log.c
new file mode 100644
index 0000000..64e99de
--- /dev/null
+++ b/htrace-c/src/util/log.c
@@ -0,0 +1,104 @@
+/**
+ * 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 "util/log.h"
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+struct htrace_log {
+    /**
+     * The lock which protects this log from concurrent writes.
+     */
+    pthread_mutex_t lock;
+
+    /**
+     * The log file.
+     */
+    FILE *fp;
+
+    /**
+     * Nonzero if we should close this file when closing the log.
+     */
+    int should_close;
+};
+
+struct htrace_log *htrace_log_alloc(const struct htrace_conf *conf)
+{
+    struct htrace_log *lg;
+    const char *path;
+
+    lg = calloc(1, sizeof(*lg));
+    if (!lg) {
+        fprintf(stderr, "htrace_log_alloc: out of memory.\n");
+        return NULL;
+    }
+    path = htrace_conf_get(conf, HTRACE_LOG_PATH_KEY);
+    if (!path) {
+        lg->fp = stderr;
+        return lg;
+    }
+    lg->fp = fopen(path, "a");
+    if (!lg->fp) {
+        int err = errno;
+        fprintf(stderr, "htrace_log_alloc: failed to open %s for "
+                "append: %d (%s).\n",
+                path, err, terror(err));
+        lg->fp = stderr;
+        return lg;
+    }
+    // If we're logging to a file, we need to close the file when we close the
+    // log.
+    lg->should_close = 1;
+    return lg;
+}
+
+void htrace_log_free(struct htrace_log *lg)
+{
+    if (!lg) {
+        return;
+    }
+    pthread_mutex_destroy(&lg->lock);
+    if (lg->should_close) {
+        fclose(lg->fp);
+    }
+    free(lg);
+}
+
+static void htrace_logv(struct htrace_log *lg, const char *fmt, va_list ap)
+{
+    pthread_mutex_lock(&lg->lock);
+    vfprintf(lg->fp, fmt, ap);
+    pthread_mutex_unlock(&lg->lock);
+}
+
+void htrace_log(struct htrace_log *lg, const char *fmt, ...)
+{
+    va_list ap;
+    va_start(ap, fmt);
+    htrace_logv(lg, fmt, ap);
+    va_end(ap);
+}
+
+// vim: ts=4:sw=4:et

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/util/log.h
----------------------------------------------------------------------
diff --git a/htrace-c/src/util/log.h b/htrace-c/src/util/log.h
new file mode 100644
index 0000000..52f8ed2
--- /dev/null
+++ b/htrace-c/src/util/log.h
@@ -0,0 +1,69 @@
+/**
+ * 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_UTIL_LOG_H
+#define APACHE_HTRACE_UTIL_LOG_H
+
+/**
+ * @file log.h
+ *
+ * Functions for HTrace logging.
+ *
+ * This is an internal header, not intended for external use.
+ */
+
+struct htrace_conf;
+
+/**
+ * Allocate a new htrace_log.
+ *
+ * @param conf          The configuration.  Will be deep copied.
+ *
+ * @return              The htrace_log, or NULL on OOM.
+ */
+struct htrace_log *htrace_log_alloc(const struct htrace_conf *conf);
+
+/**
+ * A thread-safe version of the strerror call.
+ *
+ * @param err           The POSIX error code.
+ *
+ * @return              A static or thread-local error string.
+ */
+const char *terror(int err);
+
+/**
+ * Free an htrace_log.
+ *
+ * @param lg            The log to be freed.
+ */
+void htrace_log_free(struct htrace_log *lg);
+
+/**
+ * Create an htrace log message.
+ *
+ * @param lg            The log to use.
+ * @param fmt           The format string to use.
+ * @param ...           Printf-style variable length arguments.
+ */
+void htrace_log(struct htrace_log *lg, const char *fmt, ...)
+      __attribute__((format(printf, 2, 3)));
+
+#endif
+
+// vim: ts=4:sw=4:et

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/util/process_id.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/util/process_id.c b/htrace-c/src/util/process_id.c
new file mode 100644
index 0000000..c11b693
--- /dev/null
+++ b/htrace-c/src/util/process_id.c
@@ -0,0 +1,304 @@
+/**
+ * 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 "util/log.h"
+#include "util/process_id.h"
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <ifaddrs.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+/**
+ * @file process_id.c
+ *
+ * Implements process IDs for the HTrace C client.
+ */
+
+/**
+ * The maximum number of bytes that can be in a process ID substitution
+ * variable.
+ */
+#define MAX_VAR_NAME 32
+
+enum ip_addr_type {
+    ADDR_TYPE_IPV6_LOOPBACK = 0,
+    ADDR_TYPE_IPV4_LOOPBACK,
+    ADDR_TYPE_IPV6_OTHER,
+    ADDR_TYPE_IPV4_OTHER,
+    ADDR_TYPE_IPV6_SITE_LOCAL,
+    ADDR_TYPE_IPV4_SITE_LOCAL
+};
+
+static int handle_process_subst_var(struct htrace_log *lg, char **out,
+                                    const char *var, const char *tname);
+
+enum ip_addr_type get_ipv4_addr_type(const struct sockaddr_in *ip);
+
+enum ip_addr_type get_ipv6_addr_type(const struct sockaddr_in6 *ip);
+
+static int append_char(char **out, int *j, char c)
+{
+    char *nout = realloc(*out, *j + 2); // leave space for NULL
+    if (!nout) {
+        return 0;
+    }
+    *out = nout;
+    (*out)[*j] = c;
+    *j = *j + 1;
+    (*out)[*j] = '\0';
+    return 1;
+}
+
+char *calculate_process_id(struct htrace_log *lg, const char *fmt,
+                           const char *tname)
+{
+    int i = 0, j = 0, escaping = 0, v = 0;
+    char *out = NULL, *var = NULL;
+
+    out = strdup("");
+    if (!out) {
+        goto oom;
+    }
+    while (1) {
+        char c = fmt[i++];
+        if (c == '\0') {
+            break;
+        } else if (c == '\\') {
+            if (!escaping) {
+                escaping = 1;
+                continue;
+            }
+        }
+        switch (v) {
+        case 0:
+            if (c == '%') {
+                if (!escaping) {
+                    if (!append_char(&var, &v, '%')) {
+                        goto oom;
+                    }
+                    continue;
+                }
+            }
+            break;
+        case 1:
+            if (c == '{') {
+                if (!escaping) {
+                    if (!append_char(&var, &v, '{')) {
+                        goto oom;
+                    }
+                    continue;
+                }
+            }
+            if (!append_char(&out, &j, '%')) {
+                goto oom;
+            }
+            break;
+        default:
+            if (c == '}') {
+                if (!escaping) {
+                    if (!append_char(&var, &v, '}')) {
+                        goto oom;
+                    }
+                    var[v++] = '\0';
+                    if (!handle_process_subst_var(lg, &out, var, tname)) {
+                        goto oom;
+                    }
+                    free(var);
+                    var = NULL;
+                    j = strlen(out);
+                    v = 0;
+                    continue;
+                }
+            }
+            escaping = 0;
+            if (!append_char(&var, &v, c)) {
+                goto oom;
+            }
+            continue;
+        }
+        escaping = 0;
+        v = 0;
+        if (!append_char(&out, &j, c)) {
+            goto oom;
+        }
+    }
+    out[j] = '\0';
+    if (v > 0) {
+      htrace_log(lg, "calculate_process_id(%s): unterminated process ID "
+                 "substitution variable at the end of the format string.",
+                 fmt);
+    }
+    free(var);
+    return out;
+
+oom:
+    htrace_log(lg, "calculate_process_id(tname=%s): OOM\n", tname);
+    free(out);
+    free(var);
+    return NULL;
+}
+
+static int handle_process_subst_var(struct htrace_log *lg, char **out,
+                                    const char *var, const char *tname)
+{
+    char *nout = NULL;
+
+    if (strcmp(var, "%{tname}") == 0) {
+        if (asprintf(&nout, "%s%s", *out, tname) < 0) {
+            htrace_log(lg, "handle_process_subst_var(var=%s): OOM", var);
+            return 0;
+        }
+        free(*out);
+        *out = nout;
+    } else if (strcmp(var, "%{ip}") == 0) {
+        char ip_str[256];
+        get_best_ip(lg, ip_str, sizeof(ip_str));
+        if (asprintf(&nout, "%s%s", *out, ip_str) < 0) {
+            htrace_log(lg, "handle_process_subst_var(var=%s): OOM", var);
+            return 0;
+        }
+        free(*out);
+        *out = nout;
+    } else if (strcmp(var, "%{pid}") == 0) {
+        char pid_str[64];
+        pid_t pid = getpid();
+
+        snprintf(pid_str, sizeof(pid_str), "%lld", (long long)pid);
+        if (asprintf(&nout, "%s%s", *out, pid_str) < 0) {
+            htrace_log(lg, "handle_process_subst_var(var=%s): OOM", var);
+            return 0;
+        }
+        free(*out);
+        *out = nout;
+    } else {
+        htrace_log(lg, "handle_process_subst_var(var=%s): unknown process "
+                   "ID substitution variable.\n", var);
+    }
+    return 1;
+}
+
+/**
+ * Get the "best" IP address for this node.
+ *
+ * This is complicated since nodes can have multiple network interfaces,
+ * and each network interface can have multiple IP addresses.  What we're
+ * looking for here is an IP address that will serve to identify this node
+ * to HTrace.  So we prefer site-local addresess (i.e. private ones on the
+ * LAN) to publicly routable interfaces.  If there are multiple addresses
+ * to choose from, we select the one which comes first in textual sort
+ * order.  This should ensure that we at least consistently call each node
+ * by a single name.
+ */
+void get_best_ip(struct htrace_log *lg, char *ip_str, size_t ip_str_len)
+{
+    struct ifaddrs *head, *ifa;
+    enum ip_addr_type ty = ADDR_TYPE_IPV4_LOOPBACK, nty;
+    char temp_ip_str[128];
+
+    snprintf(ip_str, ip_str_len, "%s", "127.0.0.1");
+    if (getifaddrs(&head) < 0) {
+        int res = errno;
+        htrace_log(lg, "get_best_ip: getifaddrs failed: %s\n", terror(res));
+        return;
+    }
+    for (ifa = head; ifa; ifa = ifa->ifa_next){
+        if (!ifa->ifa_addr) {
+            continue;
+        }
+        if (ifa->ifa_addr->sa_family == AF_INET) {
+            struct sockaddr_in *addr =
+                (struct sockaddr_in *)ifa->ifa_addr;
+            nty = get_ipv4_addr_type(addr);
+            if (nty < ty) {
+                continue;
+            }
+            if (!inet_ntop(AF_INET, &addr->sin_addr, temp_ip_str,
+                           sizeof(temp_ip_str))) {
+                htrace_log(lg, "get_best_ip_impl: inet_ntop(%s, AF_INET) "
+                           "failed\n", ifa->ifa_name);
+                continue;
+            }
+            if ((nty == ty) && (strcmp(temp_ip_str, ip_str) > 0)) {
+                continue;
+            }
+            snprintf(ip_str, ip_str_len, "%s", temp_ip_str);
+            ty = nty;
+        } else if (ifa->ifa_addr->sa_family == AF_INET6) {
+            struct sockaddr_in6 *addr =
+                (struct sockaddr_in6 *)ifa->ifa_addr;
+            nty = get_ipv6_addr_type(addr);
+            if (nty < ty) {
+                continue;
+            }
+            if (!inet_ntop(AF_INET6, &addr->sin6_addr, temp_ip_str,
+                           sizeof(temp_ip_str))) {
+                htrace_log(lg, "get_best_ip_impl: inet_ntop(%s, AF_INET6) "
+                           "failed\n", ifa->ifa_name);
+                continue;
+            }
+            if ((nty == ty) && (strcmp(temp_ip_str, ip_str) > 0)) {
+                continue;
+            }
+            snprintf(ip_str, ip_str_len, "%s", temp_ip_str);
+            ty = nty;
+        }
+    }
+    freeifaddrs(head);
+}
+
+enum ip_addr_type get_ipv4_addr_type(const struct sockaddr_in *ip)
+{
+    union {
+        uint8_t b[4];
+        uint32_t addr;
+    } addr;
+    addr.addr = ip->sin_addr.s_addr; // always big-endian
+    if (addr.b[0] == 127) { // 127.0.0.0/24
+        return ADDR_TYPE_IPV4_LOOPBACK;
+    }
+    if ((addr.b[0] == 10) && (addr.b[1] == 0) && (addr.b[2] == 0)) {
+        return ADDR_TYPE_IPV4_SITE_LOCAL; // 10.0.0.0/8
+    }
+    if ((addr.b[0] == 192) && (addr.b[1] == 168)) {
+        return ADDR_TYPE_IPV4_SITE_LOCAL; // 192.168.0.0/16
+    }
+    if ((addr.b[0] == 172) && (addr.b[1] == 16) && ((addr.b[2] & 0xf0) == 0)) {
+        return ADDR_TYPE_IPV4_SITE_LOCAL; // 172.16.0.0/12
+    }
+    return ADDR_TYPE_IPV4_OTHER;
+}
+
+enum ip_addr_type get_ipv6_addr_type(const struct sockaddr_in6 *ip)
+{
+    if (IN6_IS_ADDR_LOOPBACK(ip)) {
+        return ADDR_TYPE_IPV6_LOOPBACK;
+    } else if (IN6_IS_ADDR_SITELOCAL(ip)) {
+        return ADDR_TYPE_IPV6_SITE_LOCAL;
+    } else {
+        return ADDR_TYPE_IPV6_OTHER;
+    }
+}
+
+// vim:ts=4:sw=4:et

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/util/process_id.h
----------------------------------------------------------------------
diff --git a/htrace-c/src/util/process_id.h b/htrace-c/src/util/process_id.h
new file mode 100644
index 0000000..ff702ce
--- /dev/null
+++ b/htrace-c/src/util/process_id.h
@@ -0,0 +1,58 @@
+/**
+ * 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_UTIL_PROCESS_ID_H
+#define APACHE_HTRACE_UTIL_PROCESS_ID_H
+
+#include <unistd.h> /* for size_t */
+
+/**
+ * @file process_id.h
+ *
+ * Implements process IDs for the HTrace C client.
+ *
+ * This is an internal header, not intended for external use.
+ */
+
+struct htrace_log;
+
+/**
+ * Calculate the process ID.
+ *
+ * @param lg                A log object which will be used to report warnings.
+ * @param fmt               The user-provided string to use when calculating 
the
+ *                              process ID.
+ * @param tname             The name supplied when creating the htracer.
+ *
+ * @return                  NULL on OOM; the process ID otherwise.
+ */
+char *calculate_process_id(struct htrace_log *lg, const char *fmt,
+                           const char *tname);
+
+/**
+ * Get the best IP address representing this host.
+ *
+ * @param lg                A log object which will be used to report warnings.
+ * @param ip_str            (out param) output string
+ * @param ip_str_len        Length of output string
+ */
+void get_best_ip(struct htrace_log *lg, char *ip_str, size_t ip_str_len);
+
+#endif
+
+// vim: ts=4: sw=4: et

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/util/rand.h
----------------------------------------------------------------------
diff --git a/htrace-c/src/util/rand.h b/htrace-c/src/util/rand.h
new file mode 100644
index 0000000..3b55006
--- /dev/null
+++ b/htrace-c/src/util/rand.h
@@ -0,0 +1,63 @@
+/**
+ * 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_UTIL_RAND_H
+#define APACHE_HTRACE_UTIL_RAND_H
+
+/**
+ * @file rand.h
+ *
+ * Functions for providing thread-safe random numbers.
+ *
+ * This is an internal header, not intended for external use.
+ */
+
+#include <stdint.h>
+
+struct htrace_log;
+struct random_src;
+
+/**
+ * Creates a random source.
+ *
+ * @param log     The htrace_log object to use.  We may hold on to a reference
+ *                    to this log.
+ * @return        NULL on OOM; the random source otherwise.
+ */
+struct random_src *random_src_alloc(struct htrace_log *lg);
+
+/**
+ * Frees a random source.
+ *
+ * @param rnd     The random source.
+ */
+void random_src_free(struct random_src *rnd);
+
+/**
+ * Gets a random 32-bit number from the random source.
+ */
+uint32_t random_u32(struct random_src *rnd);
+
+/**
+ * Gets a random 64-bit number from the random source.
+ */
+uint64_t random_u64(struct random_src *rnd);
+
+#endif
+
+// vim: ts=4:sw=4:et

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/util/rand_linux.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/util/rand_linux.c b/htrace-c/src/util/rand_linux.c
new file mode 100644
index 0000000..960a1f6
--- /dev/null
+++ b/htrace-c/src/util/rand_linux.c
@@ -0,0 +1,143 @@
+/**
+ * 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 "util/log.h"
+#include "util/rand.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#define URANDOM_PATH "/dev/urandom"
+
+#define PSAMP_THREAD_LOCAL_BUF_LEN 256
+
+/**
+ * @file rand_linux.c
+ *
+ * A Linux implementation of a random number source.  This implementation reads
+ * random numbers from /dev/urandom.  To avoid reading from /dev/urandom too
+ * often, we have a thread-local cache of random data.  This is done using ELF
+ * TLS.
+ */
+
+struct random_src {
+    /**
+     * The HTrace log.
+     */
+    struct htrace_log *lg;
+
+    /**
+     * File descriptor for /dev/urandom.
+     */
+    int urandom_fd;
+};
+
+/**
+ * A thread-local cache of random bits.
+ */
+static __thread uint32_t g_rnd_cache[PSAMP_THREAD_LOCAL_BUF_LEN];
+
+/**
+ * An index into our thread-local cache of random bits.
+ */
+static __thread int g_rnd_cache_idx = PSAMP_THREAD_LOCAL_BUF_LEN;
+
+static void refill_rand_cache(struct random_src *rnd)
+{
+    size_t total = 0;
+
+    while (1) {
+        ssize_t res;
+        ssize_t rem = (PSAMP_THREAD_LOCAL_BUF_LEN * sizeof(uint32_t)) - total;
+        if (rem == 0) {
+            break;
+        }
+        res = read(rnd->urandom_fd, ((uint8_t*)&g_rnd_cache) + total, rem);
+        if (res < 0) {
+            int err = errno;
+            if (err == EINTR) {
+                continue;
+            }
+            htrace_log(rnd->lg, "refill_rand_cache: error refilling "
+                       "random cache: %d (%s)\n", err,
+                       terror(err));
+            return;
+        }
+        total += res;
+    }
+    g_rnd_cache_idx = 0;
+}
+
+struct random_src *random_src_alloc(struct htrace_log *lg)
+{
+    struct random_src *rnd;
+    int err;
+
+    rnd = calloc(1, sizeof(*rnd));
+    if (!rnd) {
+        htrace_log(lg, "random_src_alloc: OOM\n");
+        return NULL;
+    }
+    rnd->urandom_fd = open(URANDOM_PATH, O_RDONLY);
+    if (rnd->urandom_fd < 0) {
+        err = errno;
+        htrace_log(lg, "random_src_alloc: failed to open "
+                   URANDOM_PATH ": error %d (%s)\n", err,
+                   terror(err));
+        free(rnd);
+        return NULL;
+    }
+    rnd->lg = lg;
+    return rnd;
+}
+
+void random_src_free(struct random_src *rnd)
+{
+    if (!rnd) {
+        return;
+    }
+    if (close(rnd->urandom_fd)) {
+        int err = errno;
+        htrace_log(rnd->lg, "linux_prob_sampler_free: close error: "
+                   "%d (%s)\n", err, terror(err));
+    }
+    free(rnd);
+}
+
+uint32_t random_u32(struct random_src *rnd)
+{
+    if (g_rnd_cache_idx >= PSAMP_THREAD_LOCAL_BUF_LEN) {
+        refill_rand_cache(rnd);
+    }
+    return g_rnd_cache[g_rnd_cache_idx++];
+}
+
+uint64_t random_u64(struct random_src *rnd)
+{
+    uint64_t val = random_u32(rnd);
+    val <<= 32;
+    return val | random_u32(rnd);
+}
+
+// vim: ts=4:sw=4:tw=79:et

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/util/rand_posix.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/util/rand_posix.c b/htrace-c/src/util/rand_posix.c
new file mode 100644
index 0000000..5b2c0e7
--- /dev/null
+++ b/htrace-c/src/util/rand_posix.c
@@ -0,0 +1,120 @@
+/**
+ * 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 "util/log.h"
+#include "util/rand.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/**
+ * @file rand_posix.c
+ *
+ * A POSIX implementation of random number source.  We have to use a mutex here
+ * to protect the rand_r() state.  If we used rand() instead, we would also
+ * need to use a mutex.  The standard POSIX functions for getting random
+ * numbers are actually really unfortunate in many ways.  Hopefully we can
+ * provide platform-specific implementatinos of the probability sampler for all
+ * the major platforms.
+ */
+
+/**
+ * A sampler that fires with a certain probability.
+ */
+struct random_src {
+    /**
+     * Mutex protecting rand_r.
+     */
+    pthread_mutex_t lock;
+
+    /**
+     * State used with rand_r.
+     */
+    unsigned int rand_state;
+};
+
+struct random_src *random_src_alloc(struct htrace_log *lg)
+{
+    struct random_src *rnd;
+    int ret;
+
+    rnd = calloc(1, sizeof(*rnd));
+    if (!rnd) {
+        htrace_log(lg, "random_src_alloc: OOM\n");
+        return NULL;
+    }
+    ret = pthread_mutex_init(&rnd->lock, NULL);
+    if (ret) {
+        htrace_log(lg, "random_src_alloc: pthread_mutex_create "
+                   "failed: error %d (%s)\n", ret, terror(ret));
+        free(rnd);
+        return NULL;
+    }
+    return rnd;
+}
+
+void random_src_free(struct random_src *rnd)
+{
+    if (!rnd) {
+        return;
+    }
+    pthread_mutex_destroy(&rnd->lock);
+    free(rnd);
+}
+
+uint32_t random_u32(struct random_src *rnd)
+{
+    uint32_t val = 0;
+    pthread_mutex_lock(&rnd->lock);
+    // rand_r gives at least 15 bits of randomness.
+    // So we need to xor it 3 times to get 32 bits' worth.
+    val ^= rand_r(&rnd->rand_state);
+    val <<= 15;
+    val ^= rand_r(&rnd->rand_state);
+    val <<= 15;
+    val ^= rand_r(&rnd->rand_state);
+    val <<= 15;
+    pthread_mutex_unlock(&rnd->lock);
+    return val;
+}
+
+uint64_t random_u64(struct random_src *rnd)
+{
+    uint64_t val = 0;
+    pthread_mutex_lock(&rnd->lock);
+    // rand_r gives at least 15 bits of randomness.
+    // So we need to xor it 5 times to get 64 bits' worth.
+    val ^= rand_r(&rnd->rand_state);
+    val <<= 15;
+    val ^= rand_r(&rnd->rand_state);
+    val <<= 15;
+    val ^= rand_r(&rnd->rand_state);
+    val <<= 15;
+    val ^= rand_r(&rnd->rand_state);
+    val <<= 15;
+    val ^= rand_r(&rnd->rand_state);
+    val <<= 15;
+    pthread_mutex_unlock(&rnd->lock);
+    return val;
+}
+
+// vim: ts=4:sw=4:tw=79:et

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/util/string.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/util/string.c b/htrace-c/src/util/string.c
new file mode 100644
index 0000000..e9a4e91
--- /dev/null
+++ b/htrace-c/src/util/string.c
@@ -0,0 +1,107 @@
+/**
+ * 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 "util/log.h"
+#include "util/string.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+int fwdprintf(char **buf, int* rem, const char *fmt, ...)
+{
+    int amt, res;
+    char *b;
+    va_list ap;
+
+    if (!buf) {
+        char tmp[1] = { 0 };
+        va_start(ap, fmt);
+        res = vsnprintf(tmp, sizeof(tmp), fmt, ap);
+        va_end(ap);
+        if (res < 0) {
+            res = 0;
+        }
+        return res;
+    }
+    b = *buf;
+    va_start(ap, fmt);
+    amt = *rem;
+    res = vsnprintf(b, amt, fmt, ap);
+    va_end(ap);
+    if (res < 0) {
+        res = 0;
+    } else {
+        int sub = (amt < res) ? amt : res;
+        *rem = amt - sub;
+        *buf = b + sub;
+    }
+    return res;
+}
+
+int validate_json_string(struct htrace_log *lg, const char *str)
+{
+    const unsigned char *b = (const unsigned char *)str;
+    int off = 0;
+
+    while(*b) {
+        // Note: we don't allow newline (0x0a), tab (0x09), or carriage return
+        // (0x0d) because they cause problems down the line.
+        if (((0x20 <= b[0] && b[0] <= 0x7E)) &&
+                ((b[0] != '"') && (b[0] != '\\'))) {
+            b++;
+            off++;
+            continue;
+        }
+        if((0xC2 <= b[0] && b[0] <= 0xDF) && (0x80 <= b[1] && b[1] <= 0xBF)) {
+            b += 2; // 2-byte UTF-8, U+0080 to U+07FF
+            off += 2;
+            continue;
+        }
+        if ((b[0] == 0xe0 &&
+                    (0xa0 <= b[1] && b[1] <= 0xbf) &&
+                    (0x80 <= b[2] && b[2] <= 0xbf)
+                ) || (
+                    ((0xe1 <= b[0] && b[0] <= 0xec) ||
+                        b[0] == 0xee ||
+                        b[0] == 0xef) &&
+                    (0x80 <= b[1] && b[1] <= 0xbf) &&
+                    (0x80 <= b[2] && b[2] <= 0xbf)
+                ) || (
+                    b[0] == 0xed &&
+                    (0x80 <= b[1] && b[1] <= 0x9f) &&
+                    (0x80 <= b[2] && b[2] <= 0xbf)
+                )) {
+            b += 3; // 3-byte UTF-8, U+0800 U+FFFF
+            off += 3;
+            continue;
+        }
+        // Note: we don't allow code points outside the basic multilingual 
plane
+        // (BMP) at the moment.  The problem with them is that Javascript
+        // doesn't support them directly (they have to be encoded with UCS-2
+        // surrogate pairs).  TODO: teach htraced to do that encoding.
+        if (lg) {
+            htrace_log(lg, "validate_json_string(%s): byte %d (0x%02x) "
+                       "was problematic.\n", str, off, b[0]);
+        }
+        return 0;
+    }
+    return 1;
+}
+
+// vim: ts=4:sw=4:et

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/util/string.h
----------------------------------------------------------------------
diff --git a/htrace-c/src/util/string.h b/htrace-c/src/util/string.h
new file mode 100644
index 0000000..77e764b
--- /dev/null
+++ b/htrace-c/src/util/string.h
@@ -0,0 +1,64 @@
+/**
+ * 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_UTIL_STRING_H
+#define APACHE_HTRACE_UTIL_STRING_H
+
+/**
+ * @file string.h
+ *
+ * Functions for manipulating strings.
+ *
+ * This is an internal header, not intended for external use.
+ */
+
+struct htrace_log;
+
+/**
+ * Print to a buffer and move the pointer forward by the number of bytes
+ * written.
+ *
+ * @param buf           (inout) The buffer to write to.  We will advance this
+ *                          buffer by the number of bytes written.
+ *                          If this buffer is NULL, nothing will be written.
+ * @param rem           (inout) The maximum number of bytes to write to the
+ *                          buffer.  If bytes are written to the buffer, this
+ *                          number will be decremented by that amount.
+ * @param fmt           Printf-style format string.
+ *
+ * @return              The number of bytes that would have been used if bytes
+ *                          had been written
+ */
+int fwdprintf(char **buf, int* rem, const char *fmt, ...)
+      __attribute__((format(printf, 3, 4)));
+
+/**
+ * Validate that a string could appear in a JSON expression without causing
+ * problems.  We don't accept control characters, double quotes, backslashes,
+ * tabs, newlines, or carriage returns.
+ *
+ * @param lg            The log to print messages about invalid strings to.
+ * @param str           The string.
+ *
+ * @return              0 if the string is problematic; 1 if it's safe.
+ */
+int validate_json_string(struct htrace_log *lg, const char *str);
+
+#endif
+
+// vim: ts=4:sw=4:et

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/util/terror.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/util/terror.c b/htrace-c/src/util/terror.c
new file mode 100644
index 0000000..6db1fd8
--- /dev/null
+++ b/htrace-c/src/util/terror.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.
+ */
+
+// Get the POSIX definition of strerror_r.
+#define _POSIX_C_SOURCE 200112L
+#undef _GNU_SOURCE
+
+#include "util/build.h"
+#include "util/log.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+
+/**
+ * @file terror.c
+ *
+ * Implements the thread-safe terror() function.
+ *
+ * glibc makes it difficult to get access to the POSIX definition of 
strerror_r.
+ * Keeping this in a separate file allows us to put the proper macro magic at
+ * the top of just this file.
+ */
+
+#ifdef HAVE_IMPROVED_TLS
+const char *terror(int err)
+{
+    static __thread char buf[4096];
+    int ret;
+
+    ret = strerror_r(err, buf, sizeof(buf));
+    if (ret) {
+        return "unknown error";
+    }
+    return buf;
+}
+#else
+extern const char *sys_errlist[];
+extern int sys_nerr;
+
+const char *terror(int err)
+{
+    if (err >= sys_nerr) {
+        return "unknown error";
+    }
+    return sys_errlist[err];
+}
+#endif
+
+// vim: ts=4:sw=4:et

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/util/time.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/util/time.c b/htrace-c/src/util/time.c
new file mode 100644
index 0000000..d4ad8ed
--- /dev/null
+++ b/htrace-c/src/util/time.c
@@ -0,0 +1,96 @@
+/**
+ * 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 "util/log.h"
+#include "util/time.h"
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+#include <time.h>
+
+uint64_t timespec_to_ms(const struct timespec *ts)
+{
+    uint64_t seconds_ms, microseconds_ms;
+    seconds_ms = ts->tv_sec;
+    seconds_ms *= 1000LLU;
+    microseconds_ms = ts->tv_nsec;
+    microseconds_ms /= 1000000LLU;
+    return seconds_ms + microseconds_ms;
+}
+
+void ms_to_timespec(uint64_t ms, struct timespec *ts)
+{
+    uint64_t sec = ms / 1000LLU;
+    ts->tv_sec = sec;
+    ms -= (sec * 1000LLU);
+    ts->tv_nsec = ms * 1000000LLU;
+}
+
+uint64_t now_ms(struct htrace_log *lg)
+{
+    struct timespec ts;
+    int err;
+
+    if (clock_gettime(CLOCK_REALTIME, &ts)) {
+        err = errno;
+        if (lg) {
+            htrace_log(lg, "clock_gettime(CLOCK_REALTIME) error: %d (%s)\n",
+                       err, terror(err));
+        }
+        return 0;
+    }
+    return timespec_to_ms(&ts);
+}
+
+uint64_t monotonic_now_ms(struct htrace_log *lg)
+{
+    struct timespec ts;
+    int err;
+
+    if (clock_gettime(CLOCK_MONOTONIC, &ts)) {
+        err = errno;
+        if (lg) {
+            htrace_log(lg, "clock_gettime(CLOCK_MONOTONIC) error: %d (%s)\n",
+                       err, terror(err));
+        }
+        return 0;
+    }
+    return timespec_to_ms(&ts);
+}
+
+void sleep_ms(uint64_t ms)
+{
+    struct timespec req, rem;
+
+    ms_to_timespec(ms, &req);
+    memset(&rem, 0, sizeof(rem));
+    do {
+        if (nanosleep(&req, &rem) < 0) {
+            if (errno == EINTR) {
+                rem.tv_sec = req.tv_sec;
+                rem.tv_nsec = req.tv_nsec;
+                continue;
+            }
+        }
+    } while (0);
+}
+
+// vim: ts=4:sw=4:et

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/util/time.h
----------------------------------------------------------------------
diff --git a/htrace-c/src/util/time.h b/htrace-c/src/util/time.h
new file mode 100644
index 0000000..9b4f5d4
--- /dev/null
+++ b/htrace-c/src/util/time.h
@@ -0,0 +1,79 @@
+/**
+ * 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_UTIL_TIME_H
+#define APACHE_HTRACE_UTIL_TIME_H
+
+/**
+ * @file time.h
+ *
+ * Functions for dealing with time.
+ *
+ * This is an internal header, not intended for external use.
+ */
+
+#include <stdint.h>
+
+struct htrace_log;
+struct timespec;
+
+/**
+ * Convert a timespec into a time in milliseconds.
+ *
+ * @param ts            The timespec to convert.
+ *
+ * @return              The current time in milliseconds.
+ */
+uint64_t timespec_to_ms(const struct timespec *ts);
+
+/**
+ * Convert a time in milliseconds into a timespec.
+ *
+ * @param ms            The time in milliseconds to convert.
+ * @param ts            (out param) The timespec to fill.
+ */
+void ms_to_timespec(uint64_t ms, struct timespec *ts);
+
+/**
+ * Get the current wall-clock time in milliseconds.
+ *
+ * @param log           The log to use for error messsages.
+ *
+ * @return              The current wall-clock time in milliseconds.
+ */
+uint64_t now_ms(struct htrace_log *log);
+
+/**
+ * Get the current monotonic time in milliseconds.
+ *
+ * @param log           The log to use for error messsages.
+ *
+ * @return              The current monotonic clock time in milliseconds.
+ */
+uint64_t monotonic_now_ms(struct htrace_log *log);
+
+/**
+ * Sleep for at least a given number of milliseconds.
+ *
+ * @param ms            The number of milliseconds to sleep.
+ */
+void sleep_ms(uint64_t ms);
+
+#endif
+
+// vim: ts=4:sw=4:et

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/style.txt
----------------------------------------------------------------------
diff --git a/htrace-c/style.txt b/htrace-c/style.txt
new file mode 100644
index 0000000..d3c042e
--- /dev/null
+++ b/htrace-c/style.txt
@@ -0,0 +1,219 @@
+HTrace C client coding style
+===============================================================================
+WHITESPACE
+    The HTrace C client uses 4-space indentation.  We use spaces rather than
+hard tabs.  4 space tabs discourage excessive nesting, which makes C code
+harder to understand.  We do not use hard tabs because it requires additional
+editor configuration for many people to see them as intended.
+
+    We have a 79-column limit on line lengths.  Excessively long lines make it
+difficult to perform side-by side diffs during a code review.  They are also
+difficult to read.
+
+    The opening brace for functions is placed on the line after the function
+definition.  Example:
+
+ > void my_function(int foo)
+ > {
+ >     ...
+ > }
+
+    However, the brace for an if statement or structure definition does not get
+its own line:
+ > if (foo) {
+ >     ...
+ > }
+ > struct bar {
+ >     ...
+ > }
+
+    This style visually emphasizes the start of the function, but avoids using
+excessive vertical space for if statements and structures.
+
+    When declaring pointers, we use "int *foo" rather than "int* foo".
+Using the second form implies that "int*" is the type of everything that
+follows.  However, this is incorrect; "int* foo, bar;" declares a foo as a
+pointer-to-int, but bar as an int.  To declare two pointers-to-int on the same
+line in C, you must use int *foo, *bar"-- a reality which the second form makes
+obvious.
+
+    We put braces around all "if" statements, to avoid situations where
+a statement visually appears to be part of an if statement, but in reality is
+not.
+
+
+NAMING
+    We use_underscores for naming rather than CamelCase.  This is a somewhat
+arbitrary choice, but it fits in better with the C standard library and
+most other C libraries.  We do not use "Hungarian Notation," the practice of
+encoding type names into variable names.  Variables have types that are
+enforced by the compiler in C, so this should be sufficient.
+
+    In general, we do not use typedefs for structure names.  Using typedefs for
+structure names prevents the use of forward declarations (see FORWARD
+DECLARATIONS).  It also obscures the true nature of the type... is a foo_t a
+primitive type such as an int, or a structure?  It is usually a bad idea to
+copy a large structure by passing it directly rather than passing a pointer to
+it.  This anti-pattern is more likely to occur if the true nature of the type
+is hidden from the programmer.
+
+    Typedefs are sometimes useful for shortening the type of function pointers.
+They also may be used to represent types that can vary based on architecture of
+platform (although it would be better still to avoid this, most of the time.)
+However, they should not be overused.
+
+    Macros are named in ALL_UPPER_CASE.
+
+
+PUBLIC API
+    Most functions and structures inside the HTrace C client are not "publicly
+visible."  What this means is that they are not accessible to external users of
+libhtrace.so.  These internal functions and structures are part of the
+implementation of libhtrace, not the API.  We have the freedom to change or
+remove them as appropriate without worrying about downstream users being
+affected.
+
+    A few functions and symbols are publicly visible.  These functions are part
+of the "public API" of libhtrace.so.  Every function and structure defined in
+htrace.h is part of the public API.  This public/private separation is enforced
+by the linker, which strips out non-public symbols from the library symbol
+table.
+
+    Publicly visible functions and structures should avoid making architectural
+or platform assumptions.  For example, assuming that time_t is 64 bit is a
+mistake in the public API.
+
+    In general, we want to avoid making backwards-incompatible changes to the
+public API within minor releases of HTrace.  What changes are backwards
+incompatible?  A few examples of backwards incompatible changes are:
+
+* Modifying the types of parameters taken by a publicly visible function.
+* Changing the number of parameters passed to a publicly visible function.
+* Modifying or removing parameters from a publicly visible structure.
+* Removing a publicly visible macro
+
+    In contrast, we can add new functions or structure definitions to the
+public API without breaking backwards compatibility.
+
+    The C++ API is implemented as a header file which wraps the C API.  This
+means that we don't have to worry about C++ binary compatibility issues, which
+can be quite complex.
+
+    The htrace C client exports only the files libhtrace.so, htrace.h, and
+htrace.hpp.  We do not package up our internal header files in the final build!
+They are not accessible or usable outside the library itself.
+
+
+FORWARD DECLARATIONS
+    It is often a good idea to avoid defining a structure in a header file.
+Instead, one can often use a "forward declaration" to make the compiler aware
+that the structure type exists, without specifying its details.  Here is an
+example of a forward declaration:
+
+> struct htrace_conf;
+
+    This declaration notifies the compiler that the type exists.  Most types
+discussed in htrace.h are forward declarations rather than definitions.  This
+gives us the freedom to change the type later, without breaking the public API
+(see PUBLIC API).  Forward declarations can also speed up compilation, by
+minimizing the number of header files that need to be included.
+
+
+ERROR HANDLING
+    C does not have "finally" blocks like Java or a "defer" statement like
+Golang.  As a consequence, programmers must clean up resources which they
+allocate manually.
+
+    One useful pattern for handling errors is the "single exit function"
+pattern.  In this pattern, a function has a single exit point and we perform
+cleanup right before the exit.  An example:
+
+ > int my_function()
+ > {
+ >     int success = 0;
+ >     struct my_resource *resource1 = NULL, *resource2 = NULL;
+ > 
+ >     resource1 = allocate_resource1();
+ >     if (!resource1) {
+ >         goto done;
+ >     }
+ >     resource2 = allocate_resource1();
+ >     if (!resource2) {
+ >         goto done;
+ >     }
+ >     do_stuff(resource1, resource2);
+ >     success = 1;
+ > done:
+ >     if (resource1) {
+ >         free_resource1();
+ >     }
+ >     if (resource2) {
+ >         free_resource2();
+ >     }
+ >     return success;
+ > }
+
+    Similar to a "finally" block in Java, the code after "done" is always
+executed, and will do whatever cleanup is required.  This is much easier and
+more maintainable than trying to manually deallocate whatever is necessary each
+time an error must be handled.  Although this may seem unfamiliar to new C
+programmers, it is a traditional error handling paradigm in kernel code.
+
+    Another error handling paradigm that is sometimes used in HTrace is the
+"error string return."  This paradigm works as follows:
+
+ > void my_function(char *err, size_t err_len)
+ > {
+ >     err[0] = '\0';
+ >     ...
+ >     if (failed) {
+ >         snprintf(err, err_len, "Failed because the foo was %d", foo);
+ >         return;
+ >     }
+ >     ...
+ > }
+
+    The idea behind the error string return is that an error string is more
+flexible than an error code return.  This is generally more useful for
+internal, non-public APIs where there aren't a set of well-defined error codes
+for every possible failure case.  Note that functions which accept an error
+string always initialize the error string to the empty string (no error) as the
+first thing they do.
+
+
+PORTABILITY
+    This code should be portable to both UNIX-based platforms and Microsoft
+Windows.  Although we don't have Windows support yet, it would be nice to
+implement it eventually.
+
+    Using #ifdefs for large blocks of platform-specific code makes source code
+files difficult to read.  When we need to have different implementations of
+something based on the platform or architecture, it is often more appropriate
+to simply exclude or include an entire file from compilation.  This also
+encourages programmers to think about creating platform-neutral interfaces to
+well-encapsulated platform-specific code segments.
+
+
+LIBRARY CONTEXT ISSUES
+    Writing code for a library is more challenging in some ways than writing
+code for an application.
+
+    We cannot call fork() or exec() from our library, because the host
+application may have serious problems with these functions.  For example, if
+the host application has set up atexit() handlers, a fork plus an exit will
+cause those handlers to run unexpectedly.
+
+    We should minimize library dependencies to avoid creating headaches for our
+users.  The more dependencies we use, the more dependencies they must pull in,
+whether they want them or not.  This is why we use the libjson library for unit
+tests, but we do not include it as a dependency of libhtrace.so itself.
+
+    We cannot assume that our implementation of malloc() is the same one used
+by the calling code.  If the library dynamically allocates something, the
+library must also provide a complimentary function to free that thing.  The
+calling code should never call free() or delete on a memory area allocated by
+libhtrace.
+
+    libhtrace may be pulled in "transitively" as a dependency of another
+library.  Or it may be pulled in transitively as well as being used directly by
+the application.  We should support all of these use-cases.

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-core/src/go/src/org/apache/htrace/htraced/rest.go
----------------------------------------------------------------------
diff --git a/htrace-core/src/go/src/org/apache/htrace/htraced/rest.go 
b/htrace-core/src/go/src/org/apache/htrace/htraced/rest.go
index 978862a..1449802 100644
--- a/htrace-core/src/go/src/org/apache/htrace/htraced/rest.go
+++ b/htrace-core/src/go/src/org/apache/htrace/htraced/rest.go
@@ -44,7 +44,7 @@ func setResponseHeaders(hdr http.Header) {
 func writeError(lg *common.Logger, w http.ResponseWriter, errCode int,
        errStr string) {
        str := strings.Replace(errStr, `"`, `'`, -1)
-       lg.Info(str)
+       lg.Info(str + "\n")
        w.WriteHeader(errCode)
        w.Write([]byte(`{ "error" : "` + str + `"}`))
 }

http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index c96dc1a..3fb131e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,6 +28,7 @@ language governing permissions and limitations under the 
License. -->
   <url>http://htrace.incubator.apache.org</url>
 
   <modules>
+    <module>htrace-c</module>
     <module>htrace-core</module>
     <module>htrace-zipkin</module>
     <module>htrace-hbase</module>

Reply via email to