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:19 UTC
[1/4] incubator-htrace git commit: HTRACE-106: htrace: add C / C++
native client (cmccabe)
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>
[4/4] incubator-htrace git commit: HTRACE-106: htrace: add C / C++
native client (cmccabe)
Posted by cm...@apache.org.
HTRACE-106: htrace: add C / C++ native client (cmccabe)
Project: http://git-wip-us.apache.org/repos/asf/incubator-htrace/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-htrace/commit/a9d85254
Tree: http://git-wip-us.apache.org/repos/asf/incubator-htrace/tree/a9d85254
Diff: http://git-wip-us.apache.org/repos/asf/incubator-htrace/diff/a9d85254
Branch: refs/heads/master
Commit: a9d85254b42380ba09332be6c25d08939b6d794f
Parents: 20f00f9
Author: Colin P. Mccabe <cm...@apache.org>
Authored: Sun Apr 19 17:15:19 2015 -0700
Committer: Colin P. Mccabe <cm...@apache.org>
Committed: Sun Apr 19 17:48:35 2015 -0700
----------------------------------------------------------------------
htrace-c/BUILDING.txt | 32 +
htrace-c/README.md | 31 +
htrace-c/pom.xml | 108 +++
htrace-c/src/CMakeLists.txt | 215 ++++++
htrace-c/src/core/conf.c | 260 ++++++++
htrace-c/src/core/conf.h | 110 ++++
htrace-c/src/core/htrace.h | 346 ++++++++++
htrace-c/src/core/htrace.hpp | 239 +++++++
htrace-c/src/core/htracer.c | 167 +++++
htrace-c/src/core/htracer.h | 101 +++
htrace-c/src/core/scope.c | 166 +++++
htrace-c/src/core/scope.h | 58 ++
htrace-c/src/core/span.c | 187 ++++++
htrace-c/src/core/span.h | 138 ++++
htrace-c/src/receiver/curl.c | 124 ++++
htrace-c/src/receiver/curl.h | 60 ++
htrace-c/src/receiver/htraced.c | 649 +++++++++++++++++++
htrace-c/src/receiver/local_file.c | 184 ++++++
htrace-c/src/receiver/noop.c | 65 ++
htrace-c/src/receiver/receiver.c | 75 +++
htrace-c/src/receiver/receiver.h | 114 ++++
htrace-c/src/sampler/always.c | 71 ++
htrace-c/src/sampler/never.c | 71 ++
htrace-c/src/sampler/prob.c | 137 ++++
htrace-c/src/sampler/sampler.c | 88 +++
htrace-c/src/sampler/sampler.h | 121 ++++
htrace-c/src/test/conf-unit.c | 95 +++
htrace-c/src/test/htable-unit.c | 92 +++
htrace-c/src/test/htraced_rcv-unit.c | 110 ++++
htrace-c/src/test/linkage-unit.c | 105 +++
htrace-c/src/test/local_file_rcv-unit.c | 74 +++
htrace-c/src/test/log-unit.c | 97 +++
htrace-c/src/test/mini_htraced-unit.c | 48 ++
htrace-c/src/test/mini_htraced.c | 599 +++++++++++++++++
htrace-c/src/test/mini_htraced.h | 154 +++++
htrace-c/src/test/process_id-unit.c | 80 +++
htrace-c/src/test/rand-unit.c | 93 +++
htrace-c/src/test/rtest.c | 155 +++++
htrace-c/src/test/rtest.h | 76 +++
htrace-c/src/test/rtestpp.cc | 154 +++++
htrace-c/src/test/sampler-unit.c | 138 ++++
htrace-c/src/test/span-unit.c | 173 +++++
htrace-c/src/test/span_table.c | 139 ++++
htrace-c/src/test/span_table.h | 95 +++
htrace-c/src/test/span_util-unit.c | 74 +++
htrace-c/src/test/span_util.c | 294 +++++++++
htrace-c/src/test/span_util.h | 66 ++
htrace-c/src/test/string-unit.c | 73 +++
htrace-c/src/test/temp_dir-unit.c | 89 +++
htrace-c/src/test/temp_dir.c | 204 ++++++
htrace-c/src/test/temp_dir.h | 67 ++
htrace-c/src/test/test.c | 158 +++++
htrace-c/src/test/test.h | 211 ++++++
htrace-c/src/test/test_config.h.cmake | 28 +
htrace-c/src/test/time-unit.c | 84 +++
htrace-c/src/util/build.h.cmake | 23 +
htrace-c/src/util/htable.c | 290 +++++++++
htrace-c/src/util/htable.h | 169 +++++
htrace-c/src/util/log.c | 104 +++
htrace-c/src/util/log.h | 69 ++
htrace-c/src/util/process_id.c | 304 +++++++++
htrace-c/src/util/process_id.h | 58 ++
htrace-c/src/util/rand.h | 63 ++
htrace-c/src/util/rand_linux.c | 143 ++++
htrace-c/src/util/rand_posix.c | 120 ++++
htrace-c/src/util/string.c | 107 +++
htrace-c/src/util/string.h | 64 ++
htrace-c/src/util/terror.c | 65 ++
htrace-c/src/util/time.c | 96 +++
htrace-c/src/util/time.h | 79 +++
htrace-c/style.txt | 219 +++++++
.../go/src/org/apache/htrace/htraced/rest.go | 2 +-
pom.xml | 1 +
73 files changed, 9717 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/BUILDING.txt
----------------------------------------------------------------------
diff --git a/htrace-c/BUILDING.txt b/htrace-c/BUILDING.txt
new file mode 100644
index 0000000..f6728a0
--- /dev/null
+++ b/htrace-c/BUILDING.txt
@@ -0,0 +1,32 @@
+Building the htrace-c client
+===============================================================================
+To build the htrace-c client, activate the native profile. Example:
+
+ mvn package -DskipTests -Dmaven.javadoc.skip=true -Pnative
+
+BUILD DEPENDENCIES
+ C compiler
+ To compile the sources.
+
+ CMake
+ The CMake build system. Needed to run the native build. See
+ www.cmake.org. This should be available via "yum install cmake" or
+ similar.
+
+ libcurl-devel
+ A library to transfer data over HTTP. See http://curl.haxx.se/dev/.
+ Should be available via "yum install libcurl-devel" or similar.
+
+TEST DEPENDENCIES
+ C++ compiler
+ To compile some of the unit tests.
+
+ libjson-c
+ A library to parse JSON. This is only used for unit tests. See
+ https://github.com/json-c/json-c/wiki. Should be available via "yum
+ install json-c-devel" or similar.
+
+ htraced binary
+ You must compile the htraced binaries before running the unit tests.
+ You can do this by running "mvn compile" on the top-level project, or
+ in the htrace-htraced directory.
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/README.md
----------------------------------------------------------------------
diff --git a/htrace-c/README.md b/htrace-c/README.md
new file mode 100644
index 0000000..0347e83
--- /dev/null
+++ b/htrace-c/README.md
@@ -0,0 +1,31 @@
+The HTrace C client
+===============================================================================
+The HTrace C client is useful for distributed systems in C and C++ that need
+tracing.
+
+To use the HTrace C client, you must link against the libhtrace.so library.
+On a UNIX-based platform, you would link against the version of the library
+containing only the major version number. For example, you might link against
+libhtrace.so.3, which would then link against the appropriate minor version of
+the library, such as libhtrace.so.3.2.0. The libhtrace API will not change in
+backwards-incompatible ways within a major version.
+
+Some APIs in the library take htrace_conf objects. You can create these
+objects via htrace_conf_from_str. A string of the form "KEY1=VAL1;KEY2=VAL2"
+will create a configuration object with KEY1 set to VAL1, KEY2 set to VAL2,
+etc. htrace.h defines the configuration keys you can set, such as
+HTRACE_LOG_PATH_KEY.
+
+In general, you will want to create a single global htrace_ctx object,
+representing an htrace context object, for your program. The all threads can
+use this htrace_ctx object. The htrace_ctx contains all the per-process
+htrace state, such as the process name, the thread-local data, and the htrace
+receiver object that the process is using.
+
+If your process supports orderly shutdown, you can call htrace_ctx_free to
+accomplish this. However, you should be sure that there are no references to
+the htrace context before freeing it. Most daemons do not support orderly
+shutdown, so this is not usually a problem. It is likely to come up in the
+context of a library which uses the native htrace client.
+
+For a quick reference to the basics of trace spans and scopes, see htrace.h.
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/pom.xml
----------------------------------------------------------------------
diff --git a/htrace-c/pom.xml b/htrace-c/pom.xml
new file mode 100644
index 0000000..51e3478
--- /dev/null
+++ b/htrace-c/pom.xml
@@ -0,0 +1,108 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Licensed 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. See accompanying LICENSE file.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
+ http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>htrace-c</artifactId>
+ <packaging>jar</packaging>
+
+ <parent>
+ <artifactId>htrace</artifactId>
+ <groupId>org.apache.htrace</groupId>
+ <version>3.2.0-incubating-SNAPSHOT</version>
+ <relativePath>..</relativePath>
+ </parent>
+
+ <name>htrace-c</name>
+ <url>http://incubator.apache.org/projects/htrace.html</url>
+
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+
+ <profiles>
+ <profile>
+ <id>native</id>
+ <activation>
+ <activeByDefault>false</activeByDefault>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-enforcer-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>enforce-os</id>
+ <goals>
+ <goal>enforce</goal>
+ </goals>
+ <configuration>
+ <rules>
+ <requireOS>
+ <family>mac</family>
+ <family>unix</family>
+ <message>The native build is only supported on Mac and other UNIX systems.</message>
+ </requireOS>
+ </rules>
+ <fail>true</fail>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>make</id>
+ <phase>compile</phase>
+ <goals><goal>run</goal></goals>
+ <configuration>
+ <target>
+ <mkdir dir="${project.build.directory}/build"/>
+ <exec executable="cmake" dir="${project.build.directory}/build" failonerror="true">
+ <arg line="${basedir}/src -DCMAKE_INSTALL_PREFIX=${project.build.directory}/install"/>
+ </exec>
+ <exec executable="make" dir="${project.build.directory}/build" failonerror="true">
+ <arg line="install VERBOSE=1"/>
+ </exec>
+ </target>
+ </configuration>
+ </execution>
+ <execution>
+ <id>native_tests</id>
+ <phase>test</phase>
+ <goals><goal>run</goal></goals>
+ <configuration>
+ <target>
+ <exec executable="sh" dir="${project.build.directory}/build" failonerror="true">
+ <arg value="-c"/>
+ <arg value="[ x$SKIPTESTS = xtrue ] || make test"/>
+ <env key="SKIPTESTS" value="${skipTests}"/>
+ </exec>
+ </target>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/CMakeLists.txt
----------------------------------------------------------------------
diff --git a/htrace-c/src/CMakeLists.txt b/htrace-c/src/CMakeLists.txt
new file mode 100644
index 0000000..c9b124d
--- /dev/null
+++ b/htrace-c/src/CMakeLists.txt
@@ -0,0 +1,215 @@
+#
+# 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.
+#
+
+#
+# Build the C client for the HTrace distributed tracing system.
+#
+
+cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
+set(CMAKE_BUILD_TYPE, Release) # Default to release builds
+
+# Define "make check" as an alias for "make test."
+add_custom_target(check COMMAND ctest)
+enable_testing()
+
+if (WIN32)
+ MESSAGE(FATAL_ERROR "Windows support is not yet available.")
+else() # UNIX
+ set(CMAKE_C_FLAGS "-g ${CMAKE_C_FLAGS} -Wall -O2 -fno-strict-aliasing")
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_REENTRANT") # Enable pthreads.
+ if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
+ # _GNU_SOURCE is needed to see all the glibc definitions.
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_GNU_SOURCE")
+ # Use the 64-bit forms of off_t, etc.
+ set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64")
+ endif()
+endif()
+
+INCLUDE(CheckCSourceCompiles)
+CHECK_C_SOURCE_COMPILES("int main(void) { static __thread int i = 0; return 0; }" HAVE_IMPROVED_TLS)
+CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/util/build.h.cmake ${CMAKE_BINARY_DIR}/util/build.h)
+
+get_filename_component(HTRACE_ABSPATH "../../htrace-core/src/go/build/htrace" ABSOLUTE)
+get_filename_component(HTRACED_ABSPATH "../../htrace-core/src/go/build/htraced" ABSOLUTE)
+CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/test/test_config.h.cmake ${CMAKE_BINARY_DIR}/test/test_config.h)
+
+find_package(CURL REQUIRED)
+
+find_package(PkgConfig)
+pkg_check_modules(PC_JSON-C QUIET json-c)
+find_path(JSON_C_INCLUDE_DIR "json.h"
+ HINTS ${PC_JSON-C_INCLUDEDIR} ${PC_JSON-C_INCLUDE_DIRS} PATH_SUFFIXES json-c json)
+find_library(JSON_C_LIBRARY NAMES json-c json libjson
+ HINTS ${PC_JSON-C_LIBDIR} ${PC_JSON-C_LIBRARY_DIRS})
+IF(JSON_C_INCLUDE_DIR AND JSON_C_LIBRARY)
+ELSE(JSON_C_INCLUDE_DIR AND JSON_C_LIBRARY)
+ MESSAGE(FATAL_ERROR "Failed to find libjson-c. Try installing libjson-c with apt-get or yum, or install it manually from http://oss.metaparadigm.com/json-c/")
+ENDIF(JSON_C_INCLUDE_DIR AND JSON_C_LIBRARY)
+
+include_directories(${CURL_INCLUDE_DIR}
+ ${CMAKE_BINARY_DIR}
+ ${CMAKE_SOURCE_DIR})
+
+if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
+ set(RAND_SRC "util/rand_linux.c")
+else()
+ set(RAND_SRC "util/rand_posix.c")
+endif()
+
+set(SRC_ALL
+ ${RAND_SRC}
+ core/conf.c
+ core/htracer.c
+ core/scope.c
+ core/span.c
+ receiver/curl.c
+ receiver/htraced.c
+ receiver/local_file.c
+ receiver/noop.c
+ receiver/receiver.c
+ sampler/always.c
+ sampler/never.c
+ sampler/prob.c
+ sampler/sampler.c
+ util/htable.c
+ util/log.c
+ util/process_id.c
+ util/string.c
+ util/terror.c
+ util/time.c
+)
+
+set(DEPS_ALL
+ ${CURL_LIBRARY}
+ pthread)
+
+# The unit test version of the library, which exposes all symbols.
+add_library(htrace_test STATIC
+ ${SRC_ALL}
+ test/mini_htraced.c
+ test/span_table.c
+ test/span_util.c
+ test/temp_dir.c
+ test/test.c
+)
+target_link_libraries(htrace_test ${DEPS_ALL} ${JSON_C_LIBRARY})
+
+# Hide all symbols by default. Only the symbols we specifically mark as
+# visible should be accessable by the library user. This should avoid
+# conflicts between our function and global variable names and those of
+# the host program.
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fvisibility=hidden")
+
+# The production version of the library, which exposes only the public API.
+add_library(htrace SHARED ${SRC_ALL})
+target_link_libraries(htrace ${DEPS_ALL})
+set(HTRACE_VERSION_MAJOR "3")
+set(HTRACE_VERSION_MINOR "2")
+set(HTRACE_VERSION_PATCH "0")
+set(HTRACE_VERSION_STRING
+ "${HTRACE_VERSION_MAJOR}.${HTRACE_VERSION_MINOR}.${HTRACE_VERSION_PATCH}")
+set_target_properties(htrace PROPERTIES
+ VERSION ${HTRACE_VERSION_STRING}
+ SOVERSION ${HTRACE_VERSION_MAJOR})
+
+macro(add_utest utest)
+ add_executable(${utest}
+ ${ARGN}
+ )
+ target_link_libraries(${utest} htrace_test)
+ add_test(${utest} ${CMAKE_CURRENT_BINARY_DIR}/${utest} ${utest})
+endmacro(add_utest)
+
+add_utest(conf-unit
+ test/conf-unit.c
+)
+
+add_utest(htable-unit
+ test/htable-unit.c
+)
+
+add_utest(htraced_rcv-unit
+ test/htraced_rcv-unit.c
+ test/rtest.c
+)
+
+add_executable(linkage-unit test/linkage-unit.c)
+target_link_libraries(linkage-unit htrace dl)
+add_test(linkage-unit ${CMAKE_CURRENT_BINARY_DIR}/linkage-unit linkage-unit)
+
+add_utest(local_file_rcv-unit
+ test/local_file_rcv-unit.c
+ test/rtest.c
+)
+
+add_utest(local_file_rcvpp-unit
+ test/local_file_rcv-unit.c
+ test/rtestpp.cc
+)
+
+add_utest(log-unit
+ test/log-unit.c
+)
+
+add_utest(mini_htraced-unit
+ test/mini_htraced-unit.c
+)
+
+add_utest(process_id-unit
+ test/process_id-unit.c
+)
+
+if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
+ add_utest(rand_linux-unit
+ test/rand-unit.c
+ util/rand_linux.c
+ )
+endif()
+add_utest(rand_posix-unit
+ test/rand-unit.c
+ util/rand_posix.c
+)
+
+add_utest(sampler-unit
+ test/sampler-unit.c
+)
+
+add_utest(span-unit
+ test/span-unit.c
+)
+
+add_utest(span_util-unit
+ test/span_util-unit.c
+)
+
+add_utest(string-unit
+ test/string-unit.c
+)
+
+add_utest(temp_dir-unit
+ test/temp_dir-unit.c
+)
+
+add_utest(time-unit
+ test/time-unit.c
+)
+
+# Install libhtrace.so and htrace.h.
+# These are the only build products that external users can consume.
+install(TARGETS htrace DESTINATION lib)
+install(FILES ${CMAKE_SOURCE_DIR}/core/htrace.h DESTINATION include)
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/core/conf.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/core/conf.c b/htrace-c/src/core/conf.c
new file mode 100644
index 0000000..936e224
--- /dev/null
+++ b/htrace-c/src/core/conf.c
@@ -0,0 +1,260 @@
+/**
+ * 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/htable.h"
+#include "util/log.h"
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define HTRACE_DEFAULT_CONF_KEYS (\
+ HTRACE_PROB_SAMPLER_FRACTION_KEY "=0.01"\
+ ";" HTRACED_BUFFER_SIZE_KEY "=67108864"\
+ ";" HTRACED_SEND_TIMEOUT_MS_KEY "=120000"\
+ ";" HTRACE_PROCESS_ID "=%{tname}/%{ip}"\
+ ";" HTRACED_ADDRESS_KEY "=localhost:9095"\
+ )
+
+static int parse_key_value(char *str, char **key, char **val)
+{
+ char *eq = strchr(str, '=');
+ if (eq) {
+ *eq = '\0';
+ *val = strdup(eq + 1);
+ } else {
+ *val = strdup("true");
+ }
+ if (!*val) {
+ return ENOMEM;
+ }
+ *key = strdup(str);
+ if (!*key) {
+ free(*val);
+ return ENOMEM;
+ }
+ return 0;
+}
+
+static struct htable *htable_from_str(const char *str)
+{
+ struct htable *ht;
+ char *cstr = NULL, *saveptr = NULL, *tok;
+ int ret = ENOMEM;
+
+ ht = htable_alloc(8, ht_hash_string, ht_compare_string);
+ if (!ht) {
+ goto done;
+ }
+ if (!str) {
+ ret = 0;
+ goto done;
+ }
+ cstr = strdup(str);
+ if (!cstr) {
+ goto done;
+ }
+
+ for (tok = strtok_r(cstr, ";", &saveptr); tok;
+ tok = strtok_r(NULL, ";", &saveptr)) {
+ char *key = NULL, *val = NULL;
+ ret = parse_key_value(tok, &key, &val);
+ if (ret) {
+ goto done;
+ }
+ ret = htable_put(ht, key, val);
+ if (ret) {
+ goto done;
+ }
+ }
+ ret = 0;
+done:
+ if (ret) {
+ htable_free(ht);
+ ht = NULL;
+ }
+ free(cstr);
+ return ht;
+}
+
+struct htrace_conf *htrace_conf_from_strs(const char *values,
+ const char *defaults)
+{
+ struct htrace_conf *cnf;
+
+ cnf = calloc(1, sizeof(*cnf));
+ if (!cnf) {
+ return NULL;
+ }
+ cnf->values = htable_from_str(values);
+ if (!cnf->values) {
+ htrace_conf_free(cnf);
+ return NULL;
+ }
+ cnf->defaults = htable_from_str(defaults);
+ if (!cnf->defaults) {
+ htrace_conf_free(cnf);
+ return NULL;
+ }
+ return cnf;
+}
+
+struct htrace_conf *htrace_conf_from_str(const char *values)
+{
+ return htrace_conf_from_strs(values, HTRACE_DEFAULT_CONF_KEYS);
+}
+
+static void htrace_tuple_free(void *ctx, void *key, void *val)
+{
+ free(key);
+ free(val);
+}
+
+void htrace_conf_free(struct htrace_conf *cnf)
+{
+ if (!cnf) {
+ return;
+ }
+ if (cnf->values) {
+ htable_visit(cnf->values, htrace_tuple_free, NULL);
+ htable_free(cnf->values);
+ }
+ if (cnf->defaults) {
+ htable_visit(cnf->defaults, htrace_tuple_free, NULL);
+ htable_free(cnf->defaults);
+ }
+ free(cnf);
+}
+
+const char *htrace_conf_get(const struct htrace_conf *cnf, const char *key)
+{
+ const char *val;
+
+ val = htable_get(cnf->values, key);
+ if (val)
+ return val;
+ val = htable_get(cnf->defaults, key);
+ return val;
+}
+
+static int convert_double(struct htrace_log *log, const char *key,
+ const char *in, double *out)
+{
+ char *endptr = NULL;
+ int err;
+ double ret;
+
+ errno = 0;
+ ret = strtod(in, &endptr);
+ if (errno) {
+ err = errno;
+ htrace_log(log, "error parsing %s for %s: %d (%s)\n",
+ in, key, err, terror(err));
+ return 0;
+ }
+ while (1) {
+ char c = *endptr;
+ if (c == '\0') {
+ break;
+ }
+ if ((c != ' ') || (c != '\t')) {
+ htrace_log(log, "error parsing %s for %s: garbage at end "
+ "of string.\n", in, key);
+ return 0;
+ }
+ }
+ *out = ret;
+ return 1;
+}
+
+double htrace_conf_get_double(struct htrace_log *log,
+ const struct htrace_conf *cnf, const char *key)
+{
+ const char *val;
+ double out = 0;
+
+ val = htable_get(cnf->values, key);
+ if (val) {
+ if (convert_double(log, key, val, &out)) {
+ return out;
+ }
+ }
+ val = htable_get(cnf->defaults, key);
+ if (val) {
+ if (convert_double(log, key, val, &out)) {
+ return out;
+ }
+ }
+ return 0;
+}
+
+static int convert_u64(struct htrace_log *log, const char *key,
+ const char *in, uint64_t *out)
+{
+ char *endptr = NULL;
+ int err;
+ uint64_t ret;
+
+ errno = 0;
+ ret = strtoull(in, &endptr, 10);
+ if (errno) {
+ err = errno;
+ htrace_log(log, "error parsing %s for %s: %d (%s)\n",
+ in, key, err, terror(err));
+ return 0;
+ }
+ while (1) {
+ char c = *endptr;
+ if (c == '\0') {
+ break;
+ }
+ if ((c != ' ') || (c != '\t')) {
+ htrace_log(log, "error parsing %s for %s: garbage at end "
+ "of string.\n", in, key);
+ return 0;
+ }
+ }
+ *out = ret;
+ return 1;
+}
+
+uint64_t htrace_conf_get_u64(struct htrace_log *log,
+ const struct htrace_conf *cnf, const char *key)
+{
+ const char *val;
+ uint64_t out = 0;
+
+ val = htable_get(cnf->values, key);
+ if (val) {
+ if (convert_u64(log, key, val, &out)) {
+ return out;
+ }
+ }
+ val = htable_get(cnf->defaults, key);
+ if (val) {
+ if (convert_u64(log, key, val, &out)) {
+ return out;
+ }
+ }
+ return 0;
+}
+
+// vim:ts=4:sw=4:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/core/conf.h
----------------------------------------------------------------------
diff --git a/htrace-c/src/core/conf.h b/htrace-c/src/core/conf.h
new file mode 100644
index 0000000..aa0bb0b
--- /dev/null
+++ b/htrace-c/src/core/conf.h
@@ -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.
+ */
+
+#ifndef APACHE_HTRACE_CORE_CONF_H
+#define APACHE_HTRACE_CORE_CONF_H
+
+#include <stdint.h>
+
+/**
+ * @file conf.h
+ *
+ * Functions to manipulate HTrace configuration objects.
+ *
+ * This is an internal header, not intended for external use.
+ */
+
+struct htable;
+struct htrace_log;
+
+struct htrace_conf {
+ /**
+ * A hash table mapping keys to the values that were set.
+ */
+ struct htable *values;
+
+ /**
+ * A hash table mapping keys to the default values that were set for those
+ * keys.
+ */
+ struct htable *defaults;
+};
+
+/**
+ * Create an HTrace conf object from a values string and a defaults string.
+ *
+ * See {@ref htrace_conf_from_str} for the format.
+ *
+ * The configuration object must be later freed with htrace_conf_free.
+ *
+ * @param str The configuration string.
+ *
+ * @return NULL on OOM; the htrace configuration otherwise.
+ */
+struct htrace_conf *htrace_conf_from_strs(const char *values,
+ const char *defaults);
+
+/**
+ * Free an HTrace configuration object.
+ *
+ * @param cnf The HTrace configuration object.
+ */
+void htrace_conf_free(struct htrace_conf *cnf);
+
+/**
+ * Get the value of a key in a configuration.
+ *
+ * @param cnf The configuration.
+ * @param key The key.
+ *
+ * @return NULL if the key was not found in the values or the
+ * defaults; the value otherwise.
+ */
+const char *htrace_conf_get(const struct htrace_conf *cnf, const char *key);
+
+/**
+ * Get the value of a key in a configuration as a floating point double.
+ *
+ * @param log Log to send parse error messages to.
+ * @param cnf The configuration.
+ * @param key The key.
+ *
+ * @return The value if it was found.
+ * The default value if it was not found.
+ * 0.0 if there was no default value.
+ */
+double htrace_conf_get_double(struct htrace_log *log,
+ const struct htrace_conf *cnf, const char *key);
+
+/**
+ * Get the value of a key in a configuration as a uint64_t.
+ *
+ * @param log Log to send parse error messages to.
+ * @param cnf The configuration.
+ * @param key The key.
+ *
+ * @return The value if it was found.
+ * The default value if it was not found.
+ * 0 if there was no default value.
+ */
+uint64_t htrace_conf_get_u64(struct htrace_log *log,
+ const struct htrace_conf *cnf, const char *key);
+
+#endif
+
+// vim: ts=4: sw=4: et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/core/htrace.h
----------------------------------------------------------------------
diff --git a/htrace-c/src/core/htrace.h b/htrace-c/src/core/htrace.h
new file mode 100644
index 0000000..cdebd31
--- /dev/null
+++ b/htrace-c/src/core/htrace.h
@@ -0,0 +1,346 @@
+/**
+ * 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_HTRACE_H
+#define APACHE_HTRACE_HTRACE_H
+
+#include <stdint.h> /* for uint64_t, etc. */
+
+/**
+ * The public API for the HTrace C client.
+ *
+ * SPANS AND SCOPES
+ * HTrace is a tracing framework for distributed systems. The smallest unit of
+ * tracing in HTrace is the trace span. Trace spans represent intervals during
+ * which a thread is performing some work. Trace spans are identified by a
+ * 64-bit ID called the trace span ID. Trace spans can have one or more
+ * parents. The parent of a trace span is the operation or operations that
+ * caused it to happen.
+ *
+ * Trace spans are managed by htrace_scope objects. Creating an htrace_scope
+ * (potentially) starts a trace span. The trace span will be closed once the
+ * htrace_scope is closed and freed.
+ *
+ * SPAN RECEIVERS
+ * When a span is closed, it is sent to the current "span receiver." Span
+ * receivers decide what to do with the span data. For example, the "local
+ * file" span receiver saves the span data to a local file. The "htraced" span
+ * receiver sends the span data to the htraced daemon.
+ *
+ * Most interesting span receivers will start a background thread to handle
+ * their workload. This background thread will last until the associated
+ * htracer is shut down.
+ *
+ * SAMPLING
+ * HTrace is based around the concept of sampling. That means that only some
+ * trace scopes are managing spans-- the rest do nothing. Sampling is managed
+ * by htrace_sampler objects. The two most important samplers are the
+ * probability based sampler, and the "always" and "never" samplers.
+ *
+ * TRACERS
+ * The HTrace C client eschews globals. Instead, you are invited to create your
+ * own htracer (HTrace context) object and use it throughout your program or
+ * library. The htracer object contains the logging settings and the currently
+ * configured span receiver. Tracers are thread-safe, so you can use the same
+ * tracer for all of your threads if you like.
+ *
+ * As already mentioned, the Tracer may contain threads, so please do not call
+ * htracer_create until you are ready to start threads in your program. For
+ * example, do not call it prior to daemonizing.
+ *
+ * COMPATIBILITY
+ * When modifying this code, please try to avoid breaking binary compatibility.
+ * Applications compiled against older versions of libhtrace.so should continue
+ * to work when new versions of the library are dropped in.
+ *
+ * Adding new functions is always OK. Modifying the type signature of existing
+ * functions is not OK. When adding structures, try to avoid including the
+ * structure definition in this header, so that we can change it later on with
+ * no harmful effects. Perhaps we may need to break compatibility at some
+ * point, but let's try to avoid that if we can.
+ *
+ * PORTABILITY
+ * We aim for POSIX compatibility, although we have not done a lot of testing on
+ * non-Linux systems. Eventually, we will want to support Windows.
+ */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#pragma GCC visibility push(default) // Begin publicly visible symbols
+
+// Configuration keys.
+
+/**
+ * The path to use for the htrace client log.
+ * If this is unset, we will log to stderr.
+ */
+#define HTRACE_LOG_PATH_KEY "log.path"
+
+/**
+ * The span receiver implementation to use.
+ *
+ * Possible values:
+ * noop The "no op" span receiver, which discards all spans.
+ * local.file A receiver which writes spans to local files.
+ * htraced The htraced span receiver, which sends spans to htraced.
+ */
+#define HTRACE_SPAN_RECEIVER_KEY "span.receiver"
+
+/**
+ * The path which the local file span receiver should write spans to.
+ */
+#define HTRACE_LOCAL_FILE_RCV_PATH_KEY "local.file.path"
+
+/**
+ * The hostname and port which the htraced span receiver should send its spans
+ * to. This is in the format "hostname:port".
+ */
+#define HTRACED_ADDRESS_KEY "htraced.address"
+
+/**
+ * The timeout to use when sending spans to the htraced server.
+ */
+#define HTRACED_SEND_TIMEOUT_MS_KEY "htraced.send.timeout.ms"
+
+/**
+ * The size of the circular buffer to use in the htraced receiver.
+ */
+#define HTRACED_BUFFER_SIZE_KEY "htraced.buffer.size"
+
+/**
+ * The process ID string to use.
+ *
+ * %{ip} will be replaced by an IP address;
+ * %{pid} will be replaced by the operating system process ID;
+ * %{tname} will be replaced by the Tracer name.
+ *
+ * Defaults to %{tname}/%{ip}
+ */
+#define HTRACE_PROCESS_ID "process.id"
+
+/**
+ * The sampler to use.
+ *
+ * Possible values:
+ * never A sampler which never fires.
+ * always A sampler which always fires.
+ * prob A sampler which fires with some probability.
+ */
+#define HTRACE_SAMPLER_KEY "sampler"
+
+/**
+ * For the probability sampler, the fraction of the time that we should create a
+ * new span. This is a floating point number which is between 0.0 and 1.1,
+ * inclusive. It is _not_ a percentage.
+ */
+#define HTRACE_PROB_SAMPLER_FRACTION_KEY "prob.sampler.fraction"
+
+ // Forward declarations
+ struct htrace_conf;
+ struct htracer;
+ struct htrace_scope;
+
+ /**
+ * Create an HTrace conf object from a string.
+ *
+ * The string should be in the form:
+ * key1=val1;key2=val2;...
+ * Entries without an equals sign will set the key to 'true'.
+ *
+ * The configuration object must be later freed with htrace_conf_free.
+ *
+ * @param values The configuration string to parse.
+ * You may free this string after this function
+ * returns.
+ *
+ * @return NULL on out-of-memory error; the configuration
+ * object otherwise.
+ */
+ struct htrace_conf *htrace_conf_from_str(const char *values);
+
+ /**
+ * Free an htrace configuration.
+ *
+ * @param conf The configuration object to free.
+ */
+ void htrace_conf_free(struct htrace_conf *cnf);
+
+ /**
+ * Create a Tracer.
+ *
+ * This function does a few things:
+ * - Initialize logging (if there are configuration tuples related to
+ * logging)
+ * - Initialize trace span receivers, if any are configured.
+ *
+ * This function may start background threads.
+ *
+ * @param tname The name of the tracer to create. Will be
+ * deep-copied. Must not be null.
+ * @param conf The configuration to use. You may free this
+ * configuration object after calling this
+ * function.
+ *
+ * @return NULL on OOM; the tracer otherwise.
+ */
+ struct htracer *htracer_create(const char *tname,
+ const struct htrace_conf *cnf);
+
+ /**
+ * Get the Tracer name.
+ *
+ * @param tracer The tracer.
+ *
+ * @return The tracer name. This string is managed by the
+ * tracer itself and will remain valid until the
+ * tracer is freed.
+ */
+ const char *htracer_tname(const struct htracer *tracer);
+
+ /**
+ * Free an HTracer.
+ *
+ * Frees the memory and other resources associated with a Tracer.
+ * Closes the log file if there is one open. Shuts down the span receiver
+ * if there is one active. Attempt to flush all buffered spans.
+ *
+ * Do not call this function until all the samplers which hold a reference
+ * to this htracer have been freed. Do not call this function if there is
+ * currently an active htrace_scope object which holds a reference to this
+ * tracer.
+ *
+ * @param tracer The tracer to free.
+ */
+ void htracer_free(struct htracer *tracer);
+
+ /**
+ * Create an htrace configuration sample from a configuration.
+ *
+ * Samplers are thread-safe; you may use the same sampler simultaneously
+ * from multiple threads.
+ *
+ * @param tracer The HTracer to use. The sampler will hold a
+ * reference to this tracer. Do not free the
+ * tracer until after the sampler has been freed.
+ * @param conf The configuration to use. You may free this
+ * configuration object after calling this
+ * function.
+ *
+ * @return NULL if we are out of memory.
+ * NULL if the configuration was invalid.
+ * NULL if no sampler is configured.
+ * The sampler otherwise.
+ * Error conditions will be logged to the htracer log.
+ */
+ struct htrace_sampler *htrace_sampler_create(struct htracer *tracer,
+ struct htrace_conf *cnf);
+
+ /**
+ * Get the name of an HTrace sampler.
+ *
+ * @param smp The sampler.
+ *
+ * @return The sampler name. This string is managed by the
+ * sampler itself and will remain valid until the
+ * sampler is freed.
+ */
+ const char *htrace_sampler_to_str(struct htrace_sampler *smp);
+
+ /**
+ * Free an htrace sampler.
+ *
+ * @param sampler The sampler to free.
+ */
+ void htrace_sampler_free(struct htrace_sampler *smp);
+
+ /**
+ * Start a new trace span if necessary.
+ *
+ * You must call htrace_close_span on the scope object returned by this
+ * function.
+ *
+ * @param tracer The htracer to use. Must remain valid for the
+ * duration of the scope.
+ * @param sampler The sampler to use, or NULL for no sampler.
+ * If no sampler is used, we will create a new span
+ * only if there is a current active span.
+ * @param desc The description of the trace span. Will be deep-copied.
+ *
+ * @return The trace scope. NULL if we ran out of memory, or if we
+ * are not tracing.
+ */
+ struct htrace_scope* htrace_start_span(struct htracer *tracer,
+ struct htrace_sampler *sampler, const char *desc);
+
+ /**
+ * Detach the trace span from the given trace scope.
+ *
+ * @param scope The trace scope, or NULL.
+ *
+ * @return NULL if there was no attached trace scope;
+ * the trace scope otherwise.
+ */
+ struct htrace_span *htrace_scope_detach(struct htrace_scope *scope);
+
+ /**
+ * Create a new scope object with the given span.
+ *
+ * @param tracer The htracer to use.
+ * @param span The trace span, or NULL.
+ *
+ * @return NULL if there was no trace span;
+ * the trace scope otherwise.
+ */
+ struct htrace_scope* htrace_restart_span(struct htracer *tracer,
+ struct htrace_span *span);
+
+ /**
+ * Get the span id of an HTrace scope.
+ *
+ * @param scope The trace scope, or NULL.
+ *
+ * @return The span ID of the trace span, or 0 if there is no trace
+ * span inside the scope, or if NULL was passed.
+ */
+ uint64_t htrace_scope_get_span_id(const struct htrace_scope *scope);
+
+ /**
+ * Close a trace scope.
+ *
+ * This must be called from the same thread that the trace scope was created
+ * in.
+ *
+ * @param scope The trace scope to close. You may pass NULL here
+ * with no harmful effects-- it will be ignored.
+ * If there is a span associated with the trace scope,
+ * it will be sent to the relevant span receiver.
+ * Then the scope and the span will be freed.
+ */
+ void htrace_scope_close(struct htrace_scope *scope);
+
+#pragma GCC visibility pop // End publicly visible symbols
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+// vim: ts=4:sw=4:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/core/htrace.hpp
----------------------------------------------------------------------
diff --git a/htrace-c/src/core/htrace.hpp b/htrace-c/src/core/htrace.hpp
new file mode 100644
index 0000000..e4a87cb
--- /dev/null
+++ b/htrace-c/src/core/htrace.hpp
@@ -0,0 +1,239 @@
+/**
+ * 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_HTRACE_HPP
+#define APACHE_HTRACE_HTRACE_HPP
+
+#include "htrace.h"
+
+#include <string>
+
+/**
+ * The public C++ API for the HTrace native client.
+ *
+ * The C++ API is a wrapper around the C API. The advantage of this is that we
+ * can change the C++ API in this file without breaking binary compatibility.
+ *
+ * EXCEPTIONS
+ * We do not use exceptions in this API. This code should be usable by
+ * libraries and applications that are using the Google C++ coding style.
+ * The one case where we do use exceptions is to translate NULL pointer returns
+ * on OOM into std::bad_alloc exceptions. In general, it is extremely unlikely
+ * that the size of memory allocations we are doing will produce OOM. Most
+ * C++ programs do not attempt to handle OOM anyway because of the extra code
+ * complexity that would be required. So translating this into an exception is
+ * fine.
+ *
+ * C++11
+ * This code should not require C++11. We might add #ifdefs later to take
+ * advantage of certain C++11 or later features if they are available.
+ */
+
+namespace htrace {
+ class Sampler;
+ class Scope;
+ class Tracer;
+
+ /**
+ * An HTrace Configuration object.
+ *
+ * Configurations are thread-safe. They can be used by multiple threads
+ * simultaneously.
+ */
+ class Conf {
+ public:
+ /**
+ * Create a new HTrace Conf.
+ *
+ * @param values A configuration string containing a series of
+ * semicolon-separated key=value entries.
+ * We do not hold on to a reference to this string.
+ * @param defaults Another semicolon-separated set of key=value entries.
+ * The defaults to be used when there is no corresponding
+ * value in 'values.' We do not hold on to a reference to
+ * this string.
+ */
+ Conf(const char *values)
+ : conf_(htrace_conf_from_str(values))
+ {
+ if (!conf_) {
+ throw std::bad_alloc();
+ }
+ }
+
+ Conf(const std::string &values)
+ : conf_(htrace_conf_from_str(values.c_str()))
+ {
+ if (!conf_) {
+ throw std::bad_alloc();
+ }
+ }
+
+ ~Conf() {
+ htrace_conf_free(conf_);
+ conf_ = NULL;
+ }
+
+ private:
+ friend class Tracer;
+ friend class Sampler;
+ Conf &operator=(Conf &other); // Can't copy
+ Conf(Conf &other);
+ struct htrace_conf *conf_;
+ };
+
+ /**
+ * An HTrace context object.
+ *
+ * Contexts are thread-safe. They can be used by multiple threads simultaneoy
+ * Most applications will not need more than one HTrace context, which is
+ * often global (or at least widely used.)
+ */
+ class Tracer {
+ public:
+ /**
+ * Create a new Tracer.
+ *
+ * @param name The name of the tracer to create. We do not hold on to a
+ * reference to this string.
+ * @param conf The configuration to use for the new tracer. We do not
+ * hold on to a reference to this configuration.
+ */
+ Tracer(const std::string &name, const Conf &conf)
+ : tracer_(htracer_create(name.c_str(), conf.conf_))
+ {
+ if (!tracer_) {
+ throw std::bad_alloc();
+ }
+ }
+
+ std::string Name() {
+ return std::string(htracer_tname(tracer_));
+ }
+
+ /**
+ * Free the Tracer.
+ *
+ * This destructor must not be called until all the other objects which hold
+ * a reference (such as samplers and trace scopes) are freed. It is often
+ * not necessary to destroy this object at all unless you are writing a
+ * library and want to support unloading your library, or you are writing an
+ * application and want to support some kind of graceful shutdown.
+ *
+ * We could make this friendlier with some kind of reference counting via
+ * atomic variables, but only at the cost of reduced performance.
+ */
+ ~Tracer() {
+ htracer_free(tracer_);
+ tracer_ = NULL;
+ }
+
+ private:
+ friend class Sampler;
+ friend class Scope;
+ Tracer(const Tracer &other); // Can't copy
+ const Tracer &operator=(const Tracer &other);
+ struct htracer *tracer_;
+ };
+
+ /**
+ * An HTrace sampler.
+ *
+ * Samplers determine when new spans are created.
+ * See htrace.h for more information.
+ *
+ * Samplers are thread-safe. They can be used by multiple threads
+ * simultaneously.
+ */
+ class Sampler {
+ public:
+ /**
+ * Create a new Sampler.
+ *
+ * @param tracer The tracer to use. You must not free this tracer until
+ * after this sampler is freed.
+ * @param conf The configuration to use for the new sampler. We do not
+ * hold on to a reference to this configuration.
+ */
+ Sampler(Tracer *tracer, const Conf &conf)
+ : smp_(htrace_sampler_create(tracer->tracer_, conf.conf_)) {
+ if (!smp_) {
+ throw std::bad_alloc();
+ }
+ }
+
+ /**
+ * Get a description of this Sampler.
+ */
+ std::string ToString() {
+ return std::string(htrace_sampler_to_str(smp_));
+ }
+
+ ~Sampler() {
+ htrace_sampler_free(smp_);
+ smp_ = NULL;
+ }
+
+ private:
+ friend class Tracer;
+ friend class Scope;
+ Sampler(const Sampler &other); // Can't copy
+ const Sampler &operator=(const Sampler &other);
+
+ struct htrace_sampler *smp_;
+ };
+
+ class Scope {
+ public:
+ Scope(Tracer &tracer, const char *name)
+ : scope_(htrace_start_span(tracer.tracer_, NULL, name)) {
+ }
+
+ Scope(Tracer &tracer, const std::string &name)
+ : scope_(htrace_start_span(tracer.tracer_, NULL, name.c_str())) {
+ }
+
+ Scope(Tracer &tracer, Sampler &smp, const char *name)
+ : scope_(htrace_start_span(tracer.tracer_, smp.smp_, name)) {
+ }
+
+ Scope(Tracer &tracer, Sampler &smp, const std::string &name)
+ : scope_(htrace_start_span(tracer.tracer_, smp.smp_, name.c_str())) {
+ }
+
+ ~Scope() {
+ htrace_scope_close(scope_);
+ scope_ = NULL;
+ }
+
+ uint64_t GetSpanId() {
+ return htrace_scope_get_span_id(scope_);
+ }
+
+ private:
+ friend class Tracer;
+ Scope(htrace::Scope &other); // Can't copy
+ Scope& operator=(Scope &scope); // Can't assign
+
+ struct htrace_scope *scope_;
+ };
+}
+
+#endif
+
+// vim: ts=2:sw=2:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/core/htracer.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/core/htracer.c b/htrace-c/src/core/htracer.c
new file mode 100644
index 0000000..3305beb
--- /dev/null
+++ b/htrace-c/src/core/htracer.c
@@ -0,0 +1,167 @@
+/**
+ * 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/scope.h"
+#include "core/span.h"
+#include "receiver/receiver.h"
+#include "util/log.h"
+#include "util/process_id.h"
+#include "util/rand.h"
+#include "util/string.h"
+
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * @file htracer.c
+ *
+ * Implementation of the Tracer object.
+ */
+
+struct htracer *htracer_create(const char *tname,
+ const struct htrace_conf *cnf)
+{
+ struct htracer *tracer;
+ int ret;
+
+ tracer = calloc(1, sizeof(*tracer));
+ if (!tracer) {
+ return NULL;
+ }
+ tracer->lg = htrace_log_alloc(cnf);
+ if (!tracer->lg) {
+ free(tracer);
+ return NULL;
+ }
+ ret = pthread_key_create(&tracer->tls, NULL);
+ if (ret) {
+ htrace_log(tracer->lg, "htracer_create: pthread_key_create "
+ "failed: %s.\n", terror(ret));
+ htrace_log_free(tracer->lg);
+ return NULL;
+ }
+ tracer->tname = strdup(tname);
+ if (!tracer->tname) {
+ htrace_log(tracer->lg, "htracer_create: failed to "
+ "duplicate name string.\n");
+ htracer_free(tracer);
+ return NULL;
+ }
+ tracer->prid = calculate_process_id(tracer->lg,
+ htrace_conf_get(cnf, HTRACE_PROCESS_ID), tname);
+ if (!tracer->prid) {
+ htrace_log(tracer->lg, "htracer_create: failed to "
+ "create process id string.\n");
+ htracer_free(tracer);
+ return NULL;
+ }
+ if (!validate_json_string(tracer->lg, tracer->prid)) {
+ htrace_log(tracer->lg, "htracer_create: process ID string '%s' is "
+ "problematic.\n", tracer->prid);
+ htracer_free(tracer);
+ return NULL;
+ }
+ tracer->rnd = random_src_alloc(tracer->lg);
+ if (!tracer->rnd) {
+ htrace_log(tracer->lg, "htracer_create: failed to "
+ "allocate a random source.\n");
+ htracer_free(tracer);
+ return NULL;
+ }
+ tracer->rcv = htrace_rcv_create(tracer, cnf);
+ if (!tracer->rcv) {
+ htrace_log(tracer->lg, "htracer_create: failed to "
+ "create a receiver.\n");
+ htracer_free(tracer);
+ return NULL;
+ }
+ return tracer;
+}
+
+const char *htracer_tname(const struct htracer *tracer)
+{
+ return tracer->tname;
+}
+
+void htracer_free(struct htracer *tracer)
+{
+ struct htrace_rcv *rcv;
+
+ if (!tracer) {
+ return;
+ }
+ pthread_key_delete(tracer->tls);
+ rcv = tracer->rcv;
+ if (rcv) {
+ rcv->ty->free(rcv);
+ }
+ random_src_free(tracer->rnd);
+ free(tracer->tname);
+ free(tracer->prid);
+ htrace_log_free(tracer->lg);
+ free(tracer);
+}
+
+struct htrace_scope *htracer_cur_scope(struct htracer *tracer)
+{
+ return pthread_getspecific(tracer->tls);
+}
+
+int htracer_push_scope(struct htracer *tracer, struct htrace_scope *cur,
+ struct htrace_scope *next)
+{
+ int ret;
+ next->parent = cur;
+ ret = pthread_setspecific(tracer->tls, next);
+ if (ret) {
+ htrace_log(tracer->lg, "htracer_push_scope: pthread_setspecific "
+ "failed: %s\n", terror(ret));
+ return EIO;
+ }
+ return 0;
+}
+
+int htracer_pop_scope(struct htracer *tracer, struct htrace_scope *scope)
+{
+ struct htrace_scope *cur_scope;
+ int ret;
+
+ cur_scope = pthread_getspecific(tracer->tls);
+ if (cur_scope != scope) {
+ htrace_log(tracer->lg, "htracer_pop_scope: attempted to pop a scope "
+ "that wasn't the top of the stack. Current top of stack: "
+ "%s. Attempted to pop: %s.\n",
+ (cur_scope->span ? cur_scope->span->desc : "(detached)"),
+ (scope->span ? scope->span->desc : "(detached)"));
+ return EIO;
+ }
+ ret = pthread_setspecific(tracer->tls, scope->parent);
+ if (ret) {
+ htrace_log(tracer->lg, "htracer_pop_scope: pthread_setspecific "
+ "failed: %s\n", terror(ret));
+ return EIO;
+ }
+ return 0;
+}
+
+// vim:ts=4:sw=4:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/core/htracer.h
----------------------------------------------------------------------
diff --git a/htrace-c/src/core/htracer.h b/htrace-c/src/core/htracer.h
new file mode 100644
index 0000000..2acdf70
--- /dev/null
+++ b/htrace-c/src/core/htracer.h
@@ -0,0 +1,101 @@
+/**
+ * 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_CORE_TRACER_H
+#define APACHE_HTRACE_CORE_TRACER_H
+
+#include <pthread.h> /* for pthread_key_t */
+
+/**
+ * @file tracer.h
+ *
+ * The HTracer object.
+ *
+ * This is an internal header, not intended for external use.
+ */
+
+struct htrace_log;
+struct htrace_rcv;
+struct random_src;
+
+struct htracer {
+ /**
+ * Key for thread-local data.
+ */
+ pthread_key_t tls;
+
+ /**
+ * The htrace log to use.
+ */
+ struct htrace_log *lg;
+
+ /**
+ * The name of this tracer.
+ */
+ char *tname;
+
+ /**
+ * The process id of this context.
+ */
+ char *prid;
+
+ /**
+ * The random source to use in this context.
+ */
+ struct random_src *rnd;
+
+ /**
+ * The span receiver to use.
+ */
+ struct htrace_rcv *rcv;
+};
+
+/**
+ * Get the current scope in a given context.
+ *
+ * @param tracer The context.
+ *
+ * @return The current scope, or NULL if there is none.
+ */
+struct htrace_scope *htracer_cur_scope(struct htracer *tracer);
+
+/**
+ * Push another scope on to the current context.
+ *
+ * @param tracer The context.
+ * @param cur The current scope on the context.
+ * @param next The scope to push.
+ *
+ * @return 0 on success; nonzero otherwise.
+ */
+int htracer_push_scope(struct htracer *tracer, struct htrace_scope *cur,
+ struct htrace_scope *next);
+
+/**
+ * Pop a scope from the current context.
+ *
+ * @param tracer The context.
+ * @param scope The scope which should be the top of the stack.
+ *
+ * @return 0 on success; nonzero otherwise.
+ */
+int htracer_pop_scope(struct htracer *tracer, struct htrace_scope *scope);
+
+#endif
+
+// vim: ts=4: sw=4: et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/core/scope.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/core/scope.c b/htrace-c/src/core/scope.c
new file mode 100644
index 0000000..93008cb
--- /dev/null
+++ b/htrace-c/src/core/scope.c
@@ -0,0 +1,166 @@
+/**
+ * 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 "core/htracer.h"
+#include "core/scope.h"
+#include "core/span.h"
+#include "receiver/receiver.h"
+#include "sampler/sampler.h"
+#include "util/log.h"
+#include "util/rand.h"
+#include "util/string.h"
+#include "util/time.h"
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * @file scope.c
+ *
+ * Implementation of HTrace scopes.
+ */
+
+struct htrace_scope* htrace_start_span(struct htracer *tracer,
+ struct htrace_sampler *sampler, const char *desc)
+{
+ struct htrace_scope *cur_scope, *scope = NULL, *pscope;
+ struct htrace_span *span = NULL;
+ uint64_t span_id;
+
+ // Validate the description string. This ensures that it doesn't have
+ // anything silly in it like embedded double quotes, backslashes, or control
+ // characters.
+ if (!validate_json_string(tracer->lg, desc)) {
+ htrace_log(tracer->lg, "htrace_span_alloc(desc=%s): invalid "
+ "description string.\n", desc);
+ return NULL;
+ }
+ cur_scope = htracer_cur_scope(tracer);
+ if (!cur_scope) {
+ if (!sampler->ty->next(sampler)) {
+ return NULL;
+ }
+ }
+ do {
+ span_id = random_u64(tracer->rnd);
+ } while (span_id == 0);
+ span = htrace_span_alloc(desc, now_ms(tracer->lg), span_id);
+ if (!span) {
+ htrace_log(tracer->lg, "htrace_span_alloc(desc=%s): OOM\n", desc);
+ return NULL;
+ }
+ scope = malloc(sizeof(*scope));
+ if (!scope) {
+ htrace_span_free(span);
+ htrace_log(tracer->lg, "htrace_start_span(desc=%s): OOM\n", desc);
+ return NULL;
+ }
+ scope->tracer = tracer;
+ scope->span = span;
+
+ // Search enclosing trace scopes for the first one that hasn't disowned
+ // its trace span.
+ for (pscope = cur_scope; pscope; pscope = pscope->parent) {
+ struct htrace_span *pspan = pscope->span;
+ if (pspan) {
+ span->parent.single = pspan->span_id;
+ span->num_parents = 1;
+ break;
+ }
+ pscope = pscope->parent;
+ }
+ if (htracer_push_scope(tracer, cur_scope, scope) != 0) {
+ htrace_span_free(span);
+ free(scope);
+ return NULL;
+ }
+ return scope;
+}
+
+struct htrace_span *htrace_scope_detach(struct htrace_scope *scope)
+{
+ struct htrace_span *span = scope->span;
+
+ if (span == NULL) {
+ htrace_log(scope->tracer->lg, "htrace_scope_detach: attempted to "
+ "detach a scope which was already detached.\n");
+ return NULL;
+ }
+ scope->span = NULL;
+ return span;
+}
+
+struct htrace_scope* htrace_restart_span(struct htracer *tracer,
+ struct htrace_span *span)
+{
+ struct htrace_scope *cur_scope, *scope = NULL;
+
+ scope = malloc(sizeof(*scope));
+ if (!scope) {
+ htrace_span_free(span);
+ htrace_log(tracer->lg, "htrace_start_span(desc=%s, parent_id=%016"PRIx64
+ "): OOM\n", span->desc, span->span_id);
+ return NULL;
+ }
+ scope->tracer = tracer;
+ scope->parent = NULL;
+ scope->span = span;
+ cur_scope = htracer_cur_scope(tracer);
+ if (htracer_push_scope(tracer, cur_scope, scope) != 0) {
+ htrace_span_free(span);
+ free(scope);
+ return NULL;
+ }
+ return scope;
+}
+
+uint64_t htrace_scope_get_span_id(const struct htrace_scope *scope)
+{
+ struct htrace_span *span;
+
+ if (!scope) {
+ return 0;
+ }
+ span = scope->span;
+ return span ? span->span_id : 0;
+}
+
+void htrace_scope_close(struct htrace_scope *scope)
+{
+ struct htracer *tracer;
+
+ if (!scope) {
+ return;
+ }
+ tracer = scope->tracer;
+ if (htracer_pop_scope(tracer, scope) == 0) {
+ struct htrace_span *span = scope->span;
+ if (span) {
+ struct htrace_rcv *rcv = tracer->rcv;
+ span->end_ms = now_ms(tracer->lg);
+ rcv->ty->add_span(rcv, span);
+ htrace_span_free(span);
+ }
+ free(scope);
+ }
+}
+
+// vim:ts=4:sw=4:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/core/scope.h
----------------------------------------------------------------------
diff --git a/htrace-c/src/core/scope.h b/htrace-c/src/core/scope.h
new file mode 100644
index 0000000..f76cd42
--- /dev/null
+++ b/htrace-c/src/core/scope.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_SCOPE_H
+#define APACHE_HTRACE_SCOPE_H
+
+/**
+ * @file scope.h
+ *
+ * Functions related to trace scopes.
+ *
+ * This is an internal header, not intended for external use.
+ */
+
+#include <stdint.h>
+
+/**
+ * A trace scope.
+ *
+ * Currently, trace scopes contain span data (there is no separate object for
+ * the span data.)
+ */
+struct htrace_scope {
+ /**
+ * The HTracer object associated with this scope. Cannot be NULL.
+ * This memory is managed externally from the htrace_scope object.
+ */
+ struct htracer *tracer;
+
+ /**
+ * The parent scope, or NULL if this is a top-level scope.
+ */
+ struct htrace_scope *parent;
+
+ /**
+ * The span object associated with this scope, or NULL if there is none.
+ */
+ struct htrace_span *span;
+};
+
+#endif
+
+// vim: ts=4:sw=4:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/core/span.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/core/span.c b/htrace-c/src/core/span.c
new file mode 100644
index 0000000..13ba3cf
--- /dev/null
+++ b/htrace-c/src/core/span.c
@@ -0,0 +1,187 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "core/span.h"
+#include "receiver/receiver.h"
+#include "sampler/sampler.h"
+#include "util/log.h"
+#include "util/rand.h"
+#include "util/string.h"
+#include "util/time.h"
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * @file span.c
+ *
+ * Implementation of HTrace spans.
+ */
+
+struct htrace_span *htrace_span_alloc(const char *desc,
+ uint64_t begin_ms, uint64_t span_id)
+{
+ struct htrace_span *span;
+
+ span = malloc(sizeof(*span));
+ if (!span) {
+ return NULL;
+ }
+ span->desc = strdup(desc);
+ if (!span->desc) {
+ free(span);
+ return NULL;
+ }
+ span->begin_ms = begin_ms;
+ span->end_ms = 0;
+ span->span_id = span_id;
+ span->prid = NULL;
+ span->num_parents = 0;
+ span->parent.single = 0;
+ span->parent.list = NULL;
+ return span;
+}
+
+void htrace_span_free(struct htrace_span *span)
+{
+ if (!span) {
+ return;
+ }
+ free(span->desc);
+ free(span->prid);
+ if (span->num_parents > 1) {
+ free(span->parent.list);
+ }
+ free(span);
+}
+
+static int compare_spanids(const void *va, const void *vb)
+{
+ uint64_t a = *((uint64_t*)va);
+ uint64_t b = *((uint64_t*)vb);
+ if (a < b) {
+ return -1;
+ } else if (a > b) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+void htrace_span_sort_and_dedupe_parents(struct htrace_span *span)
+{
+ int i, j, num_parents = span->num_parents;
+ uint64_t prev;
+
+ if (num_parents <= 1) {
+ return;
+ }
+ qsort(span->parent.list, num_parents, sizeof(uint64_t), compare_spanids);
+ prev = span->parent.list[0];
+ j = 1;
+ for (i = 1; i < num_parents; i++) {
+ uint64_t id = span->parent.list[i];
+ if (id != prev) {
+ span->parent.list[j++] = span->parent.list[i];
+ prev = id;
+ }
+ }
+ span->num_parents = j;
+ if (j == 1) {
+ // After deduplication, there is now only one entry. Switch to the
+ // optimized no-malloc representation for 1 entry.
+ free(span->parent.list);
+ span->parent.single = prev;
+ } else if (j != num_parents) {
+ // After deduplication, there are now fewer entries. Use realloc to
+ // shrink the size of our dynamic allocation if possible.
+ uint64_t *nlist = realloc(span->parent.list, sizeof(uint64_t) * j);
+ if (nlist) {
+ span->parent.list = nlist;
+ }
+ }
+}
+
+/**
+ * Translate the span to a JSON string.
+ *
+ * This function can be called in two ways. With buf == NULL, we will determine
+ * the size of the buffer that would be required to hold a JSON string
+ * containing the span contents. With buf non-NULL, we will write the span
+ * contents to the provided buffer.
+ *
+ * @param scope The scope
+ * @param max The maximum number of bytes to write to buf.
+ * @param buf If non-NULL, where the string will be written.
+ *
+ * @return The number of bytes that the span json would take
+ * up if it were written out.
+ */
+static int span_json_sprintf_impl(const struct htrace_span *span,
+ int max, char *buf)
+{
+ int num_parents, i, ret = 0;
+ const char *prefix = "";
+
+ // Note that we have validated the description and process ID strings to
+ // make sure they don't contain anything evil. So we don't need to escape
+ // them here.
+
+ ret += fwdprintf(&buf, &max, "{\"s\":\"%016" PRIx64 "\",\"b\":%" PRId64
+ ",\"e\":%" PRId64",", span->span_id, span->begin_ms,
+ span->end_ms);
+ if (span->desc) {
+ ret += fwdprintf(&buf, &max, "\"d\":\"%s\",", span->desc);
+ }
+ if (span->prid) {
+ ret += fwdprintf(&buf, &max, "\"r\":\"%s\",", span->prid);
+ }
+ num_parents = span->num_parents;
+ if (num_parents == 0) {
+ ret += fwdprintf(&buf, &max, "\"p\":[]");
+ } else if (num_parents == 1) {
+ ret += fwdprintf(&buf, &max, "\"p\":[\"%016"PRIx64"\"]",
+ span->parent.single);
+ } else if (num_parents > 1) {
+ ret += fwdprintf(&buf, &max, "\"p\":[");
+ for (i = 0; i < num_parents; i++) {
+ ret += fwdprintf(&buf, &max, "%s\"%016" PRIx64 "\"", prefix,
+ span->parent.list[i]);
+ prefix = ",";
+ }
+ ret += fwdprintf(&buf, &max, "]");
+ }
+ ret += fwdprintf(&buf, &max, "}");
+ // Add one to 'ret' to take into account the terminating null that we
+ // need to write.
+ return ret + 1;
+}
+
+int span_json_size(const struct htrace_span *scope)
+{
+ return span_json_sprintf_impl(scope, 0, NULL);
+}
+
+void span_json_sprintf(const struct htrace_span *scope, int max, void *buf)
+{
+ span_json_sprintf_impl(scope, max, buf);
+}
+
+// vim:ts=4:sw=4:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/core/span.h
----------------------------------------------------------------------
diff --git a/htrace-c/src/core/span.h b/htrace-c/src/core/span.h
new file mode 100644
index 0000000..b19bd94
--- /dev/null
+++ b/htrace-c/src/core/span.h
@@ -0,0 +1,138 @@
+/**
+ * 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_SPAN_H
+#define APACHE_HTRACE_SPAN_H
+
+/**
+ * @file span.h
+ *
+ * Functions related to HTrace spans and trace scopes.
+ *
+ * This is an internal header, not intended for external use.
+ */
+
+#include <stdint.h>
+
+struct htracer;
+
+struct htrace_span {
+ /**
+ * The name of this trace scope.
+ * Dynamically allocated. Will never be NULL.
+ */
+ char *desc;
+
+ /**
+ * The beginning time in wall-clock milliseconds.
+ */
+ uint64_t begin_ms;
+
+ /**
+ * The end time in wall-clock milliseconds.
+ */
+ uint64_t end_ms;
+
+ /**
+ * The span id.
+ */
+ uint64_t span_id;
+
+ /**
+ * The process ID of this trace scope.
+ * Dynamically allocated. May be null.
+ */
+ char *prid;
+
+ /**
+ * The number of parents.
+ */
+ int num_parents;
+
+ union {
+ /**
+ * If there is 1 parent, this is the parent ID.
+ */
+ uint64_t single;
+
+ /**
+ * If there are multiple parents, this is a pointer to a dynamically
+ * allocated array of parent IDs.
+ */
+ uint64_t *list;
+ } parent;
+};
+
+/**
+ * Allocate an htrace span.
+ *
+ * @param desc The span name to use. Will be deep-copied.
+ * @param begin_ms The value to use for begin_ms.
+ * @param span_id The span ID to use.
+ *
+ * @return NULL on OOM; the span otherwise.
+ */
+struct htrace_span *htrace_span_alloc(const char *desc,
+ uint64_t begin_ms, uint64_t span_id);
+
+/**
+ * Free the memory associated with an htrace span.
+ *
+ * @param span The span to free.
+ */
+void htrace_span_free(struct htrace_span *span);
+
+/**
+ * Sort and deduplicate the parents array within the span.
+ *
+ * @param span The span to process.
+ */
+void htrace_span_sort_and_dedupe_parents(struct htrace_span *span);
+
+/**
+ * Escape a JSON string. Specifically, put backslashes before double quotes and
+ * other backslashes.
+ *
+ * @param in The string to escape.
+ *
+ * @param out The escaped string. Malloced. NULL on OOM.
+ */
+char *json_escape(const char *in);
+
+/**
+ * Get the buffer size that would be needed to serialize this span to a buffer.
+ *
+ * @param span The span.
+ *
+ * @return The buffer size in bytes. This will never be less
+ * than 1.
+ */
+int span_json_size(const struct htrace_span *span);
+
+/**
+ * Get the buffer size that would be needed to serialize this span to a buffer.
+ *
+ * @param span The span.
+ *
+ * @return The buffer size in bytes.
+ */
+void span_json_sprintf(const struct htrace_span *span, int max, void *buf);
+
+#endif
+
+// vim: ts=4:sw=4:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/receiver/curl.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/receiver/curl.c b/htrace-c/src/receiver/curl.c
new file mode 100644
index 0000000..0905dd4
--- /dev/null
+++ b/htrace-c/src/receiver/curl.c
@@ -0,0 +1,124 @@
+/**
+ * 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 "util/log.h"
+
+#include <curl/curl.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * Unfortunately, libcurl requires a non-threadsafe initialization function to
+ * be called before it is usable. This is unfortunate for a library like
+ * libhtrace, which is designed to be used in a multi-threaded context.
+ *
+ * This mutex protects us against an application creating two htraced receivers
+ * at around the same time, and calling that non-threadsafe initialization
+ * function.
+ *
+ * Of course, this doesn't protect us against the application also initializing
+ * libcurl. We can protect against that by statically linking a private copy of
+ * libcurl into libhtrace, so that we will be initializing and using our own
+ * private copy of libcurl rather than the application's.
+ */
+static pthread_mutex_t g_curl_refcnt_lock = PTHREAD_MUTEX_INITIALIZER;
+
+/**
+ * The current number of CURL handles that are open.
+ */
+static int64_t g_curl_refcnt;
+
+static int curl_addref(struct htrace_log *lg)
+{
+ int success = 0;
+ CURLcode curl_err = 0;
+
+ pthread_mutex_lock(&g_curl_refcnt_lock);
+ if (g_curl_refcnt >= 1) {
+ g_curl_refcnt++;
+ success = 1;
+ goto done;
+ }
+ curl_err = curl_global_init(CURL_GLOBAL_ALL);
+ if (curl_err) {
+ htrace_log(lg, "curl_global_init failed: error %d (%s)\n",
+ curl_err, curl_easy_strerror(curl_err));
+ goto done;
+ }
+ htrace_log(lg, "successfully initialized libcurl...\n");
+ g_curl_refcnt = 1;
+ success = 1;
+
+done:
+ pthread_mutex_unlock(&g_curl_refcnt_lock);
+ return success;
+}
+
+static void curl_unref(struct htrace_log *lg)
+{
+ pthread_mutex_lock(&g_curl_refcnt_lock);
+ g_curl_refcnt--;
+ if (g_curl_refcnt > 0) {
+ goto done;
+ }
+ curl_global_cleanup();
+ htrace_log(lg, "shut down libcurl...\n");
+done:
+ pthread_mutex_unlock(&g_curl_refcnt_lock);
+}
+
+CURL* htrace_curl_init(struct htrace_log *lg, const struct htrace_conf *conf)
+{
+ CURL *curl = NULL;
+ int success = 0;
+
+ if (!curl_addref(lg)) {
+ return NULL;
+ }
+ curl = curl_easy_init();
+ if (!curl) {
+ htrace_log(lg, "curl_easy_init failed.\n");
+ goto done;
+ }
+ success = 1;
+
+done:
+ if (!success) {
+ if (curl) {
+ curl_easy_cleanup(curl);
+ }
+ curl_unref(lg);
+ return NULL;
+ }
+ return curl;
+}
+
+void htrace_curl_free(struct htrace_log *lg, CURL *curl)
+{
+ if (!curl) {
+ return;
+ }
+ curl_easy_cleanup(curl);
+ curl_unref(lg);
+}
+
+// vim:ts=4:sw=4:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/receiver/curl.h
----------------------------------------------------------------------
diff --git a/htrace-c/src/receiver/curl.h b/htrace-c/src/receiver/curl.h
new file mode 100644
index 0000000..9249cd3
--- /dev/null
+++ b/htrace-c/src/receiver/curl.h
@@ -0,0 +1,60 @@
+/**
+ * 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_CURL_H
+#define APACHE_HTRACE_RECEIVER_CURL_H
+
+/**
+ * @file curl.h
+ *
+ * Utility functions wrapping libcurl.
+ *
+ * This is an internal header, not intended for external use.
+ */
+
+#include <curl/curl.h> // for the CURL type
+
+struct htrace_conf;
+struct htrace_log;
+
+/**
+ * Initialize a libcurl handle.
+ *
+ * This function also takes care of calling curl_global_init if necessary.
+ *
+ * @param lg The HTrace log to use for error messages.
+ * @param conf The HTrace configuration to use.
+ *
+ * @return A libcurl handle, or NULL on failure.
+ */
+CURL* htrace_curl_init(struct htrace_log *lg, const struct htrace_conf *conf);
+
+/**
+ * Free a libcurl handle.
+ *
+ * This function also takes care of calling curl_global_cleanup if necessary.
+ *
+ * @param lg The HTrace log to use for error messages.
+ *
+ * @param curl The libcurl handle.
+ */
+void htrace_curl_free(struct htrace_log *lg, CURL *curl);
+
+#endif
+
+// vim: ts=4: sw=4: et
[3/4] incubator-htrace git commit: HTRACE-106: htrace: add C / C++
native client (cmccabe)
Posted by cm...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/receiver/htraced.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/receiver/htraced.c b/htrace-c/src/receiver/htraced.c
new file mode 100644
index 0000000..ea61541
--- /dev/null
+++ b/htrace-c/src/receiver/htraced.c
@@ -0,0 +1,649 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "core/conf.h"
+#include "core/htrace.h"
+#include "core/htracer.h"
+#include "core/span.h"
+#include "receiver/curl.h"
+#include "receiver/receiver.h"
+#include "test/test.h"
+#include "util/log.h"
+#include "util/time.h"
+
+#include <errno.h>
+#include <curl/curl.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * @file htraced.c
+ *
+ * The htraced span receiver which implements sending spans on to htraced.
+ */
+
+/**
+ * The minimum buffer size to allow for the htraced circular buffer.
+ *
+ * This should hopefully allow at least a few spans to be buffered.
+ */
+#define HTRACED_MIN_BUFFER_SIZE (4ULL * 1024ULL * 1024ULL)
+
+/**
+ * The maximum buffer size to allow for the htraced circular buffer.
+ * This is mainly to avoid overflow. Of course, you couldn't allocate a buffer
+ * anywhere near this size anyway.
+ */
+#define HTRACED_MAX_BUFFER_SIZE 0x7ffffffffffffffLL
+
+/**
+ * The minimum number of milliseconds to allow for send_timeo_ms.
+ */
+#define HTRACED_SEND_TIMEO_MS_MIN 30000LL
+
+/**
+ * The maximum number of milliseconds to allow for send_timeo_ms.
+ * This is mainly to avoid overflow.
+ */
+#define HTRACED_SEND_TIMEO_MS_MAX 86400000LL
+
+/**
+ * The maximum size of the message we will send over the wire.
+ * This also sets the size of the transmission buffer.
+ * This constant must not be more than 2^^32 on 32-bit systems.
+ */
+#define HTRACED_MAX_MSG_LEN (8ULL * 1024ULL * 1024ULL)
+
+/**
+ * The maximum number of times we will try to add a span to the circular buffer
+ * before giving up.
+ */
+#define HTRACED_MAX_ADD_TRIES 3
+
+/**
+ * The maximum number of times to try to send some spans to the htraced daemon
+ * before giving up.
+ */
+#define HTRACED_MAX_SEND_TRIES 3
+
+/**
+ * The number of milliseconds to sleep after failing to send some spans to the
+ * htraced daemon.
+ */
+#define HTRACED_SEND_RETRY_SLEEP_MS 5000
+
+/*
+ * A span receiver that writes spans to htraced.
+ */
+struct htraced_rcv {
+ struct htrace_rcv base;
+
+ /**
+ * Nonzero if the receiver should shut down.
+ */
+ int shutdown;
+
+ /**
+ * The htracer object associated with this receciver.
+ */
+ struct htracer *tracer;
+
+ /**
+ * The HTraced server URL.
+ * Dynamically allocated.
+ */
+ char *url;
+
+ /**
+ * Buffered span data becomes eligible to be sent even if there isn't much
+ * in the buffer after this timeout elapses.
+ */
+ uint64_t send_timeo_ms;
+
+ /**
+ * The maximum number of bytes we will buffer before waking the sending
+ * thread. We may sometimes send slightly more than this amount if the
+ * thread takes a while to wake up.
+ */
+ uint64_t send_threshold;
+
+ /**
+ * The CURL handle.
+ */
+ CURL *curl;
+
+ /**
+ * Length of the circular buffer.
+ */
+ uint64_t clen;
+
+ /**
+ * A circular buffer containing span data.
+ */
+ uint8_t *cbuf;
+
+ /**
+ * 'start' pointer of the circular buffer.
+ */
+ uint64_t cstart;
+
+ /**
+ * 'end' pointer of the circular buffer.
+ */
+ uint64_t cend;
+
+ /**
+ * The monotonic-clock time at which we last did a send operation.
+ */
+ uint64_t last_send_ms;
+
+ /**
+ * RPC messages are copied into this buffer before being sent.
+ * Its length is HTRACED_MAX_MSG_LEN.
+ */
+ uint8_t *sbuf;
+
+ /**
+ * Lock protecting the circular buffer from concurrent writes.
+ */
+ pthread_mutex_t lock;
+
+ /**
+ * Condition variable used to wake up the background thread.
+ */
+ pthread_cond_t cond;
+
+ /**
+ * Background transmitter thread.
+ */
+ pthread_t xmit_thread;
+};
+
+void* run_htraced_xmit_manager(void *data);
+static int should_xmit(struct htraced_rcv *rcv, uint64_t now);
+static void htraced_xmit(struct htraced_rcv *rcv, uint64_t now);
+static uint64_t cbuf_used(const struct htraced_rcv *rcv);
+static int32_t cbuf_to_sbuf(struct htraced_rcv *rcv);
+
+static struct htrace_rcv *htraced_rcv_create(struct htracer *tracer,
+ const struct htrace_conf *conf)
+{
+ struct htraced_rcv *rcv;
+ const char *url;
+ int ret;
+
+ url = htrace_conf_get(conf, HTRACED_ADDRESS_KEY);
+ if (!url) {
+ htrace_log(tracer->lg, "htraced_rcv_create: no value found for %s. "
+ "You must set this configuration key to the "
+ "hostname:port identifying the htraced server.\n",
+ HTRACED_ADDRESS_KEY);
+ goto error;
+ }
+ rcv = malloc(sizeof(*rcv));
+ if (!rcv) {
+ htrace_log(tracer->lg, "htraced_rcv_create: OOM while "
+ "allocating htraced_rcv.\n");
+ goto error;
+ }
+ rcv->base.ty = &g_htraced_rcv_ty;
+ rcv->shutdown = 0;
+ rcv->tracer = tracer;
+ if (asprintf(&rcv->url, "%s/writeSpans", url) < 0) {
+ rcv->url = NULL;
+ goto error_free_rcv;
+ }
+ rcv->send_timeo_ms = htrace_conf_get_u64(tracer->lg, conf,
+ HTRACED_SEND_TIMEOUT_MS_KEY);
+ if (rcv->send_timeo_ms < HTRACED_SEND_TIMEO_MS_MIN) {
+ htrace_log(tracer->lg, "htraced_rcv_create: invalid send timeout of %"
+ PRId64 " ms. Setting the minimum timeout of %lld"
+ " ms instead.\n", rcv->send_timeo_ms, HTRACED_SEND_TIMEO_MS_MIN);
+ rcv->send_timeo_ms = HTRACED_SEND_TIMEO_MS_MIN;
+ } else if (rcv->send_timeo_ms > HTRACED_SEND_TIMEO_MS_MAX) {
+ htrace_log(tracer->lg, "htraced_rcv_create: invalid send timeout of %"
+ PRId64 " ms. Setting the maximum timeout of %lld"
+ " ms instead.\n", rcv->send_timeo_ms, HTRACED_SEND_TIMEO_MS_MAX);
+ rcv->send_timeo_ms = HTRACED_SEND_TIMEO_MS_MAX;
+ }
+ rcv->curl = htrace_curl_init(tracer->lg, conf);
+ if (!rcv->curl) {
+ goto error_free_url;
+ }
+ rcv->clen = htrace_conf_get_u64(tracer->lg, conf, HTRACED_BUFFER_SIZE_KEY);
+ if (rcv->clen < HTRACED_MIN_BUFFER_SIZE) {
+ htrace_log(tracer->lg, "htraced_rcv_create: invalid buffer size %" PRId64
+ ". Setting the minimum buffer size of %llu"
+ " instead.\n", rcv->clen, HTRACED_MIN_BUFFER_SIZE);
+ rcv->clen = HTRACED_MIN_BUFFER_SIZE;
+ } else if (rcv->clen > HTRACED_MAX_BUFFER_SIZE) {
+ htrace_log(tracer->lg, "htraced_rcv_create: invalid buffer size %" PRId64
+ ". Setting the maximum buffer size of %lld"
+ " instead.\n", rcv->clen, HTRACED_MAX_BUFFER_SIZE);
+ rcv->clen = HTRACED_MAX_BUFFER_SIZE;
+ }
+ rcv->cbuf = malloc(rcv->clen);
+ if (!rcv->cbuf) {
+ htrace_log(tracer->lg, "htraced_rcv_create: failed to malloc %"PRId64
+ " bytes for the htraced circular buffer.\n", rcv->clen);
+ goto error_free_curl;
+ }
+ // Send when the buffer gets 1/4 full.
+ rcv->send_threshold = rcv->clen * 0.25;
+ rcv->cstart = 0;
+ rcv->cend = 0;
+ rcv->last_send_ms = monotonic_now_ms(tracer->lg);
+ rcv->sbuf = malloc(HTRACED_MAX_MSG_LEN);
+ if (!rcv->sbuf) {
+ goto error_free_cbuf;
+ }
+ ret = pthread_mutex_init(&rcv->lock, NULL);
+ if (ret) {
+ htrace_log(tracer->lg, "htraced_rcv_create: pthread_mutex_init "
+ "error %d: %s\n", ret, terror(ret));
+ goto error_free_sbuf;
+ }
+ ret = pthread_cond_init(&rcv->cond, NULL);
+ if (ret) {
+ htrace_log(tracer->lg, "htraced_rcv_create: pthread_cond_init "
+ "error %d: %s\n", ret, terror(ret));
+ goto error_free_lock;
+ }
+ ret = pthread_create(&rcv->xmit_thread, NULL, run_htraced_xmit_manager, rcv);
+ if (ret) {
+ htrace_log(tracer->lg, "htraced_rcv_create: failed to create xmit thread: "
+ "error %d: %s\n", ret, terror(ret));
+ goto error_free_cvar;
+ }
+ htrace_log(tracer->lg, "Initialized htraced receiver with url=%s, "
+ "send_timeo_ms=%" PRId64 ", send_threshold=%" PRId64 ", clen=%"
+ PRId64 ".\n", rcv->url, rcv->send_timeo_ms, rcv->send_threshold,
+ rcv->clen);
+ return (struct htrace_rcv*)rcv;
+
+error_free_cvar:
+ pthread_cond_destroy(&rcv->cond);
+error_free_lock:
+ pthread_mutex_destroy(&rcv->lock);
+error_free_sbuf:
+ free(rcv->sbuf);
+error_free_cbuf:
+ free(rcv->cbuf);
+error_free_curl:
+ htrace_curl_free(tracer->lg, rcv->curl);
+error_free_url:
+ free(rcv->url);
+error_free_rcv:
+ free(rcv);
+error:
+ return NULL;
+}
+
+void* run_htraced_xmit_manager(void *data)
+{
+ struct htraced_rcv *rcv = data;
+ struct htrace_log *lg = rcv->tracer->lg;
+ uint64_t now, wakeup;
+ struct timespec wakeup_ts;
+ int ret;
+
+ pthread_mutex_lock(&rcv->lock);
+ while (1) {
+ now = monotonic_now_ms(lg);
+ while (should_xmit(rcv, now)) {
+ htraced_xmit(rcv, now);
+ }
+ if (rcv->shutdown) {
+ while (cbuf_used(rcv) > 0) {
+ htraced_xmit(rcv, now);
+ }
+ break;
+ }
+ // Wait for one of a few things to happen:
+ // * Shutdown
+ // * The wakeup timer to elapse, leading us to check if we should send
+ // because of send_timeo_ms.
+ // * A writer to signal that we should wake up because enough bytes are
+ // buffered.
+ wakeup = now + (rcv->send_timeo_ms / 2);
+ ms_to_timespec(wakeup, &wakeup_ts);
+ ret = pthread_cond_timedwait(&rcv->cond, &rcv->lock, &wakeup_ts);
+ if ((ret != 0) && (ret != ETIMEDOUT)) {
+ htrace_log(lg, "run_htraced_xmit_manager: pthread_cond_timedwait "
+ "error: %d (%s)\n", ret, terror(ret));
+ }
+ }
+ pthread_mutex_unlock(&rcv->lock);
+ htrace_log(lg, "run_htraced_xmit_manager: shutting down the transmission "
+ "manager thread.\n");
+ return NULL;
+}
+
+/**
+ * Determine if the xmit manager should send.
+ * This function must be called with the lock held.
+ *
+ * @param rcv The htraced receiver.
+ * @param now The current time in milliseconds.
+ *
+ * @return nonzero if we should send now.
+ */
+static int should_xmit(struct htraced_rcv *rcv, uint64_t now)
+{
+ uint64_t used;
+
+ used = cbuf_used(rcv);
+ if (used > rcv->send_threshold) {
+ // We have buffered a lot of bytes, so let's send.
+ return 1;
+ }
+ if (now - rcv->last_send_ms > rcv->send_timeo_ms) {
+ // It's been too long since the last transmission, so let's send.
+ if (used > 0) {
+ return 1;
+ }
+ }
+ return 0; // Let's wait.
+}
+
+/**
+ * Send all the spans which we have buffered.
+ *
+ * @param rcv The htraced receiver.
+ * @param slen The length of the buffer to send.
+ *
+ * @return 1 on success; 0 otherwise.
+ */
+static int htraced_xmit_impl(struct htraced_rcv *rcv, int32_t slen)
+{
+ struct htrace_log *lg = rcv->tracer->lg;
+ CURLcode res;
+ char *pid_header = NULL;
+ struct curl_slist *headers = NULL;
+ int ret = 0;
+
+ // Disable the use of SIGALARM to interrupt DNS lookups.
+ curl_easy_setopt(rcv->curl, CURLOPT_NOSIGNAL, 1);
+ // Do not use a global DNS cache.
+ curl_easy_setopt(rcv->curl, CURLOPT_DNS_USE_GLOBAL_CACHE, 0);
+ // Disable verbosity.
+ curl_easy_setopt(rcv->curl, CURLOPT_VERBOSE, 0);
+ // The user agent is libhtraced.
+ curl_easy_setopt(rcv->curl, CURLOPT_USERAGENT, "libhtraced");
+ // Set URL
+ curl_easy_setopt(rcv->curl, CURLOPT_URL, rcv->url);
+ // Set POST
+ curl_easy_setopt(rcv->curl, CURLOPT_POST, 1L);
+ // Set the size that we're copying from rcv->sbuf
+ curl_easy_setopt(rcv->curl, CURLOPT_POSTFIELDSIZE, (long)slen);
+ if (asprintf(&pid_header, "htrace-pid: %s", rcv->tracer->prid) < 0) {
+ htrace_log(lg, "htraced_xmit(%s) failed: OOM allocating htrace-pid\n",
+ rcv->url);
+ goto done;
+ }
+ curl_easy_setopt(rcv->curl, CURLOPT_POSTFIELDS, rcv->sbuf);
+ headers = curl_slist_append(headers, pid_header);
+ if (!headers) {
+ htrace_log(lg, "htraced_xmit(%s) failed: OOM allocating headers\n",
+ rcv->url);
+ return 0;
+ }
+ headers = curl_slist_append(headers, "Content-Type: application/json");
+ if (!headers) {
+ htrace_log(lg, "htraced_xmit(%s) failed: OOM allocating headers\n",
+ rcv->url);
+ return 0;
+ }
+ curl_easy_setopt(rcv->curl, CURLOPT_HTTPHEADER, headers);
+ res = curl_easy_perform(rcv->curl);
+ if (res != CURLE_OK) {
+ htrace_log(lg, "htraced_xmit(%s) failed: error %lld (%s)\n",
+ rcv->url, (long long)res, curl_easy_strerror(res));
+ }
+ ret = res == CURLE_OK;
+done:
+ curl_easy_reset(rcv->curl);
+ free(pid_header);
+ curl_slist_free_all(headers);
+ return ret;
+}
+
+static void htraced_xmit(struct htraced_rcv *rcv, uint64_t now)
+{
+ int32_t slen;
+ int tries = 0;
+
+ // Move span data from the circular buffer into the transmission buffer.
+ slen = cbuf_to_sbuf(rcv);
+
+ // Release the lock while doing network I/O, so that we don't block threads
+ // adding spans.
+ pthread_mutex_unlock(&rcv->lock);
+ while (1) {
+ int retry, success = htraced_xmit_impl(rcv, slen);
+ if (success) {
+ break;
+ }
+ tries++;
+ retry = (tries < HTRACED_MAX_SEND_TRIES);
+ htrace_log(rcv->tracer->lg, "htraced_xmit(%s) failed on try %d. %s\n",
+ rcv->url, tries,
+ (retry ? "Retrying after a delay." : "Giving up."));
+ if (!retry) {
+ break;
+ }
+ }
+ pthread_mutex_lock(&rcv->lock);
+ rcv->last_send_ms = now;
+}
+
+/**
+ * Move data from the circular buffer into the transmission buffer, advancing
+ * the circular buffer's start offset.
+ *
+ * This function must be called with the lock held.
+ *
+ * Note that we rely on HTRACED_MAX_MSG_LEN being < 4GB in this function for
+ * correctness on 32-bit systems.
+ *
+ * @param rcv The htraced receiver.
+ *
+ * @return The amount of data copied.
+ */
+static int32_t cbuf_to_sbuf(struct htraced_rcv *rcv)
+{
+ int32_t rem = HTRACED_MAX_MSG_LEN;
+ size_t amt;
+
+ if (rcv->cstart < rcv->cend) {
+ amt = rcv->cend - rcv->cstart;
+ if (amt > rem) {
+ amt = rem;
+ }
+ memcpy(rcv->sbuf, rcv->cbuf + rcv->cstart, amt);
+ rem -= amt;
+ rcv->cstart += amt;
+ } else {
+ amt = rcv->clen - rcv->cstart;
+ if (amt > rem) {
+ amt = rem;
+ }
+ memcpy(rcv->sbuf, rcv->cbuf + rcv->cstart, amt);
+ rem -= amt;
+ rcv->cstart += amt;
+ if (rem > 0) {
+ amt = rcv->cend;
+ if (amt > rem) {
+ amt = rem;
+ }
+ memcpy(rcv->sbuf, rcv->cbuf, amt);
+ rem -= amt;
+ rcv->cstart = amt;
+ }
+ }
+ return HTRACED_MAX_MSG_LEN - rem;
+}
+
+/**
+ * Returns the current number of bytes used in the htraced circular buffer.
+ * Must be called under the lock.
+ *
+ * @param rcv The htraced receiver.
+ *
+ * @return The number of bytes used.
+ */
+static uint64_t cbuf_used(const struct htraced_rcv *rcv)
+{
+ if (rcv->cstart <= rcv->cend) {
+ return rcv->cend - rcv->cstart;
+ }
+ return rcv->clen - (rcv->cstart - rcv->cend);
+}
+
+static void htraced_rcv_add_span(struct htrace_rcv *r,
+ struct htrace_span *span)
+{
+ int json_len, tries, retry;
+ uint64_t used, rem;
+ struct htraced_rcv *rcv = (struct htraced_rcv *)r;
+ struct htrace_log *lg = rcv->tracer->lg;
+
+ {
+ char buf[4096];
+ span_json_sprintf(span, sizeof(buf), buf);
+ }
+
+ json_len = span_json_size(span);
+ tries = 0;
+ do {
+ pthread_mutex_lock(&rcv->lock);
+ used = cbuf_used(rcv);
+ if (used + json_len >= rcv->clen) {
+ pthread_cond_signal(&rcv->cond);
+ pthread_mutex_unlock(&rcv->lock);
+ tries++;
+ retry = tries < HTRACED_MAX_ADD_TRIES;
+ htrace_log(lg, "htraced_rcv_add_span: not enough space in the "
+ "circular buffer. Have %" PRId64 ", need %d"
+ ". %s...\n", (rcv->clen - used), json_len,
+ (retry ? "Retrying" : "Giving up"));
+ if (retry) {
+ pthread_yield();
+ continue;
+ }
+ return;
+ }
+ } while (0);
+ // OK, now we have the lock, and we know that there is enough space in the
+ // circular buffer.
+ rem = rcv->clen - rcv->cend;
+ if (rem < json_len) {
+ // Handle a 'torn write' where the circular buffer loops around to the
+ // beginning in the middle of the write.
+ char *temp = alloca(json_len);
+ span_json_sprintf(span, json_len, temp);
+ temp[json_len - 1] = '\n';
+ memcpy(rcv->cbuf + rcv->cend, temp, rem);
+ memcpy(rcv->cbuf, temp + rem, json_len - rem);
+ rcv->cend = json_len - rem;
+ } else {
+ span_json_sprintf(span, json_len, rcv->cbuf + rcv->cend);
+ rcv->cbuf[rcv->cend + json_len - 1] = '\n';
+ rcv->cend += json_len;
+ }
+ used += json_len;
+ if (used > rcv->send_threshold) {
+ pthread_cond_signal(&rcv->cond);
+ }
+ pthread_mutex_unlock(&rcv->lock);
+}
+
+static void htraced_rcv_flush(struct htrace_rcv *r)
+{
+ struct htraced_rcv *rcv = (struct htraced_rcv *)r;
+
+ while (1) {
+ pthread_mutex_lock(&rcv->lock);
+ if (cbuf_used(rcv) == 0) {
+ // If the buffer is empty, we're done.
+ // Note that there is no guarantee that we'll ever be done if spans
+ // are being added continuously throughout the flush. This is OK,
+ // since flush() is actually only used by unit tests.
+ // We could do something more clever here, but it would be a lot more
+ // complex.
+ pthread_mutex_unlock(&rcv->lock);
+ break;
+ }
+ // Get the xmit thread to send what it can, by resetting the "last send
+ // time" to the oldest possible monotonic time.
+ rcv->last_send_ms = 0;
+ pthread_cond_signal(&rcv->cond);
+ pthread_mutex_unlock(&rcv->lock);
+ }
+}
+
+static void htraced_rcv_free(struct htrace_rcv *r)
+{
+ struct htraced_rcv *rcv = (struct htraced_rcv *)r;
+ struct htrace_log *lg;
+ int ret;
+
+ if (!rcv) {
+ return;
+ }
+ lg = rcv->tracer->lg;
+ htrace_log(lg, "Shutting down htraced receiver with url=%s\n", rcv->url);
+ pthread_mutex_lock(&rcv->lock);
+ rcv->shutdown = 1;
+ pthread_cond_signal(&rcv->cond);
+ pthread_mutex_unlock(&rcv->lock);
+ ret = pthread_join(rcv->xmit_thread, NULL);
+ if (ret) {
+ htrace_log(lg, "htraced_rcv_free: pthread_join "
+ "error %d: %s\n", ret, terror(ret));
+ }
+ free(rcv->url);
+ free(rcv->cbuf);
+ free(rcv->sbuf);
+ htrace_curl_free(lg, rcv->curl);
+ ret = pthread_mutex_destroy(&rcv->lock);
+ if (ret) {
+ htrace_log(lg, "htraced_rcv_free: pthread_mutex_destroy "
+ "error %d: %s\n", ret, terror(ret));
+ }
+ ret = pthread_cond_destroy(&rcv->cond);
+ if (ret) {
+ htrace_log(lg, "htraced_rcv_free: pthread_cond_destroy "
+ "error %d: %s\n", ret, terror(ret));
+ }
+ free(rcv);
+}
+
+const struct htrace_rcv_ty g_htraced_rcv_ty = {
+ "htraced",
+ htraced_rcv_create,
+ htraced_rcv_add_span,
+ htraced_rcv_flush,
+ htraced_rcv_free,
+};
+
+// vim:ts=4:sw=4:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/receiver/local_file.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/receiver/local_file.c b/htrace-c/src/receiver/local_file.c
new file mode 100644
index 0000000..667da29
--- /dev/null
+++ b/htrace-c/src/receiver/local_file.c
@@ -0,0 +1,184 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "core/conf.h"
+#include "core/htrace.h"
+#include "core/htracer.h"
+#include "core/span.h"
+#include "receiver/receiver.h"
+#include "util/log.h"
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * A span receiver that writes spans to a local file.
+ */
+struct local_file_rcv {
+ struct htrace_rcv base;
+
+ /**
+ * The htracer object associated with this receciver.
+ */
+ struct htracer *tracer;
+
+ /**
+ * The local file.
+ */
+ FILE *fp;
+
+ /**
+ * Path to the local file. Dynamically allocated.
+ */
+ char *path;
+
+ /**
+ * Lock protecting the local file from concurrent writes.
+ */
+ pthread_mutex_t lock;
+};
+
+static void local_file_rcv_free(struct htrace_rcv *r);
+
+static struct htrace_rcv *local_file_rcv_create(struct htracer *tracer,
+ const struct htrace_conf *conf)
+{
+ struct local_file_rcv *rcv;
+ const char *path;
+ int ret;
+
+ path = htrace_conf_get(conf, HTRACE_LOCAL_FILE_RCV_PATH_KEY);
+ if (!path) {
+ htrace_log(tracer->lg, "local_file_rcv_create: no value found for %s. "
+ "You must set this configuration key to the path you wish "
+ "to write spans to.\n", HTRACE_LOCAL_FILE_RCV_PATH_KEY);
+ return NULL;
+ }
+ rcv = calloc(1, sizeof(*rcv));
+ if (!rcv) {
+ htrace_log(tracer->lg, "local_file_rcv_create: OOM while "
+ "allocating local_file_rcv.\n");
+ return NULL;
+ }
+ ret = pthread_mutex_init(&rcv->lock, NULL);
+ if (ret) {
+ htrace_log(tracer->lg, "local_file_rcv_create: failed to "
+ "create mutex while setting up local_file_rcv: "
+ "error %d (%s)\n", ret, terror(ret));
+ free(rcv);
+ return NULL;
+ }
+ rcv->base.ty = &g_local_file_rcv_ty;
+ rcv->path = strdup(path);
+ if (!rcv->path) {
+ local_file_rcv_free((struct htrace_rcv*)rcv);
+ return NULL;
+ }
+ rcv->tracer = tracer;
+ rcv->fp = fopen(path, "a");
+ if (!rcv->fp) {
+ ret = errno;
+ htrace_log(tracer->lg, "local_file_rcv_create: failed to "
+ "open '%s' for write: error %d (%s)\n",
+ path, ret, terror(ret));
+ local_file_rcv_free((struct htrace_rcv*)rcv);
+ }
+ htrace_log(tracer->lg, "Initialized local_file receiver with path=%s.\n",
+ rcv->path);
+ return (struct htrace_rcv*)rcv;
+}
+
+static void local_file_rcv_add_span(struct htrace_rcv *r,
+ struct htrace_span *span)
+{
+ int len, res, err;
+ char *buf;
+ struct local_file_rcv *rcv = (struct local_file_rcv *)r;
+
+ span->prid = rcv->tracer->prid;
+ len = span_json_size(span);
+ buf = malloc(len + 1);
+ if (!buf) {
+ span->prid = NULL;
+ htrace_log(rcv->tracer->lg, "local_file_rcv_add_span: OOM\n");
+ return;
+ }
+ span_json_sprintf(span, len, buf);
+ span->prid = NULL;
+ buf[len - 1] = '\n';
+ buf[len] = '\0';
+ pthread_mutex_lock(&rcv->lock);
+ res = fwrite(buf, 1, len, rcv->fp);
+ err = errno;
+ pthread_mutex_unlock(&rcv->lock);
+ if (res < len) {
+ htrace_log(rcv->tracer->lg, "local_file_rcv_add_span(%s): fwrite error: "
+ "%d (%s)\n", rcv->path, err, terror(err));
+ }
+ free(buf);
+}
+
+static void local_file_rcv_flush(struct htrace_rcv *r)
+{
+ struct local_file_rcv *rcv = (struct local_file_rcv *)r;
+ if (fflush(rcv->fp) < 0) {
+ int e = errno;
+ htrace_log(rcv->tracer->lg, "local_file_rcv_flush(path=%s): fflush "
+ "error: %s\n", rcv->path, terror(e));
+ }
+}
+
+static void local_file_rcv_free(struct htrace_rcv *r)
+{
+ struct local_file_rcv *rcv = (struct local_file_rcv *)r;
+ int ret;
+ struct htrace_log *lg;
+
+ if (!rcv) {
+ return;
+ }
+ lg = rcv->tracer->lg;
+ htrace_log(lg, "Shutting down local_file receiver with path=%s\n",
+ rcv->path);
+ ret = pthread_mutex_destroy(&rcv->lock);
+ if (ret) {
+ htrace_log(lg, "local_file_rcv_free: pthread_mutex_destroy "
+ "error %d: %s\n", ret, terror(ret));
+ }
+ ret = fclose(rcv->fp);
+ if (ret) {
+ htrace_log(lg, "local_file_rcv_free: fclose error "
+ "%d: %s\n", ret, terror(ret));
+ }
+ free(rcv->path);
+ free(rcv);
+}
+
+const struct htrace_rcv_ty g_local_file_rcv_ty = {
+ "local.file",
+ local_file_rcv_create,
+ local_file_rcv_add_span,
+ local_file_rcv_flush,
+ local_file_rcv_free,
+};
+
+// vim:ts=4:sw=4:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/receiver/noop.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/receiver/noop.c b/htrace-c/src/receiver/noop.c
new file mode 100644
index 0000000..980854b
--- /dev/null
+++ b/htrace-c/src/receiver/noop.c
@@ -0,0 +1,65 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "core/htracer.h"
+#include "receiver/receiver.h"
+#include "util/log.h"
+
+/**
+ * A span receiver that does nothing but discard all spans.
+ */
+struct noop_rcv {
+ struct htrace_rcv base;
+};
+
+struct noop_rcv g_noop_rcv = {
+ { &g_noop_rcv_ty },
+};
+
+static struct htrace_rcv *noop_rcv_create(struct htracer *tracer,
+ const struct htrace_conf *conf)
+{
+ htrace_log(tracer->lg, "Using no-op htrace span receiver.\n");
+ return (struct htrace_rcv *)&g_noop_rcv;
+}
+
+static void noop_rcv_add_span(struct htrace_rcv *rcv,
+ struct htrace_span *span)
+{
+ // do nothing
+}
+
+static void noop_rcv_flush(struct htrace_rcv *rcv)
+{
+ // do nothing
+}
+
+static void noop_rcv_free(struct htrace_rcv *rcv)
+{
+ // do nothing
+}
+
+const struct htrace_rcv_ty g_noop_rcv_ty = {
+ "noop",
+ noop_rcv_create,
+ noop_rcv_add_span,
+ noop_rcv_flush,
+ noop_rcv_free,
+};
+
+// vim:ts=4:sw=4:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/receiver/receiver.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/receiver/receiver.c b/htrace-c/src/receiver/receiver.c
new file mode 100644
index 0000000..7fd3669
--- /dev/null
+++ b/htrace-c/src/receiver/receiver.c
@@ -0,0 +1,75 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "core/conf.h"
+#include "core/htrace.h"
+#include "core/htracer.h"
+#include "receiver/receiver.h"
+#include "util/log.h"
+
+#include <stdint.h>
+#include <string.h>
+
+const struct htrace_rcv_ty * const g_rcv_tys[] = {
+ &g_noop_rcv_ty,
+ &g_local_file_rcv_ty,
+ &g_htraced_rcv_ty,
+ NULL,
+};
+
+static const struct htrace_rcv_ty *select_rcv_ty(struct htracer *tracer,
+ const struct htrace_conf *conf)
+{
+ const char *tstr;
+ const char *prefix = "";
+ size_t i;
+ char buf[256] = { 0 };
+
+ tstr = htrace_conf_get(conf, HTRACE_SPAN_RECEIVER_KEY);
+ if (!tstr) {
+ htrace_log(tracer->lg, "No %s configured.\n", HTRACE_SPAN_RECEIVER_KEY);
+ return &g_noop_rcv_ty;
+ }
+ for (i = 0; g_rcv_tys[i]; i++) {
+ if (strcmp(g_rcv_tys[i]->name, tstr) == 0) {
+ return g_rcv_tys[i];
+ }
+ }
+ for (i = 0; g_rcv_tys[i]; i++) {
+ if ((strlen(buf) + strlen(prefix) +
+ strlen(g_rcv_tys[i]->name)) < sizeof(buf)) {
+ strcat(buf, prefix);
+ strcat(buf, g_rcv_tys[i]->name);
+ prefix = ", ";
+ }
+ }
+ htrace_log(tracer->lg, "Unknown span receiver type as '%s'. Valid "
+ "span receiver types are: %s\n", tstr, buf);
+ return &g_noop_rcv_ty;
+}
+
+struct htrace_rcv *htrace_rcv_create(struct htracer *tracer,
+ const struct htrace_conf *conf)
+{
+ const struct htrace_rcv_ty *ty;
+
+ ty = select_rcv_ty(tracer, conf);
+ return ty->create(tracer, conf);
+}
+
+// vim:ts=4:sw=4:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/receiver/receiver.h
----------------------------------------------------------------------
diff --git a/htrace-c/src/receiver/receiver.h b/htrace-c/src/receiver/receiver.h
new file mode 100644
index 0000000..1e01486
--- /dev/null
+++ b/htrace-c/src/receiver/receiver.h
@@ -0,0 +1,114 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef APACHE_HTRACE_RECEIVER_RECEIVER_H
+#define APACHE_HTRACE_RECEIVER_RECEIVER_H
+
+/**
+ * @file rcv.h
+ *
+ * Functions related to HTrace receivers.
+ *
+ * This is an internal header, not intended for external use.
+ */
+
+struct htrace_conf;
+struct htrace_span;
+struct htracer;
+
+/**
+ * Base class for an HTrace span receiver.
+ *
+ * Implementations should begin with this class as the first member.
+ */
+struct htrace_rcv {
+ /**
+ * The type of the receiver.
+ */
+ const struct htrace_rcv_ty *ty;
+};
+
+/**
+ * A table of callbacks that implements an HTrace span receiver.
+ */
+struct htrace_rcv_ty {
+ /**
+ * The name of this HTrace span receiver type.
+ */
+ const char * const name;
+
+ /**
+ * Create an HTrace span receiver of this type.
+ *
+ * @param tracer The HTrace context to use. The span receiver may
+ * hold on to this pointer.
+ * @param conf The HTrace configuration to use. The span
+ * receiver must not hold on to this pointer.
+ *
+ * @return The HTrace span receciver.
+ */
+ struct htrace_rcv *(*create)(struct htracer *tracer,
+ const struct htrace_conf *conf);
+
+ /**
+ * Callback to add a new span.
+ *
+ * @param rcv The HTrace span receiver.
+ * @param span The trace span to add.
+ */
+ void (*add_span)(struct htrace_rcv *rcv, struct htrace_span *span);
+
+ /**
+ * Flush all buffered spans to the backing store used by this receiver.
+ *
+ * @param rcv The HTrace span receiver.
+ */
+ void (*flush)(struct htrace_rcv *rcv);
+
+ /**
+ * Frees this HTrace span receiver.
+ *
+ * @param rcv The HTrace span receiver.
+ */
+ void (*free)(struct htrace_rcv *rcv);
+};
+
+/**
+ * Create an HTrace span receiver.
+ *
+ * @param tracer The HTrace context to use. The newly created
+ * span receiver may hold on to this pointer.
+ * @param conf The HTrace configuration to use. The newly
+ * created span receiver will not hold on to this
+ * pointer.
+ *
+ * @return The HTrace span receciver.
+ */
+struct htrace_rcv *htrace_rcv_create(struct htracer *tracer,
+ const struct htrace_conf *conf);
+
+/*
+ * HTrace span receiver types.
+ */
+const struct htrace_rcv_ty g_noop_rcv_ty;
+const struct htrace_rcv_ty g_local_file_rcv_ty;
+const struct htrace_rcv_ty g_htraced_rcv_ty;
+
+#endif
+
+// vim: ts=4: sw=4: et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/sampler/always.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/sampler/always.c b/htrace-c/src/sampler/always.c
new file mode 100644
index 0000000..a570e79
--- /dev/null
+++ b/htrace-c/src/sampler/always.c
@@ -0,0 +1,71 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sampler/sampler.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/**
+ * A sampler that always fires.
+ */
+struct always_sampler {
+ struct htrace_sampler base;
+};
+
+static struct htrace_sampler *always_sampler_create(struct htracer *tracer,
+ const struct htrace_conf *conf);
+static const char *always_sampler_to_str(struct htrace_sampler *sampler);
+static int always_sampler_next(struct htrace_sampler *sampler);
+static void always_sampler_free(struct htrace_sampler *sampler);
+
+const struct htrace_sampler_ty g_always_sampler_ty = {
+ "always",
+ always_sampler_create,
+ always_sampler_to_str,
+ always_sampler_next,
+ always_sampler_free,
+};
+
+const struct always_sampler g_always_sampler = {
+ { (struct htrace_sampler_ty*) &g_always_sampler_ty },
+};
+
+static struct htrace_sampler *always_sampler_create(struct htracer *tracer,
+ const struct htrace_conf *conf)
+{
+ return (struct htrace_sampler*)&g_always_sampler;
+}
+
+static const char *always_sampler_to_str(struct htrace_sampler *sampler)
+{
+ return "AlwaysSampler";
+}
+
+static int always_sampler_next(struct htrace_sampler *sampler)
+{
+ return 1;
+}
+
+static void always_sampler_free(struct htrace_sampler *sampler)
+{
+ // do nothing.
+}
+
+// vim: ts=4:sw=4:tw=79:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/sampler/never.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/sampler/never.c b/htrace-c/src/sampler/never.c
new file mode 100644
index 0000000..cf25bfa
--- /dev/null
+++ b/htrace-c/src/sampler/never.c
@@ -0,0 +1,71 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "sampler/sampler.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/**
+ * A sampler that never fires.
+ */
+struct never_sampler {
+ struct htrace_sampler base;
+};
+
+static struct htrace_sampler *never_sampler_create(struct htracer *tracer,
+ const struct htrace_conf *conf);
+static const char *never_sampler_to_str(struct htrace_sampler *sampler);
+static int never_sampler_next(struct htrace_sampler *sampler);
+static void never_sampler_free(struct htrace_sampler *sampler);
+
+const struct htrace_sampler_ty g_never_sampler_ty = {
+ "never",
+ never_sampler_create,
+ never_sampler_to_str,
+ never_sampler_next,
+ never_sampler_free,
+};
+
+struct never_sampler g_never_sampler = {
+ { (struct htrace_sampler_ty*) &g_never_sampler_ty },
+};
+
+static struct htrace_sampler *never_sampler_create(struct htracer *tracer,
+ const struct htrace_conf *conf)
+{
+ return (struct htrace_sampler*)&g_never_sampler;
+}
+
+static const char *never_sampler_to_str(struct htrace_sampler *sampler)
+{
+ return "NeverSampler";
+}
+
+static int never_sampler_next(struct htrace_sampler *sampler)
+{
+ return 0;
+}
+
+static void never_sampler_free(struct htrace_sampler *sampler)
+{
+ // do nothing.
+}
+
+// vim: ts=4:sw=4:tw=79:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/sampler/prob.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/sampler/prob.c b/htrace-c/src/sampler/prob.c
new file mode 100644
index 0000000..789a2eb
--- /dev/null
+++ b/htrace-c/src/sampler/prob.c
@@ -0,0 +1,137 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "core/conf.h"
+#include "core/htrace.h"
+#include "core/htracer.h"
+#include "sampler/sampler.h"
+#include "util/log.h"
+#include "util/rand.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/**
+ * A sampler that fires with a certain chance.
+ */
+struct prob_sampler {
+ struct htrace_sampler base;
+
+ /**
+ * A random source.
+ */
+ struct random_src *rnd;
+
+ /**
+ * The name of this probability sampler.
+ */
+ char *name;
+
+ /**
+ * The threshold at which we should sample.
+ */
+ uint32_t threshold;
+};
+
+static double get_prob_sampler_threshold(struct htrace_log *lg,
+ const struct htrace_conf *conf);
+static struct htrace_sampler *prob_sampler_create(struct htracer *tracer,
+ const struct htrace_conf *conf);
+static const char *prob_sampler_to_str(struct htrace_sampler *s);
+static int prob_sampler_next(struct htrace_sampler *s);
+static void prob_sampler_free(struct htrace_sampler *s);
+
+const struct htrace_sampler_ty g_prob_sampler_ty = {
+ "prob",
+ prob_sampler_create,
+ prob_sampler_to_str,
+ prob_sampler_next,
+ prob_sampler_free,
+};
+
+static double get_prob_sampler_threshold(struct htrace_log *lg,
+ const struct htrace_conf *conf)
+{
+ double fraction =
+ htrace_conf_get_double(lg, conf, HTRACE_PROB_SAMPLER_FRACTION_KEY);
+ if (fraction < 0) {
+ htrace_log(lg, "sampler_create: can't have a sampling fraction "
+ "less than 0. Setting fraction to 0.\n");
+ fraction = 0.0;
+ } else if (fraction > 1.0) {
+ htrace_log(lg, "sampler_create: can't have a sampling fraction "
+ "greater than 1. Setting fraction to 1.\n");
+ fraction = 1.0;
+ }
+ return fraction;
+}
+
+static struct htrace_sampler *prob_sampler_create(struct htracer *tracer,
+ const struct htrace_conf *conf)
+{
+ struct prob_sampler *smp;
+ double fraction;
+
+ smp = calloc(1, sizeof(*smp));
+ if (!smp) {
+ htrace_log(tracer->lg, "prob_sampler_create: OOM\n");
+ return NULL;
+ }
+ smp->base.ty = &g_prob_sampler_ty;
+ smp->rnd = random_src_alloc(tracer->lg);
+ if (!smp->rnd) {
+ htrace_log(tracer->lg, "random_src_alloc failed.\n");
+ free(smp);
+ return NULL;
+ }
+ fraction = get_prob_sampler_threshold(tracer->lg, conf);
+ smp->threshold = 0xffffffffLU * fraction;
+ if (asprintf(&smp->name, "ProbabilitySampler(fraction=%.03g)",
+ fraction) < 0) {
+ smp->name = NULL;
+ random_src_free(smp->rnd);
+ free(smp);
+ }
+ return (struct htrace_sampler *)smp;
+}
+
+static const char *prob_sampler_to_str(struct htrace_sampler *s)
+{
+ struct prob_sampler *smp = (struct prob_sampler *)s;
+ return smp->name;
+}
+
+static int prob_sampler_next(struct htrace_sampler *s)
+{
+ struct prob_sampler *smp = (struct prob_sampler *)s;
+ return random_u32(smp->rnd) < smp->threshold;
+}
+
+static void prob_sampler_free(struct htrace_sampler *s)
+{
+ struct prob_sampler *smp = (struct prob_sampler *)s;
+ random_src_free(smp->rnd);
+ free(smp->name);
+ free(smp);
+}
+
+// vim: ts=4:sw=4:tw=79:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/sampler/sampler.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/sampler/sampler.c b/htrace-c/src/sampler/sampler.c
new file mode 100644
index 0000000..81aef94
--- /dev/null
+++ b/htrace-c/src/sampler/sampler.c
@@ -0,0 +1,88 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "core/conf.h"
+#include "core/htrace.h"
+#include "core/htracer.h"
+#include "sampler/sampler.h"
+#include "util/log.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+const struct htrace_sampler_ty * const g_sampler_tys[] = {
+ &g_never_sampler_ty,
+ &g_always_sampler_ty,
+ &g_prob_sampler_ty,
+ NULL,
+};
+
+static const struct htrace_sampler_ty *select_sampler_ty(
+ struct htracer *tracer, const struct htrace_conf *cnf)
+{
+ const char *tstr;
+ const char *prefix = "";
+ size_t i;
+ char buf[256] = { 0 };
+
+ tstr = htrace_conf_get(cnf, HTRACE_SAMPLER_KEY);
+ if (!tstr) {
+ htrace_log(tracer->lg, "No %s configured.\n", HTRACE_SAMPLER_KEY);
+ return &g_never_sampler_ty;
+ }
+ for (i = 0; g_sampler_tys[i]; i++) {
+ if (strcmp(g_sampler_tys[i]->name, tstr) == 0) {
+ return g_sampler_tys[i];
+ }
+ }
+ for (i = 0; g_sampler_tys[i]; i++) {
+ if ((strlen(buf) + strlen(prefix) +
+ strlen(g_sampler_tys[i]->name)) < sizeof(buf)) {
+ strcat(buf, prefix);
+ strcat(buf, g_sampler_tys[i]->name);
+ prefix = ", ";
+ }
+ }
+ htrace_log(tracer->lg, "Unknown sampler type '%s'. Valid "
+ "sampler types are: %s\n", tstr, buf);
+ return &g_never_sampler_ty;
+}
+
+struct htrace_sampler *htrace_sampler_create(struct htracer *tracer,
+ struct htrace_conf *cnf)
+{
+ const struct htrace_sampler_ty *ty;
+
+ ty = select_sampler_ty(tracer, cnf);
+ return ty->create(tracer, cnf);
+}
+
+const char *htrace_sampler_to_str(struct htrace_sampler *smp)
+{
+ return smp->ty->to_str(smp);
+}
+
+void htrace_sampler_free(struct htrace_sampler *smp)
+{
+ return smp->ty->free(smp);
+}
+
+// vim: ts=4:sw=4:tw=79:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/sampler/sampler.h
----------------------------------------------------------------------
diff --git a/htrace-c/src/sampler/sampler.h b/htrace-c/src/sampler/sampler.h
new file mode 100644
index 0000000..1c423e6
--- /dev/null
+++ b/htrace-c/src/sampler/sampler.h
@@ -0,0 +1,121 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef APACHE_HTRACE_SAMPLER_SAMPLER_H
+#define APACHE_HTRACE_SAMPLER_SAMPLER_H
+
+/**
+ * @file sampler.h
+ *
+ * Functions related to HTrace samplers.
+ *
+ * This is an internal header, not intended for external use.
+ */
+
+#include <stdint.h>
+
+struct htrace_conf;
+struct htrace_log;
+struct htracer;
+
+/**
+ * Base class for an HTrace sampler.
+ *
+ * Implementations should begin with this class as the first member.
+ */
+struct htrace_sampler {
+ /**
+ * The type of the sampler.
+ */
+ const struct htrace_sampler_ty *ty;
+};
+
+/**
+ * A table of callbacks that implements an HTrace sampler.
+ */
+struct htrace_sampler_ty {
+ /**
+ * The name of this probability sampler type.
+ *
+ * This is used to select the sampler via the configuration.
+ */
+ const char *name;
+
+ /**
+ * Create an HTrace sampler of this type.
+ *
+ * @param tracer The HTrace context to use. The sampler may
+ * hold on to this pointer.
+ * @param conf The HTrace configuration to use. The sampler
+ * must not hold on to this pointer.
+ *
+ * @return The HTrace span receciver.
+ */
+ struct htrace_sampler *(*create)(struct htracer *tracer,
+ const struct htrace_conf *conf);
+
+ /**
+ * Get the name of this HTrace sampler.
+ *
+ * @param smp The sampler.
+ *
+ * @return A description of this sampler object. This string
+ * must remain valid at least until the sampler
+ * is freed.
+ */
+ const char*(*to_str)(struct htrace_sampler *smp);
+
+ /**
+ * Sampler callback.
+ *
+ * This callback must be able to be safely called by multiple threads
+ * simultaneously.
+ *
+ * @param smp The HTrace sampler.
+ *
+ * @return 1 to begin a new span; 0 otherwise.
+ */
+ int (*next)(struct htrace_sampler *smp);
+
+ /**
+ * Frees this HTrace sampler.
+ *
+ * @param rcv The HTrace sampler.
+ */
+ void (*free)(struct htrace_sampler *smp);
+};
+
+/**
+ * Get the configured fraction for the probability sampler.
+ *
+ * @param log A log to send parse error messages to.
+ * @param conf The configuration to use.
+ *
+ * @return A double between 0.0 and 1.0, inclusive.
+ */
+double get_prob_sampler_fraction(struct htrace_log *lg,
+ struct htrace_conf *conf);
+
+extern const struct htrace_sampler_ty g_never_sampler_ty;
+extern const struct htrace_sampler_ty g_always_sampler_ty;
+extern const struct htrace_sampler_ty g_prob_sampler_ty;
+extern const struct always_sampler g_always_sampler;
+
+#endif
+
+// vim: ts=4:sw=4:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/test/conf-unit.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/test/conf-unit.c b/htrace-c/src/test/conf-unit.c
new file mode 100644
index 0000000..debc866
--- /dev/null
+++ b/htrace-c/src/test/conf-unit.c
@@ -0,0 +1,95 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "core/conf.h"
+#include "test/test.h"
+#include "util/log.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int test_simple_conf(void)
+{
+ struct htrace_conf *conf;
+ struct htrace_log *lg;
+ conf = htrace_conf_from_strs("foo=bar;foo2=baz;foo3=quux;foo5=123",
+ "foo3=default3;foo4=default4");
+ lg = htrace_log_alloc(conf);
+ EXPECT_NONNULL(conf);
+ EXPECT_STR_EQ("bar", htrace_conf_get(conf, "foo"));
+ EXPECT_STR_EQ("quux", htrace_conf_get(conf, "foo3"));
+ EXPECT_STR_EQ("default4", htrace_conf_get(conf, "foo4"));
+ EXPECT_UINT64_EQ((uint64_t)123, htrace_conf_get_u64(lg, conf, "foo5"));
+ EXPECT_UINT64_EQ((uint64_t)123, htrace_conf_get_u64(lg, conf, "foo5"));
+ EXPECT_NULL(htrace_conf_get(conf, "unknown"));
+
+ htrace_log_free(lg);
+ htrace_conf_free(conf);
+ return EXIT_SUCCESS;
+}
+
+static int test_double_conf(void)
+{
+ struct htrace_conf *conf;
+ struct htrace_log *lg;
+ double d;
+
+ conf = htrace_conf_from_strs("my.double=5.4;bozo=wakkawakkaa",
+ "my.double=1.1;bozo=2.0");
+ EXPECT_NONNULL(conf);
+ lg = htrace_log_alloc(conf);
+ d = htrace_conf_get_double(lg, conf, "my.double");
+ // Do a sloppy comparison to avoid thinking about IEEE float precision
+ // issues
+ if ((d > 5.401) || (d < 5.399)) {
+ htrace_log(lg, "failed to parse my.double... expected 5.4, "
+ "got %g\n", d);
+ return EXIT_FAILURE;
+ }
+ // 'bozo' should fall back on the default, since the configured value
+ // cannot be parsed.
+ d = htrace_conf_get_double(lg, conf, "bozo");
+ if ((d > 2.001) || (d < 1.999)) {
+ htrace_log(lg, "failed to parse bozo... expected 2.0, "
+ "got %g\n", d);
+ return EXIT_FAILURE;
+ }
+ // 'unknown' should get 0.0, since there is no value or default.
+ d = htrace_conf_get_double(lg, conf, "unknown");
+ if ((d > 0.001) || (d < -0.001)) {
+ htrace_log(lg, "failed to parse unknown... expected 0.0, "
+ "got %g\n", d);
+ return EXIT_FAILURE;
+ }
+
+ htrace_log_free(lg);
+ htrace_conf_free(conf);
+ return EXIT_SUCCESS;
+}
+
+int main(void)
+{
+ test_simple_conf();
+ test_double_conf();
+
+ return EXIT_SUCCESS;
+}
+
+// vim: ts=4:sw=4:tw=79:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/test/htable-unit.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/test/htable-unit.c b/htrace-c/src/test/htable-unit.c
new file mode 100644
index 0000000..a656fcc
--- /dev/null
+++ b/htrace-c/src/test/htable-unit.c
@@ -0,0 +1,92 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "test/test.h"
+#include "util/htable.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static uint32_t simple_hash(const void *key, uint32_t size)
+{
+ uintptr_t k = (uintptr_t)key;
+ return ((13 + k) * 6367) % size;
+}
+
+static int simple_compare(const void *a, const void *b)
+{
+ return a == b;
+}
+
+static void expect_102(void *f, void *k, void *v)
+{
+ int *found_102 = f;
+ uintptr_t key = (uintptr_t)k;
+ uintptr_t val = (uintptr_t)v;
+
+ if ((key == 2) && (val == 102)) {
+ *found_102 = 1;
+ } else {
+ abort();
+ }
+}
+
+static void *htable_pop_val(struct htable *ht, void *key)
+{
+ void *old_key, *old_val;
+
+ htable_pop(ht, key, &old_key, &old_val);
+ return old_val;
+}
+
+int main(void)
+{
+ struct htable *ht;
+ int found_102 = 0;
+
+ ht = htable_alloc(4, simple_hash, simple_compare);
+ EXPECT_INT_EQ(0, htable_used(ht));
+ EXPECT_INT_EQ(4, htable_capacity(ht));
+ EXPECT_NULL(htable_get(ht, (void*)123));
+ EXPECT_NULL(htable_pop_val(ht, (void*)123));
+ EXPECT_INT_ZERO(htable_put(ht, (void*)123, (void*)456));
+ EXPECT_UINTPTR_EQ(456L, (uintptr_t)htable_get(ht, (void*)123));
+ EXPECT_UINTPTR_EQ(456L, (uintptr_t)htable_pop_val(ht, (void*)123));
+ EXPECT_NULL(htable_pop_val(ht, (void*)123));
+
+ // Enlarge the hash table
+ EXPECT_INT_ZERO(htable_put(ht, (void*)1, (void*)101));
+ EXPECT_INT_ZERO(htable_put(ht, (void*)2, (void*)102));
+ EXPECT_INT_ZERO(htable_put(ht, (void*)3, (void*)103));
+ EXPECT_INT_EQ(3, htable_used(ht));
+ EXPECT_INT_EQ(8, htable_capacity(ht));
+ EXPECT_UINTPTR_EQ(102L, (uintptr_t)htable_get(ht, (void*)2));
+ EXPECT_UINTPTR_EQ(101L, (uintptr_t)htable_pop_val(ht, (void*)1));
+ EXPECT_UINTPTR_EQ(103L, (uintptr_t)htable_pop_val(ht, (void*)3));
+ EXPECT_INT_EQ(1, htable_used(ht));
+ htable_visit(ht, expect_102, &found_102);
+ EXPECT_INT_EQ(1, found_102);
+ htable_free(ht);
+
+ return EXIT_SUCCESS;
+}
+
+// vim: ts=4:sw=4:tw=79:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/test/htraced_rcv-unit.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/test/htraced_rcv-unit.c b/htrace-c/src/test/htraced_rcv-unit.c
new file mode 100644
index 0000000..29f62d1
--- /dev/null
+++ b/htrace-c/src/test/htraced_rcv-unit.c
@@ -0,0 +1,110 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "core/conf.h"
+#include "core/htrace.h"
+#include "test/mini_htraced.h"
+#include "test/rtest.h"
+#include "test/span_table.h"
+#include "test/span_util.h"
+#include "test/temp_dir.h"
+#include "test/test.h"
+#include "util/log.h"
+#include "util/time.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static int htraced_rcv_test(struct rtest *rt)
+{
+ char err[512], *conf_str, *json_path;
+ size_t err_len = sizeof(err);
+ struct mini_htraced_params params;
+ struct mini_htraced *ht = NULL;
+ struct span_table *st;
+ uint64_t start_ms;
+
+ params.name = rt->name;
+ params.confstr = "";
+ mini_htraced_build(¶ms, &ht, err, err_len);
+ EXPECT_STR_EQ("", err);
+
+ EXPECT_INT_GE(0, asprintf(&json_path, "%s/%s",
+ ht->root_dir, "spans.json"));
+ EXPECT_INT_GE(0, asprintf(&conf_str, "%s=%s;%s=%s",
+ HTRACE_SPAN_RECEIVER_KEY, "htraced",
+ HTRACED_ADDRESS_KEY, ht->htraced_http_addr));
+ EXPECT_INT_ZERO(rt->run(rt, conf_str));
+ start_ms = monotonic_now_ms(NULL);
+ //
+ // It may take a little while for htraced to commit the incoming spans sent
+ // via RPC to its data store. htraced does not have read-after-write
+ // consistency, in other words. This isn't normally an issue since trace
+ // collection is done in the background.
+ //
+ // For this unit test, it means that we want to retry if we find too few
+ // spans the first time we dump the htraced data store contents.
+ //
+ while (1) {
+ int nspans;
+
+ // This uses the bin/htrace program to dump the spans to a json file.
+ mini_htraced_dump_spans(ht, err, err_len, json_path);
+ EXPECT_STR_EQ("", err);
+ st = span_table_alloc();
+ EXPECT_NONNULL(st);
+ nspans = load_trace_span_file(json_path, st);
+ EXPECT_INT_GE(0, nspans);
+ if (nspans >= rt->spans_created) {
+ break;
+ }
+ span_table_free(st);
+ st = NULL;
+ EXPECT_UINT64_GE(start_ms, monotonic_now_ms(NULL) + 30000);
+ sleep_ms(100);
+ fprintf(stderr, "htraced_test_app1: retrying htrace dumpAll...\n");
+ }
+ EXPECT_INT_ZERO(rt->verify(rt, st));
+ free(conf_str);
+ free(json_path);
+ span_table_free(st);
+ mini_htraced_stop(ht);
+ mini_htraced_free(ht);
+
+ return EXIT_SUCCESS;
+}
+
+int main(void)
+{
+ int i;
+
+ for (i = 0; g_rtests[i]; i++) {
+ struct rtest *rtest = g_rtests[i];
+ if (htraced_rcv_test(rtest) != EXIT_SUCCESS) {
+ fprintf(stderr, "rtest %s failed\n", rtest->name);
+ return EXIT_FAILURE;
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
+
+// vim: ts=4:sw=4:tw=79:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/test/linkage-unit.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/test/linkage-unit.c b/htrace-c/src/test/linkage-unit.c
new file mode 100644
index 0000000..f3c5eba
--- /dev/null
+++ b/htrace-c/src/test/linkage-unit.c
@@ -0,0 +1,105 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "core/htrace.h"
+#include "test/test_config.h"
+
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * @file linkage-unit.c
+ *
+ * Tests the linkage of libhtrace.so.
+ * Verifies that the functions in htrace.h are publicly visible, and that
+ * functions not included in that header are not.
+ *
+ * This unit test links against the production libhtrace.so, not the test
+ * library. This also verifies that we can run a program compiled against the
+ * production library without encountering unresolved symbols or similar.
+ */
+
+static const char * const PUBLIC_SYMS[] = {
+ "htrace_conf_free",
+ "htrace_conf_from_str",
+ "htrace_restart_span",
+ "htrace_sampler_create",
+ "htrace_sampler_free",
+ "htrace_sampler_to_str",
+ "htrace_scope_close",
+ "htrace_scope_detach",
+ "htrace_scope_get_span_id",
+ "htrace_start_span",
+ "htracer_create",
+ "htracer_free",
+ "htracer_tname"
+};
+
+#define PUBLIC_SYMS_SIZE (sizeof(PUBLIC_SYMS) / sizeof(PUBLIC_SYMS[0]))
+
+/**
+ * Test that we can call the htrace_conf_from_strs function without aborting at
+ * runtime. This may seem like a trivial test, but keep in mind this is the
+ * only unit test that uses the real libhtrace.so, not the testing library.
+ */
+static int test_call_htrace_conf_from_strs(void)
+{
+ struct htrace_conf *cnf;
+
+ cnf = htrace_conf_from_str("foo=bar");
+ htrace_conf_free(cnf);
+ return EXIT_SUCCESS;
+}
+
+/**
+ * Test that we can find all the public symbols in the library.
+ */
+static int find_public_symbols(void)
+{
+ int i;
+ void *sym;
+
+ for (i = 0; i < PUBLIC_SYMS_SIZE; i++) {
+ sym = dlsym(RTLD_DEFAULT, PUBLIC_SYMS[i]);
+ if (!sym) {
+ fprintf(stderr, "Failed to find %s, or its value was NULL.\n",
+ PUBLIC_SYMS[i]);
+ return EXIT_FAILURE;
+ }
+ }
+ if (dlsym(RTLD_DEFAULT, "htrace_span_alloc")) {
+ fprintf(stderr, "Found non-public symbol htrace_span_alloc.\n");
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
+}
+
+int main(void)
+{
+ if (test_call_htrace_conf_from_strs()) {
+ abort();
+ }
+ if (find_public_symbols()) {
+ abort();
+ }
+ return EXIT_SUCCESS;
+}
+
+// vim: ts=4:sw=4:tw=79:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/test/local_file_rcv-unit.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/test/local_file_rcv-unit.c b/htrace-c/src/test/local_file_rcv-unit.c
new file mode 100644
index 0000000..8d518a9
--- /dev/null
+++ b/htrace-c/src/test/local_file_rcv-unit.c
@@ -0,0 +1,74 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "core/conf.h"
+#include "core/htrace.h"
+#include "test/rtest.h"
+#include "test/span_table.h"
+#include "test/span_util.h"
+#include "test/temp_dir.h"
+#include "test/test.h"
+#include "util/log.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int local_file_rcv_test(struct rtest *rt)
+{
+ char err[512];
+ size_t err_len = sizeof(err);
+ char *local_path, *tdir, *conf_str = NULL;
+ struct span_table *st;
+
+ st = span_table_alloc();
+ tdir = create_tempdir("local_file_rcv-unit", 0777, err, err_len);
+ EXPECT_STR_EQ("", err);
+ register_tempdir_for_cleanup(tdir);
+ EXPECT_INT_GE(0, asprintf(&local_path, "%s/%s", tdir, "spans.json"));
+ EXPECT_INT_GE(0, asprintf(&conf_str, "%s=%s;%s=%s",
+ HTRACE_SPAN_RECEIVER_KEY, "local.file",
+ HTRACE_LOCAL_FILE_RCV_PATH_KEY, local_path));
+ EXPECT_INT_ZERO(rt->run(rt, conf_str));
+ EXPECT_INT_GE(0, load_trace_span_file(local_path, st));
+ EXPECT_INT_ZERO(rt->verify(rt, st));
+ free(conf_str);
+ free(local_path);
+ free(tdir);
+ span_table_free(st);
+
+ return EXIT_SUCCESS;
+}
+
+int main(void)
+{
+ int i;
+
+ for (i = 0; g_rtests[i]; i++) {
+ struct rtest *rtest = g_rtests[i];
+ if (local_file_rcv_test(rtest) != EXIT_SUCCESS) {
+ fprintf(stderr, "rtest %s failed\n", rtest->name);
+ return EXIT_FAILURE;
+ }
+ }
+
+ return EXIT_SUCCESS;
+}
+
+// vim: ts=4:sw=4:tw=79:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/test/log-unit.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/test/log-unit.c b/htrace-c/src/test/log-unit.c
new file mode 100644
index 0000000..af7de48
--- /dev/null
+++ b/htrace-c/src/test/log-unit.c
@@ -0,0 +1,97 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "core/conf.h"
+#include "test/temp_dir.h"
+#include "test/test.h"
+#include "util/log.h"
+
+#include <errno.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int verify_log_file(const char *path)
+{
+ FILE *fp;
+ char contents[4096];
+ size_t res;
+ const char * const expected_contents =
+ "foo 2, bar, and baz.\nquux as well.\n";
+
+ fp = fopen(path, "r");
+ if (!fp) {
+ int e = errno;
+ fprintf(stderr, "failed to open %s: error %d (%s)\n",
+ path, e, terror(e));
+ return EXIT_FAILURE;
+ }
+ memset(contents, 0, sizeof(contents));
+ res = fread(contents, 1, sizeof(contents), fp);
+ if (res < strlen(expected_contents)) {
+ int e = errno;
+ if (feof(fp)) {
+ fprintf(stderr, "fread(%s): unexpected eof.\n", path);
+ return EXIT_FAILURE;
+ }
+ fprintf(stderr, "fread(%s): error %d (%s)\n",
+ path, e, terror(e));
+ return EXIT_FAILURE;
+ }
+ fclose(fp);
+ EXPECT_STR_EQ(expected_contents, contents);
+
+ return EXIT_SUCCESS;
+}
+
+static int verify_log_to_file(void)
+{
+ struct htrace_conf *conf;
+ struct htrace_log *lg;
+ char *tdir, log_path[PATH_MAX], conf_str[PATH_MAX];
+ char err[128];
+ size_t err_len = sizeof(err);
+
+ tdir = create_tempdir("verify_log_to_file", 0775, err, err_len);
+ EXPECT_NONNULL(tdir);
+ EXPECT_INT_ZERO(register_tempdir_for_cleanup(tdir));
+ snprintf(log_path, sizeof(log_path), "%s/log.txt", tdir);
+ snprintf(conf_str, sizeof(conf_str), "log.path=%s", log_path);
+ conf = htrace_conf_from_strs(conf_str, "");
+ EXPECT_NONNULL(conf);
+ lg = htrace_log_alloc(conf);
+ EXPECT_NONNULL(lg);
+ htrace_log(lg, "foo %d, bar, and baz.\n", 2);
+ htrace_log(lg, "quux as well.\n");
+ htrace_log_free(lg);
+ EXPECT_INT_ZERO(verify_log_file(log_path));
+ htrace_conf_free(conf);
+ free(tdir);
+
+ return EXIT_SUCCESS;
+}
+
+int main(void)
+{
+ EXPECT_INT_ZERO(verify_log_to_file());
+
+ return EXIT_SUCCESS;
+}
+
+// vim: ts=4:sw=4:tw=79:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/test/mini_htraced-unit.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/test/mini_htraced-unit.c b/htrace-c/src/test/mini_htraced-unit.c
new file mode 100644
index 0000000..bfcd2ef
--- /dev/null
+++ b/htrace-c/src/test/mini_htraced-unit.c
@@ -0,0 +1,48 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "test/mini_htraced.h"
+#include "test/test.h"
+
+#include <stdlib.h>
+
+static int test_mini_htraced_start_stop(void)
+{
+ char err[128];
+ size_t err_len = sizeof(err);
+ struct mini_htraced_params params;
+ struct mini_htraced *ht = NULL;
+
+ err[0] = '\0';
+ params.name = "test_mini_htraced_start_stop";
+ params.confstr = "";
+ mini_htraced_build(¶ms, &ht, err, err_len);
+ EXPECT_STR_EQ("", err);
+ mini_htraced_stop(ht);
+ mini_htraced_free(ht);
+
+ return 0;
+}
+
+int main(void)
+{
+ EXPECT_INT_ZERO(test_mini_htraced_start_stop());
+ return EXIT_SUCCESS;
+}
+
+// vim: ts=4:sw=4:tw=79:et
[2/4] incubator-htrace git commit: HTRACE-106: htrace: add C / C++
native client (cmccabe)
Posted by cm...@apache.org.
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/test/mini_htraced.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/test/mini_htraced.c b/htrace-c/src/test/mini_htraced.c
new file mode 100644
index 0000000..31b5484
--- /dev/null
+++ b/htrace-c/src/test/mini_htraced.c
@@ -0,0 +1,599 @@
+/**
+ * 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/temp_dir.h"
+#include "test/test_config.h"
+#include "test/test.h"
+#include "util/log.h"
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <json/json_object.h>
+#include <json/json_tokener.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+/**
+ * The maximum size of the notification data sent from the htraced daemon on
+ * startup.
+ */
+#define MAX_NDATA 65536
+
+/**
+ * The separator to use in between paths. TODO: portability
+ */
+#define PATH_LIST_SEP ':'
+
+/**
+ * The maximum number of arguments when launching an external process.
+ */
+#define MAX_LAUNCH_ARGS 32
+
+/**
+ * Retry an operation that may get EINTR.
+ * The operation must return a non-negative value on success.
+ */
+#define RETRY_ON_EINTR(ret, expr) do { \
+ ret = expr; \
+ if (ret >= 0) \
+ break; \
+} while (errno == EINTR);
+
+#define MINI_HTRACED_LAUNCH_REDIRECT_FDS 0x1
+
+static void mini_htraced_open_snsock(struct mini_htraced *ht, char *err,
+ size_t err_len);
+
+static void mini_htraced_write_conf_file(struct mini_htraced *ht,
+ char *err, size_t err_len);
+
+static int mini_htraced_write_conf_key(FILE *fp, const char *key,
+ const char *fmt, ...)
+ __attribute__((format(printf, 3, 4)));
+
+static void mini_htraced_launch_daemon(struct mini_htraced *ht,
+ char *err, size_t err_len);
+
+/**
+ * Launch an external process.
+ *
+ * @param ht The mini htraced object.
+ * @param path The binary to launch
+ * @param err (out param) the error, or empty string on success.
+ * @param err_len The length of the error buffer.
+ * @param flags The flags.
+ * MINI_HTRACED_LAUNCH_REDIRECT_FDS: redirect
+ * stderr, stdout, stdin to null.
+ * finished successfully.
+ * @param ... Additional arguments to pass to the process.
+ * NULL_terminated. The first argument will
+ * always be the path to the binary.
+ *
+ * @return The new process ID, on success. -1 on failure.
+ * The error string will always be set on failure.
+ */
+pid_t mini_htraced_launch(const struct mini_htraced *ht, const char *path,
+ char *err, size_t err_len, int flags, ...)
+ __attribute__((sentinel));
+
+
+static void mini_htraced_read_startup_notification(struct mini_htraced *ht,
+ char *err, size_t err_len);
+
+static void parse_startup_notification(struct mini_htraced *ht,
+ char *ndata, size_t ndata_len,
+ char *err, size_t err_len);
+
+void mini_htraced_build(const struct mini_htraced_params *params,
+ struct mini_htraced **hret,
+ char *err, size_t err_len)
+{
+ struct mini_htraced *ht = NULL;
+ int i, ret;
+
+ err[0] = '\0';
+ ht = calloc(1, sizeof(*ht));
+ if (!ht) {
+ snprintf(err, err_len, "out of memory allocating mini_htraced object");
+ goto done;
+ }
+ ht->snsock = -1;
+ ht->root_dir = create_tempdir(params->name, 0777, err, err_len);
+ if (err[0]) {
+ goto done;
+ }
+ ret = register_tempdir_for_cleanup(ht->root_dir);
+ if (ret) {
+ snprintf(err, err_len, "register_tempdir_for_cleanup(%s) "
+ "failed: %s", ht->root_dir, terror(ret));
+ goto done;
+ }
+ for (i = 0; i < NUM_DATA_DIRS; i++) {
+ if (asprintf(ht->data_dir + i, "%s/dir%d", ht->root_dir, i) < 0) {
+ ht->data_dir[i] = NULL;
+ snprintf(err, err_len, "failed to create path to data dir %d", i);
+ goto done;
+ }
+ }
+ if (asprintf(&ht->htraced_log_path, "%s/htraced.log", ht->root_dir) < 0) {
+ ht->htraced_log_path = NULL;
+ snprintf(err, err_len, "failed to create path to htraced.log");
+ goto done;
+ }
+ if (asprintf(&ht->htraced_conf_path, "%s/htraced-conf.xml",
+ ht->root_dir) < 0) {
+ ht->htraced_conf_path = NULL;
+ snprintf(err, err_len, "failed to create path to htraced-conf.xml");
+ goto done;
+ }
+ mini_htraced_open_snsock(ht, err, err_len);
+ if (err[0]) {
+ goto done;
+ }
+ mini_htraced_write_conf_file(ht, err, err_len);
+ if (err[0]) {
+ goto done;
+ }
+ mini_htraced_launch_daemon(ht, err, err_len);
+ if (err[0]) {
+ goto done;
+ }
+ mini_htraced_read_startup_notification(ht, err, err_len);
+ if (err[0]) {
+ goto done;
+ }
+ if (asprintf(&ht->client_conf_defaults, "%s=%s",
+ HTRACED_ADDRESS_KEY, ht->htraced_http_addr) < 0) {
+ ht->client_conf_defaults = NULL;
+ snprintf(err, err_len, "failed to allocate client conf defaults.");
+ goto done;
+ }
+ *hret = ht;
+ err[0] = '\0';
+
+done:
+ if (err[0]) {
+ mini_htraced_free(ht);
+ }
+}
+
+static int do_waitpid(pid_t pid, char *err, size_t err_len)
+{
+ err[0] = '\0';
+ while (1) {
+ int status, res = waitpid(pid, &status, 0);
+ if (res < 0) {
+ if (errno == EINTR) {
+ continue;
+ }
+ snprintf(err, err_len, "waitpid(%lld) error: %s",
+ (long long)pid, terror(res));
+ return -1;
+ }
+ if (WIFEXITED(status)) {
+ return WEXITSTATUS(status);
+ }
+ return -1; // signal or other exit
+ }
+}
+
+void mini_htraced_stop(struct mini_htraced *ht)
+{
+ char err[512];
+ size_t err_len = sizeof(err);
+
+ if (!ht->htraced_pid_valid) {
+ return;
+ }
+ kill(ht->htraced_pid, SIGTERM);
+ ht->htraced_pid_valid = 0;
+ do_waitpid(ht->htraced_pid, err, err_len);
+ if (err[0]) {
+ fprintf(stderr, "%s\n", err);
+ }
+}
+
+void mini_htraced_free(struct mini_htraced *ht)
+{
+ int i;
+
+ if (!ht) {
+ return;
+ }
+ mini_htraced_stop(ht);
+ if (ht->root_dir) {
+ unregister_tempdir_for_cleanup(ht->root_dir);
+ if (!getenv("SKIP_CLEANUP")) {
+ recursive_unlink(ht->root_dir);
+ }
+ }
+ free(ht->root_dir);
+ for (i = 0; i < NUM_DATA_DIRS; i++) {
+ free(ht->data_dir[i]);
+ }
+ free(ht->htraced_log_path);
+ free(ht->htraced_conf_path);
+ free(ht->client_conf_defaults);
+ if (ht->snsock >= 0) {
+ close(ht->snsock);
+ ht->snsock = -1;
+ }
+ free(ht->htraced_http_addr);
+ free(ht);
+}
+
+static void mini_htraced_open_snsock(struct mini_htraced *ht, char *err,
+ size_t err_len)
+{
+ struct sockaddr_in snaddr;
+ socklen_t len = sizeof(snaddr);
+ struct timeval tv;
+
+ err[0] = '\0';
+ memset(&snaddr, 0, sizeof(snaddr));
+ snaddr.sin_family = AF_INET;
+ snaddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ ht->snsock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (ht->snsock < 0) {
+ int res = errno;
+ snprintf(err, err_len, "Failed to create new socket: %s\n",
+ terror(res));
+ return;
+ }
+ if (bind(ht->snsock, (struct sockaddr *) &snaddr, sizeof(snaddr)) < 0) {
+ int res = errno;
+ snprintf(err, err_len, "bind failed: %s\n", terror(res));
+ return;
+ }
+ if (getsockname(ht->snsock, (struct sockaddr *)&snaddr, &len) < 0) {
+ int res = errno;
+ snprintf(err, err_len, "getsockname failed: %s\n", terror(res));
+ return;
+ }
+ ht->snport = ntohs(snaddr.sin_port);
+ if (listen(ht->snsock, 32) < 0) {
+ int res = errno;
+ snprintf(err, err_len, "listen failed: %s\n", terror(res));
+ return;
+ }
+ // On Linux, at least, this makes accept() time out after 30 seconds. I'm
+ // too lazy to use select() here.
+ tv.tv_sec = 30;
+ tv.tv_usec = 0;
+ setsockopt(ht->snsock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+}
+
+static void mini_htraced_write_conf_file(struct mini_htraced *ht,
+ char *err, size_t err_len)
+{
+ FILE *fp;
+ int res;
+
+ err[0] = '\0';
+ fp = fopen(ht->htraced_conf_path, "w");
+ if (!fp) {
+ res = errno;
+ snprintf(err, err_len, "fopen(%s) failed: %s",
+ ht->htraced_conf_path, terror(res));
+ goto error;
+ }
+ if (fprintf(fp, "\
+<?xml version=\"1.0\"?>\n\
+<?xml-stylesheet type=\"text/xsl\" href=\"configuration.xsl\"?>\n\
+<configuration>\n") < 0) {
+ goto ioerror;
+ }
+ if (mini_htraced_write_conf_key(fp, "log.path", "%s",
+ ht->htraced_log_path)) {
+ goto ioerror;
+ }
+ if (mini_htraced_write_conf_key(fp, "web.address", "127.0.0.1:0")) {
+ goto ioerror;
+ }
+ if (mini_htraced_write_conf_key(fp, "data.store.directories",
+ "%s%c%s", ht->data_dir[0], PATH_LIST_SEP, ht->data_dir[1])) {
+ goto ioerror;
+ }
+ if (mini_htraced_write_conf_key(fp, "startup.notification.address",
+ "localhost:%d", ht->snport)) {
+ goto ioerror;
+ }
+ if (mini_htraced_write_conf_key(fp, "log.level", "%s", "TRACE")) {
+ goto ioerror;
+ }
+ if (fprintf(fp, "</configuration>\n") < 0) {
+ goto ioerror;
+ }
+ res = fclose(fp);
+ if (res) {
+ snprintf(err, err_len, "fclose(%s) failed: %s",
+ ht->htraced_conf_path, terror(res));
+ }
+ return;
+
+ioerror:
+ snprintf(err, err_len, "fprintf(%s) error",
+ ht->htraced_conf_path);
+error:
+ if (fp) {
+ fclose(fp);
+ }
+}
+
+static int mini_htraced_write_conf_key(FILE *fp, const char *key,
+ const char *fmt, ...)
+{
+ va_list ap;
+
+ if (fprintf(fp, " <property>\n <name>%s</name>\n <value>",
+ key) < 0) {
+ return 1;
+ }
+ va_start(ap, fmt);
+ if (vfprintf(fp, fmt, ap) < 0) {
+ va_end(ap);
+ return 1;
+ }
+ va_end(ap);
+ if (fprintf(fp, "</value>\n </property>\n") < 0) {
+ return 1;
+ }
+ return 0;
+}
+
+pid_t mini_htraced_launch(const struct mini_htraced *ht, const char *path,
+ char *err, size_t err_len, int flags, ...)
+{
+ pid_t pid;
+ int res, num_args = 0;
+ va_list ap;
+ const char *args[MAX_LAUNCH_ARGS + 1];
+ const char * const env[] = {
+ "HTRACED_CONF_DIR=.", "HTRACED_WEB_DIR=", NULL
+ };
+
+ err[0] = '\0';
+ if (access(path, X_OK) < 0) {
+ snprintf(err, err_len, "The %s binary is not accessible and "
+ "executable.", path);
+ return -1;
+ }
+ va_start(ap, flags);
+ args[num_args++] = path;
+ while (1) {
+ const char *arg = va_arg(ap, char*);
+ if (!arg) {
+ break;
+ }
+ if (num_args >= MAX_LAUNCH_ARGS) {
+ va_end(ap);
+ snprintf(err, err_len, "Too many arguments to launch! The "
+ "maximum number of arguments is %d.\n", MAX_LAUNCH_ARGS);
+ return -1;
+ }
+ args[num_args++] = arg;
+ }
+ va_end(ap);
+ args[num_args++] = NULL;
+
+ pid = fork();
+ if (pid == -1) {
+ // Fork failed.
+ res = errno;
+ snprintf(err, err_len, "fork() failed for %s: %s\n",
+ path, terror(res));
+ return -1;
+ } else if (pid == 0) {
+ // Child process.
+ // We don't want to delete the temporary directory when this child
+ // process exists. The parent process is responsible for that, if it
+ // is to be done at all.
+ unregister_tempdir_for_cleanup(ht->root_dir);
+
+ // Make things nicer by exiting when the parent process exits.
+ prctl(PR_SET_PDEATHSIG, SIGHUP);
+
+ if (flags & MINI_HTRACED_LAUNCH_REDIRECT_FDS) {
+ int null_fd;
+ RETRY_ON_EINTR(null_fd, open("/dev/null", O_WRONLY));
+ if (null_fd < 0) {
+ _exit(127);
+ }
+ RETRY_ON_EINTR(res, dup2(null_fd, STDOUT_FILENO));
+ if (res < 0) {
+ _exit(127);
+ }
+ RETRY_ON_EINTR(res, dup2(null_fd, STDERR_FILENO));
+ if (res < 0) {
+ _exit(127);
+ }
+ }
+ if (chdir(ht->root_dir) < 0) {
+ _exit(127);
+ }
+ execve(path, (char *const*)args, (char * const*)env);
+ _exit(127);
+ }
+ // Parent process.
+ return pid;
+}
+
+static void mini_htraced_launch_daemon(struct mini_htraced *ht,
+ char *err, size_t err_len)
+{
+ int flags = 0;
+ pid_t pid;
+
+ if (!getenv("SKIP_CLEANUP")) {
+ flags |= MINI_HTRACED_LAUNCH_REDIRECT_FDS;
+ }
+ pid = mini_htraced_launch(ht, HTRACED_ABSPATH, err, err_len, flags, NULL);
+ if (err[0]) {
+ return;
+ }
+ ht->htraced_pid_valid = 1;
+ ht->htraced_pid = pid;
+}
+
+void mini_htraced_dump_spans(struct mini_htraced *ht,
+ char *err, size_t err_len,
+ const char *path)
+{
+ pid_t pid;
+ int ret;
+ char *addr = NULL;
+
+ err[0] = '\0';
+ if (asprintf(&addr, "--addr=%s", ht->htraced_http_addr) < 0) {
+ addr = NULL;
+ snprintf(err, err_len, "OOM while allocating the addr string");
+ return;
+ }
+ pid = mini_htraced_launch(ht, HTRACE_ABSPATH, err, err_len, 0,
+ addr, "dumpAll", path, NULL);
+ free(addr);
+ if (err[0]) {
+ return;
+ }
+ ret = do_waitpid(pid, err, err_len);
+ if (err[0]) {
+ return;
+ }
+ if (ret != EXIT_SUCCESS) {
+ snprintf(err, err_len, "%s returned non-zero exit status %d\n",
+ HTRACE_ABSPATH, ret);
+ return;
+ }
+}
+
+static void mini_htraced_read_startup_notification(struct mini_htraced *ht,
+ char *err, size_t err_len)
+{
+ char *ndata = NULL;
+ int res, sock = -1;
+ size_t ndata_len = 0;
+
+ err[0] = '\0';
+ ndata = malloc(MAX_NDATA);
+ if (!ndata) {
+ snprintf(err, err_len, "failed to allocate %d byte buffer for "
+ "notification data.", MAX_NDATA);
+ goto done;
+ }
+ RETRY_ON_EINTR(sock, accept(ht->snsock, NULL, NULL));
+ if (sock < 0) {
+ int e = errno;
+ snprintf(err, err_len, "accept failed: %s", terror(e));
+ goto done;
+ }
+ while (ndata_len < MAX_NDATA) {
+ res = recv(sock, ndata + ndata_len, MAX_NDATA - ndata_len, 0);
+ if (res == 0) {
+ break;
+ }
+ if (res < 0) {
+ int e = errno;
+ if (e == EINTR) {
+ continue;
+ }
+ snprintf(err, err_len, "recv error: %s", terror(e));
+ goto done;
+ }
+ ndata_len += res;
+ }
+ parse_startup_notification(ht, ndata, ndata_len, err, err_len);
+ if (err[0]) {
+ goto done;
+ }
+
+done:
+ if (sock >= 0) {
+ close(sock);
+ }
+ free(ndata);
+}
+
+static void parse_startup_notification(struct mini_htraced *ht,
+ char *ndata, size_t ndata_len,
+ char *err, size_t err_len)
+{
+ struct json_tokener *tok = NULL;
+ struct json_object *root = NULL, *http_addr, *process_id;
+ int32_t pid;
+
+ err[0] = '\0';
+ tok = json_tokener_new();
+ if (!tok) {
+ snprintf(err, err_len, "json_tokener_new failed.");
+ goto done;
+ }
+ root = json_tokener_parse_ex(tok, ndata, ndata_len);
+ if (!root) {
+ enum json_tokener_error jerr = json_tokener_get_error(tok);
+ snprintf(err, err_len, "Failed to parse startup notification: %s.",
+ json_tokener_error_desc(jerr));
+ goto done;
+ }
+ // Find the http address, in the form of hostname:port, which the htraced
+ // is listening on.
+ if (!json_object_object_get_ex(root, "HttpAddr", &http_addr)) {
+ snprintf(err, err_len, "Failed to find HttpAddr in the startup "
+ "notification.");
+ goto done;
+ }
+ ht->htraced_http_addr = strdup(json_object_get_string(http_addr));
+ if (!ht->htraced_http_addr) {
+ snprintf(err, err_len, "OOM");
+ goto done;
+ }
+ // Check that the process ID from the startup notification matches the
+ // process ID from the fork.
+ if (!json_object_object_get_ex(root, "ProcessId", &process_id)) {
+ snprintf(err, err_len, "Failed to find ProcessId in the startup "
+ "notification.");
+ goto done;
+ }
+ pid = json_object_get_int(process_id);
+ if (pid != ht->htraced_pid) {
+ snprintf(err, err_len, "Startup notification pid was %lld, but the "
+ "htraced process id was %lld.",
+ (long long)pid, (long long)ht->htraced_pid);
+ goto done;
+ }
+
+done:
+ json_tokener_free(tok);
+ if (root) {
+ json_object_put(root);
+ }
+}
+
+// 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.h
----------------------------------------------------------------------
diff --git a/htrace-c/src/test/mini_htraced.h b/htrace-c/src/test/mini_htraced.h
new file mode 100644
index 0000000..a803f55
--- /dev/null
+++ b/htrace-c/src/test/mini_htraced.h
@@ -0,0 +1,154 @@
+/**
+ * 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_MINI_HTRACED_H
+#define APACHE_HTRACE_TEST_MINI_HTRACED_H
+
+/**
+ * @file mini_htraced.h
+ *
+ * Implements a mini htraced cluster which can be used in unit tests.
+ *
+ * This is useful for testing the htraced trace sink of the native client.
+ *
+ * This is an internal header, not intended for external use.
+ */
+
+#include <unistd.h> /* for pid_t and size_t */
+
+struct htrace_conf;
+
+#define NUM_DATA_DIRS 2
+
+struct mini_htraced_params {
+ /**
+ * The name of the mini htraced process to start.
+ * This shows up in the test directory name and some other places.
+ * The memory should be managed by the caller.
+ */
+ const char *name;
+
+ /**
+ * The configuration to use, in string form.
+ * The memory should be managed by the caller.
+ */
+ const char *confstr;
+};
+
+struct mini_htraced {
+ /**
+ * The process ID of the mini htraced.
+ */
+ int pid;
+
+ /**
+ * The path to the root directory that all the state associated with this
+ * mini_htraced will be placed under. Malloced.
+ */
+ char *root_dir;
+
+ /**
+ * Paths to the data directories to use for htraced. Malloced.
+ */
+ char *data_dir[NUM_DATA_DIRS];
+
+ /**
+ * Path to the server's log file. Malloced.
+ */
+ char *htraced_log_path;
+
+ /**
+ * Path to the server's conf file. Malloced.
+ */
+ char *htraced_conf_path;
+
+ /**
+ * The client configuration defaults. Malloced.
+ */
+ char *client_conf_defaults;
+
+ /**
+ * The startup notification port.
+ */
+ int snport;
+
+ /**
+ * The startup notification socket, or -1 if the socket has been closed.
+ */
+ int snsock;
+
+ /**
+ * Nonzero if the htraced pid is valid.
+ */
+ int htraced_pid_valid;
+
+ /**
+ * The process ID of the htraced pid.
+ */
+ pid_t htraced_pid;
+
+ /**
+ * The HTTP address of the htraced, in hostname:port format.
+ */
+ char *htraced_http_addr;
+};
+
+/**
+ * Build the mini HTraced cluster.
+ *
+ * @param params The parameters to use.
+ * @param ht (out param) The mini htraced object on success.
+ * @param err (out param) The error message if there was an
+ * error.
+ * @param err_len The length of the error buffer provided by the
+ * caller.
+ */
+void mini_htraced_build(const struct mini_htraced_params *params, struct mini_htraced **ht,
+ char *err, size_t err_len);
+
+/**
+ * Stop the htraced process.
+ *
+ * @param ht The mini htraced object.
+ */
+void mini_htraced_stop(struct mini_htraced *ht);
+
+/**
+ * Free the memory associated with the mini htraced object.
+ *
+ * @param ht The mini htraced object.
+ */
+void mini_htraced_free(struct mini_htraced *ht);
+
+/**
+ * Dump the spans contained in this htraced instance to a file.
+ *
+ * @param ht The mini htraced object.
+ * @param err (out param) The error message if there was an
+ * error.
+ * @param err_len The length of the error buffer provided by the
+ * caller.
+ * @param path The path to dump the spans to, in json form.
+ */
+void mini_htraced_dump_spans(struct mini_htraced *ht,
+ char *err, size_t err_len,
+ const char *path);
+
+#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/process_id-unit.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/test/process_id-unit.c b/htrace-c/src/test/process_id-unit.c
new file mode 100644
index 0000000..5454eb0
--- /dev/null
+++ b/htrace-c/src/test/process_id-unit.c
@@ -0,0 +1,80 @@
+/**
+ * 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/process_id.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+static struct htrace_conf *g_cnf;
+
+static struct htrace_log *g_lg;
+
+static int test_calculate_process_id(const char *expected, const char *fmt)
+{
+ char *process_id;
+
+ process_id = calculate_process_id(g_lg, fmt, "fooproc");
+ EXPECT_NONNULL(process_id);
+ EXPECT_STR_EQ(expected, process_id);
+ free(process_id);
+
+ return EXIT_SUCCESS;
+}
+
+static int test_calculate_process_ids(void)
+{
+ char buf[128];
+
+ EXPECT_INT_ZERO(test_calculate_process_id("my.name", "my.name"));
+ EXPECT_INT_ZERO(test_calculate_process_id("my.fooproc", "my.%{tname}"));
+ EXPECT_INT_ZERO(test_calculate_process_id("\%foo", "\%foo"));
+ EXPECT_INT_ZERO(test_calculate_process_id("fooproc", "%{tname}"));
+ EXPECT_INT_ZERO(test_calculate_process_id("\\fooproc", "\\\\%{tname}"));
+ EXPECT_INT_ZERO(test_calculate_process_id("\%{tname}", "\\%{tname}"));
+
+ snprintf(buf, sizeof(buf), "me.%lld", (long long)getpid());
+ EXPECT_INT_ZERO(test_calculate_process_id(buf, "me.%{pid}"));
+
+ get_best_ip(g_lg, buf, sizeof(buf));
+ EXPECT_INT_ZERO(test_calculate_process_id(buf, "%{ip}"));
+
+ return EXIT_SUCCESS;
+}
+
+int main(void)
+{
+ g_cnf = htrace_conf_from_strs("", "");
+ EXPECT_NONNULL(g_cnf);
+ g_lg = htrace_log_alloc(g_cnf);
+ EXPECT_NONNULL(g_lg);
+ EXPECT_INT_ZERO(test_calculate_process_ids());
+ htrace_log_free(g_lg);
+ htrace_conf_free(g_cnf);
+
+ 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/rand-unit.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/test/rand-unit.c b/htrace-c/src/test/rand-unit.c
new file mode 100644
index 0000000..c02dbf1
--- /dev/null
+++ b/htrace-c/src/test/rand-unit.c
@@ -0,0 +1,93 @@
+/**
+ * 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/rand.h"
+
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct htrace_log *g_rand_unit_lg;
+
+#define ARRAY_SIZE 128
+
+static int compare_u32(const void *v1, const void *v2)
+{
+ uint32_t a = *(uint32_t *)v1;
+ uint32_t b = *(uint32_t *)v2;
+
+ if (a < b) {
+ return -1;
+ } else if (b < a) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/**
+ * Test that we can generate an array of unique uint32_t objects.
+ */
+static int test_u32_uniqueness(void)
+{
+ struct random_src *rnd = random_src_alloc(g_rand_unit_lg);
+ uint32_t prev, *arr;
+ int i, duplicate;
+
+ do {
+ arr = calloc(ARRAY_SIZE, sizeof(uint32_t));
+ EXPECT_NONNULL(arr);
+ for (i = 0; i < ARRAY_SIZE; i++) {
+ arr[i] = random_u32(rnd);
+ }
+ qsort(arr, ARRAY_SIZE, sizeof(arr[0]), compare_u32);
+ prev = 0;
+ duplicate = 0;
+ for (i = 0; i < ARRAY_SIZE; i++) {
+ if (arr[i] == prev) {
+ duplicate = 1;
+ }
+ prev = arr[i];
+ }
+ } while (duplicate);
+ random_src_free(rnd);
+ free(arr);
+ return EXIT_SUCCESS;
+}
+
+int main(void)
+{
+ struct htrace_conf *conf;
+
+ conf = htrace_conf_from_strs("", "");
+ EXPECT_NONNULL(conf);
+ g_rand_unit_lg = htrace_log_alloc(conf);
+ EXPECT_NONNULL(g_rand_unit_lg);
+ EXPECT_INT_ZERO(test_u32_uniqueness());
+ htrace_log_free(g_rand_unit_lg);
+ htrace_conf_free(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/rtest.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/test/rtest.c b/htrace-c/src/test/rtest.c
new file mode 100644
index 0000000..0ec5272
--- /dev/null
+++ b/htrace-c/src/test/rtest.c
@@ -0,0 +1,155 @@
+/**
+ * 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/span.h"
+#include "test/rtest.h"
+#include "test/span_table.h"
+#include "test/span_util.h"
+#include "test/test.h"
+#include "util/log.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define RECEIVER_TEST_TNAME "receiver-unit"
+
+/**
+ * @file rtestpp.cc
+ *
+ * The C receiver tests.
+ */
+
+struct rtest_data {
+ struct htrace_conf *cnf;
+ struct htrace_sampler *always;
+ struct htracer *tracer;
+};
+
+static void get_receiver_test_prid(char *prid, size_t prid_len)
+{
+ snprintf(prid, prid_len, RECEIVER_TEST_TNAME "/%lld", (long long)getpid());
+}
+
+static int rtest_data_init(const char *conf_str, struct rtest_data **out)
+{
+ struct rtest_data *rdata = calloc(1, sizeof(*(rdata)));
+ EXPECT_NONNULL(rdata);
+ rdata->cnf = htrace_conf_from_strs(conf_str,
+ HTRACE_PROCESS_ID"=%{tname}/%{pid};sampler=always");
+ EXPECT_NONNULL(rdata->cnf);
+ rdata->tracer = htracer_create(RECEIVER_TEST_TNAME, rdata->cnf);
+ EXPECT_NONNULL(rdata->tracer);
+ rdata->always = htrace_sampler_create(rdata->tracer, rdata->cnf);
+ EXPECT_NONNULL(rdata->always);
+ *out = rdata;
+ return EXIT_SUCCESS;
+}
+
+static void rtest_data_free(struct rtest_data *rdata)
+{
+ htrace_sampler_free(rdata->always);
+ rdata->always = NULL;
+ htracer_free(rdata->tracer);
+ rdata->tracer = NULL;
+ htrace_conf_free(rdata->cnf);
+ rdata->cnf = NULL;
+ free(rdata);
+}
+
+static int rtest_verify_table_size(struct rtest *rt, struct span_table *st)
+{
+ EXPECT_INT_EQ(rt->spans_created, span_table_size(st));
+
+ return EXIT_SUCCESS;
+}
+
+static int doit(struct rtest_data *rdata)
+{
+ struct htrace_scope *scope1, *scope2, *scope2_5;
+
+ scope1 = htrace_start_span(rdata->tracer, NULL, "part1");
+ EXPECT_UINT64_GT(0L, htrace_scope_get_span_id(scope1));
+ htrace_scope_close(scope1);
+ scope2 = htrace_start_span(rdata->tracer, NULL, "part2");
+ EXPECT_UINT64_GT(0L, htrace_scope_get_span_id(scope2));
+ scope2_5 = htrace_start_span(rdata->tracer, NULL, "part2.5");
+ htrace_scope_close(scope2_5);
+ htrace_scope_close(scope2);
+ return EXIT_SUCCESS;
+}
+
+int rtest_simple_run(struct rtest *rt, const char *conf_str)
+{
+ struct htrace_scope *scope0;
+ struct rtest_data *rdata = NULL;
+
+ EXPECT_INT_ZERO(rtest_data_init(conf_str, &rdata));
+ EXPECT_NONNULL(rdata);
+ scope0 = htrace_start_span(rdata->tracer, rdata->always, "doit");
+ doit(rdata);
+ htrace_scope_close(scope0);
+ rt->spans_created = 4;
+ rtest_data_free(rdata);
+ return EXIT_SUCCESS;
+}
+
+int rtest_simple_verify(struct rtest *rt, struct span_table *st)
+{
+ struct htrace_span *span;
+ uint64_t doit_id, part2_id;
+ char prid[128];
+
+ EXPECT_INT_ZERO(rtest_verify_table_size(rt, st));
+ get_receiver_test_prid(prid, sizeof(prid));
+ EXPECT_INT_ZERO(span_table_get(st, &span, "doit", prid));
+ doit_id = span->span_id;
+ EXPECT_INT_ZERO(span->num_parents);
+
+ EXPECT_INT_ZERO(span_table_get(st, &span, "part1", prid));
+ EXPECT_INT_EQ(1, span->num_parents);
+ EXPECT_UINT64_EQ(doit_id, span->parent.single);
+
+ EXPECT_INT_ZERO(span_table_get(st, &span, "part2", prid));
+ EXPECT_INT_EQ(1, span->num_parents);
+ part2_id = span->span_id;
+ EXPECT_UINT64_EQ(doit_id, span->parent.single);
+
+ EXPECT_INT_ZERO(span_table_get(st, &span, "part2.5", prid));
+ EXPECT_INT_EQ(1, span->num_parents);
+ EXPECT_UINT64_EQ(part2_id, span->parent.single);
+
+ return EXIT_SUCCESS;
+}
+
+static struct rtest g_rtest_simple = {
+ "rtest_simple",
+ rtest_simple_run,
+ rtest_simple_verify,
+};
+
+struct rtest * const g_rtests[] = {
+ &g_rtest_simple,
+ NULL
+};
+
+// vim: ts=4:sw=4:tw=79:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/test/rtest.h
----------------------------------------------------------------------
diff --git a/htrace-c/src/test/rtest.h b/htrace-c/src/test/rtest.h
new file mode 100644
index 0000000..52d0309
--- /dev/null
+++ b/htrace-c/src/test/rtest.h
@@ -0,0 +1,76 @@
+/**
+ * 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_RTEST_H
+#define APACHE_HTRACE_TEST_RTEST_H
+
+/**
+ * @file rtest.h
+ *
+ * Declares receiver tests.
+ *
+ * This is an internal header, not intended for external use.
+ */
+
+struct span_table;
+
+/**
+ * A receiver test.
+ */
+struct rtest {
+ /**
+ * The name of the receiver test.
+ */
+ const char *name;
+
+ /**
+ * Run the receiver test.
+ *
+ * @param rt The receiver test.
+ * @param conf_str The configuration string to use.
+ *
+ * @return zero on success
+ */
+ int (*run)(struct rtest *rt, const char *conf_str);
+
+ /**
+ * Verify that the receiver test succeeded.
+ *
+ * @param rt The receiver test.
+ * @param st The span table.
+ *
+ * @return zero on success
+ */
+ int (*verify)(struct rtest *rt, struct span_table *st);
+
+ /**
+ * The number of spans that have been created by this test.
+ */
+ int spans_created;
+};
+
+/**
+ * A NULL-terminated list of pointers to rtests.
+ */
+extern struct rtest * const g_rtests[];
+
+#define RECEIVER_TEST_TNAME "receiver-unit"
+
+#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/rtestpp.cc
----------------------------------------------------------------------
diff --git a/htrace-c/src/test/rtestpp.cc b/htrace-c/src/test/rtestpp.cc
new file mode 100644
index 0000000..649826c
--- /dev/null
+++ b/htrace-c/src/test/rtestpp.cc
@@ -0,0 +1,154 @@
+/**
+ * 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.
+ */
+
+#define __STDC_FORMAT_MACROS
+
+#include "core/htrace.hpp"
+
+extern "C" {
+#include "core/conf.h"
+#include "core/span.h"
+#include "test/rtest.h"
+#include "test/span_table.h"
+#include "test/span_util.h"
+#include "test/test.h"
+#include "util/log.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+}
+
+/**
+ * @file rtestpp.cc
+ *
+ * The C++ receiver tests.
+ */
+
+using std::string;
+
+class RTestData {
+public:
+ RTestData(struct rtest *rt, const char *conf_str)
+ : rt_(rt),
+ cnf_(string(HTRACE_PROCESS_ID"=%{tname}/%{pid};sampler=always;") +
+ string(conf_str).c_str()),
+ tracer_(RECEIVER_TEST_TNAME, cnf_),
+ always_(&tracer_, cnf_)
+ {
+ }
+
+ // Test that initialization succeeeded
+ int TestInit() {
+ EXPECT_STR_EQ(RECEIVER_TEST_TNAME, tracer_.Name().c_str());
+ EXPECT_STR_EQ("AlwaysSampler", always_.ToString().c_str());
+ return EXIT_SUCCESS;
+ }
+
+ struct rtest *rt_;
+ htrace::Conf cnf_;
+ htrace::Tracer tracer_;
+ htrace::Sampler always_;
+
+private:
+ RTestData(const RTestData &other); // disallow copying
+ RTestData& operator=(const RTestData &other); // disallow assignment
+};
+
+static void get_receiver_test_prid(char *prid, size_t prid_len)
+{
+ snprintf(prid, prid_len, RECEIVER_TEST_TNAME "/%lld", (long long)getpid());
+}
+
+static int rtest_generic_verify(struct rtest *rt, struct span_table *st)
+{
+ EXPECT_INT_EQ(rt->spans_created, span_table_size(st));
+
+ return EXIT_SUCCESS;
+}
+
+static int doit(RTestData &tdata, struct rtest *rt)
+{
+ {
+ htrace::Scope scope1(tdata.tracer_, "part1");
+ EXPECT_UINT64_GT(0L, scope1.GetSpanId());
+ }
+ {
+ htrace::Scope scope2(tdata.tracer_, "part2");
+ EXPECT_UINT64_GT(0L, scope2.GetSpanId());
+ {
+ htrace::Scope scope2_5(tdata.tracer_, "part2.5");
+ EXPECT_UINT64_GT(0L, scope2_5.GetSpanId());
+ }
+ }
+ return EXIT_SUCCESS;
+}
+
+int rtestpp_simple_run(struct rtest *rt, const char *conf_str)
+{
+ RTestData tdata(rt, conf_str);
+ EXPECT_INT_ZERO(tdata.TestInit());
+
+ htrace::Scope scope0(tdata.tracer_, tdata.always_, "doit");
+ doit(tdata, rt);
+ rt->spans_created = 4;
+ return EXIT_SUCCESS;
+}
+
+int rtestpp_simple_verify(struct rtest *rt, struct span_table *st)
+{
+ struct htrace_span *span;
+ uint64_t doit_id, part2_id;
+ char prid[128];
+
+ EXPECT_INT_ZERO(rtest_generic_verify(rt, st));
+ get_receiver_test_prid(prid, sizeof(prid));
+ EXPECT_INT_ZERO(span_table_get(st, &span, "doit", prid));
+ doit_id = span->span_id;
+ EXPECT_INT_ZERO(span->num_parents);
+
+ EXPECT_INT_ZERO(span_table_get(st, &span, "part1", prid));
+ EXPECT_INT_EQ(1, span->num_parents);
+ EXPECT_UINT64_EQ(doit_id, span->parent.single);
+
+ EXPECT_INT_ZERO(span_table_get(st, &span, "part2", prid));
+ EXPECT_INT_EQ(1, span->num_parents);
+ part2_id = span->span_id;
+ EXPECT_UINT64_EQ(doit_id, span->parent.single);
+
+ EXPECT_INT_ZERO(span_table_get(st, &span, "part2.5", prid));
+ EXPECT_INT_EQ(1, span->num_parents);
+ EXPECT_UINT64_EQ(part2_id, span->parent.single);
+
+ return EXIT_SUCCESS;
+}
+
+struct rtest g_rtestpp_simple = {
+ "rtestpp_simple",
+ rtestpp_simple_run,
+ rtestpp_simple_verify,
+};
+
+struct rtest * const g_rtests[] = {
+ &g_rtestpp_simple,
+ NULL
+};
+
+// vim: ts=4:sw=4:tw=79:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/test/sampler-unit.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/test/sampler-unit.c b/htrace-c/src/test/sampler-unit.c
new file mode 100644
index 0000000..2a42292
--- /dev/null
+++ b/htrace-c/src/test/sampler-unit.c
@@ -0,0 +1,138 @@
+/**
+ * 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 "sampler/sampler.h"
+#include "test/test.h"
+#include "util/log.h"
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static struct htrace_conf *g_test_conf;
+
+static struct htrace_log *g_test_lg;
+
+static struct htracer *g_test_tracer;
+
+#define NUM_TEST_SAMPLES 1000
+
+static int test_simple_sampler(const char *name, int expected)
+{
+ struct htrace_conf *conf;
+ struct htrace_sampler *smp;
+ char confstr[256] = { 0 };
+ int i;
+
+ if (name) {
+ snprintf(confstr, sizeof(confstr), "%s=%s",
+ HTRACE_SAMPLER_KEY, name);
+ }
+ conf = htrace_conf_from_strs(confstr, "");
+ EXPECT_NONNULL(conf);
+ smp = htrace_sampler_create(g_test_tracer, conf);
+ EXPECT_NONNULL(smp);
+ for (i = 0; i < NUM_TEST_SAMPLES; i++) {
+ int s = smp->ty->next(smp);
+ EXPECT_INT_EQ(expected, s);
+ }
+ htrace_conf_free(conf);
+ htrace_sampler_free(smp);
+ return EXIT_SUCCESS;
+}
+
+static int test_unconfigured_sampler()
+{
+ return test_simple_sampler(NULL, 0);
+}
+
+static int test_never_sampler()
+{
+ return test_simple_sampler("never", 0);
+}
+
+static int test_always_sampler()
+{
+ return test_simple_sampler("always", 1);
+}
+
+#define NUM_PROB_TEST_SAMPLES 500000
+
+static int test_prob_sampler(double target, double slop)
+{
+ struct htrace_conf *conf;
+ struct htrace_sampler *smp;
+ char confstr[256] = { 0 };
+ double actual, diff;
+
+ snprintf(confstr, sizeof(confstr),
+ "sampler=prob;prob.sampler.fraction=%g", target);
+ conf = htrace_conf_from_strs(confstr, "");
+ EXPECT_NONNULL(conf);
+ smp = htrace_sampler_create(g_test_tracer, conf);
+ EXPECT_NONNULL(smp);
+ do {
+ int i;
+ uint64_t total = 0;
+
+ for (i = 0; i < NUM_PROB_TEST_SAMPLES; i++) {
+ int val = smp->ty->next(smp);
+ if ((val != 0) && (val != 1)) {
+ htrace_log(g_test_lg, "Invalid return from sampler: "
+ "expected 0 or 1, but got %d\n", val);
+ abort();
+ }
+ total += val;
+ }
+ actual = ((double)total) / (double)NUM_PROB_TEST_SAMPLES;
+ diff = fabs(target - actual);
+ htrace_log(g_test_lg, "After %d samples, fraction is %g. Target "
+ "was %g. %s\n", NUM_PROB_TEST_SAMPLES, actual, target,
+ (diff < slop) ? "Done. " : "Retrying.");
+ } while (diff >= slop);
+ htrace_conf_free(conf);
+ htrace_sampler_free(smp);
+ return EXIT_SUCCESS;
+}
+
+int main(void)
+{
+ g_test_conf = htrace_conf_from_strs("", HTRACE_PROCESS_ID"=sampler-unit");
+ EXPECT_NONNULL(g_test_conf);
+ g_test_lg = htrace_log_alloc(g_test_conf);
+ EXPECT_NONNULL(g_test_lg);
+ g_test_tracer = htracer_create("sampler-unit", g_test_conf);
+ EXPECT_NONNULL(g_test_tracer);
+
+ EXPECT_INT_ZERO(test_unconfigured_sampler());
+ EXPECT_INT_ZERO(test_never_sampler());
+ EXPECT_INT_ZERO(test_always_sampler());
+ EXPECT_INT_ZERO(test_prob_sampler(0.5, 0.001));
+ EXPECT_INT_ZERO(test_prob_sampler(0.01, 0.001));
+ EXPECT_INT_ZERO(test_prob_sampler(0.1, 0.001));
+
+ htracer_free(g_test_tracer);
+ 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/test/span-unit.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/test/span-unit.c b/htrace-c/src/test/span-unit.c
new file mode 100644
index 0000000..da6ca66
--- /dev/null
+++ b/htrace-c/src/test/span-unit.c
@@ -0,0 +1,173 @@
+/**
+ * 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 "sampler/sampler.h"
+#include "test/span_util.h"
+#include "test/test.h"
+#include "util/htable.h"
+#include "util/log.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define MAX_SPAN_JSON_LEN 100000
+
+static struct htrace_conf *g_test_conf;
+
+static struct htrace_log *g_test_lg;
+
+static struct htracer *g_test_tracer;
+
+static struct htrace_span *create_span(const char *desc,
+ uint64_t begin_ms, uint64_t end_ms, uint64_t span_id,
+ const char *prid, ...) __attribute__((sentinel));
+
+static int test_span_to_json(const char *expected,
+ struct htrace_span *span)
+{
+ int buf_len;
+ char *buf;
+ struct htrace_span *rspan = NULL;
+ char err[128];
+ size_t err_len = sizeof(err);
+
+ htrace_span_sort_and_dedupe_parents(span);
+ buf_len = span_json_size(span);
+ if ((0 > buf_len) || (buf_len > MAX_SPAN_JSON_LEN)) {
+ fprintf(stderr, "invalid span_json_size %d.\n", buf_len);
+ return EXIT_FAILURE;
+ }
+ buf = malloc(buf_len);
+ EXPECT_NONNULL(buf);
+ span_json_sprintf(span, buf_len, buf);
+ EXPECT_STR_EQ(expected, buf);
+ span_json_parse(buf, &rspan, err, err_len);
+ if (err[0]) {
+ fprintf(stderr, "Failed to parse span json %s: %s\n", buf, err);
+ return EXIT_FAILURE;
+ }
+ EXPECT_NONNULL(rspan);
+ if (span_compare(span, rspan) != 0) {
+ htrace_span_free(rspan);
+ fprintf(stderr, "Failed to parse the span json back into a span "
+ "which was identical to the input. JSON: %s\n", buf);
+ return EXIT_FAILURE;
+ }
+ free(buf);
+ htrace_span_free(rspan);
+ return EXIT_SUCCESS;
+}
+
+static struct htrace_span *create_span(const char *desc,
+ uint64_t begin_ms, uint64_t end_ms, uint64_t span_id,
+ const char *prid, ...)
+{
+ struct htrace_span* span = NULL;
+ uint64_t *parents, parent;
+ int i, num_parents = 0;
+ va_list ap, ap2;
+
+ va_start(ap, prid);
+ va_copy(ap2, ap);
+ while (1) {
+ parent = va_arg(ap2, uint64_t);
+ if (!parent) {
+ break;
+ }
+ num_parents++;
+ } while (parent);
+ va_end(ap2);
+ if (num_parents > 0) {
+ parents = xcalloc(sizeof(uint64_t) * num_parents);
+ for (i = 0; i < num_parents; i++) {
+ parents[i] = va_arg(ap, uint64_t);
+ }
+ }
+ va_end(ap);
+ span = htrace_span_alloc(desc, begin_ms, span_id);
+ span->end_ms = end_ms;
+ span->span_id = span_id;
+ span->prid = xstrdup(prid);
+ span->num_parents = num_parents;
+ if (num_parents == 1) {
+ span->parent.single = parents[0];
+ free(parents);
+ } else if (num_parents > 1) {
+ span->parent.list = parents;
+ }
+ return span;
+}
+
+static int test_spans_to_str(void)
+{
+ struct htrace_span *span;
+
+ span = create_span("foo", 123LLU, 456LLU, 789LLU, "span-unit",
+ NULL);
+ EXPECT_INT_ZERO(test_span_to_json(
+ "{\"s\":\"0000000000000315\",\"b\":123,\"e\":456,"
+ "\"d\":\"foo\",\"r\":\"span-unit\""
+ ",\"p\":[]}", span));
+ htrace_span_free(span);
+
+ span = create_span("myspan", 34359738368LLU,
+ 34359739368LLU, 68719476736LLU, "span-unit2",
+ 1LLU, 2LLU, 3LLU, NULL);
+ EXPECT_INT_ZERO(test_span_to_json(
+ "{\"s\":\"0000001000000000\",\"b\":34359738368,\"e\":34359739368,"
+ "\"d\":\"myspan\",\"r\":\"span-unit2\"," "\"p\":[\"0000000000000001\","
+ "\"0000000000000002\",\"0000000000000003\"]}", span));
+ htrace_span_free(span);
+
+ span = create_span("nextSpan", 14359739368LLU, 18719476736LLU,
+ 0x8000001000000000LLU, "span-unit3",
+ 1LLU, 1LLU, 1LLU, NULL);
+ EXPECT_INT_ZERO(test_span_to_json(
+ "{\"s\":\"8000001000000000\",\"b\":14359739368,\"e\":18719476736,"
+ "\"d\":\"nextSpan\",\"r\":\"span-unit3\"," "\"p\":[\"0000000000000001\"]"
+ "}", span));
+ htrace_span_free(span);
+ return EXIT_SUCCESS;
+}
+
+int main(void)
+{
+ g_test_conf = htrace_conf_from_strs("", HTRACE_PROCESS_ID"=span-unit");
+ EXPECT_NONNULL(g_test_conf);
+ g_test_lg = htrace_log_alloc(g_test_conf);
+ EXPECT_NONNULL(g_test_lg);
+ g_test_tracer = htracer_create("span-unit", g_test_conf);
+ EXPECT_NONNULL(g_test_tracer);
+
+ EXPECT_INT_ZERO(test_spans_to_str());
+
+ htracer_free(g_test_tracer);
+ 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/test/span_table.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/test/span_table.c b/htrace-c/src/test/span_table.c
new file mode 100644
index 0000000..8840fbf
--- /dev/null
+++ b/htrace-c/src/test/span_table.c
@@ -0,0 +1,139 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "core/span.h"
+#include "test/span_table.h"
+#include "test/span_util.h"
+#include "test/test.h"
+#include "util/htable.h"
+#include "util/log.h"
+
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+struct span_table *span_table_alloc(void)
+{
+ struct htable *ht;
+
+ ht = htable_alloc(128, ht_hash_string, ht_compare_string);
+ if (!ht) {
+ return NULL;
+ }
+ return (struct span_table*)ht;
+}
+
+int span_table_get(struct span_table *st, struct htrace_span **out,
+ const char *desc, const char *prid)
+{
+ struct htable *ht = (struct htable *)st;
+ struct htrace_span *span;
+
+ span = htable_get(ht, desc);
+ EXPECT_NONNULL(span);
+ EXPECT_STR_EQ(desc, span->desc);
+ EXPECT_UINT64_GE(span->begin_ms, span->end_ms);
+ EXPECT_UINT64_GT(0L, span->span_id);
+ EXPECT_NONNULL(span->prid);
+ EXPECT_STR_EQ(prid, span->prid);
+ *out = span;
+ return EXIT_SUCCESS;
+}
+
+int span_table_put(struct span_table *st, struct htrace_span *span)
+{
+ struct htable *ht = (struct htable *)st;
+ int res;
+
+ res = htable_put(ht, span->desc, span);
+ if (res) {
+ htrace_span_free(span);
+ return res;
+ }
+ return 0;
+}
+
+static void span_table_free_entry(void *tracer, void *key, void *val)
+{
+ struct htrace_span *span = val;
+ htrace_span_free(span);
+ // We don't need to free the key, since it's simply the span->desc string,
+ // which is already freed by htrace_span_free.
+}
+
+void span_table_free(struct span_table *st)
+{
+ struct htable *ht = (struct htable *)st;
+
+ // Free all entries
+ htable_visit(ht, span_table_free_entry, NULL);
+ // Free the table itself
+ htable_free(ht);
+}
+
+uint32_t span_table_size(struct span_table *st)
+{
+ struct htable *ht = (struct htable *)st;
+ return htable_used(ht);
+}
+
+int load_trace_span_file(const char *path, struct span_table *st)
+{
+ char line[8196], err[1024];
+ size_t err_len = sizeof(err);
+ FILE *fp = NULL;
+ int lineno = 0, ret = EXIT_FAILURE;
+ struct htrace_span *span;
+
+ fp = fopen(path, "r");
+ if (!fp) {
+ int res = errno;
+ fprintf(stderr, "failed to open %s: %s\n", path, terror(res));
+ return -1;
+ }
+ while (1) {
+ ++lineno;
+ if (!fgets(line, sizeof(line), fp)) {
+ if (ferror(fp)) {
+ int res = errno;
+ fprintf(stderr, "error reading from %s: %s\n",
+ path, terror(res));
+ break;
+ }
+ ret = EXIT_SUCCESS;
+ break;
+ }
+ span_json_parse(line, &span, err, err_len);
+ if (err[0]) {
+ fprintf(stderr, "error parsing line %d. Failed to parse %s "
+ "from %s: %s\n", lineno, line, path, err);
+ break;
+ }
+ span_table_put(st, span);
+ }
+ fclose(fp);
+ if (ret == EXIT_SUCCESS) {
+ //fprintf(stderr, "loaded %d spans from %s\n", lineno - 1, path);
+ return lineno - 1;
+ }
+ return -1;
+}
+
+// vim: ts=4:sw=4:tw=79:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/test/span_table.h
----------------------------------------------------------------------
diff --git a/htrace-c/src/test/span_table.h b/htrace-c/src/test/span_table.h
new file mode 100644
index 0000000..b55cd92
--- /dev/null
+++ b/htrace-c/src/test/span_table.h
@@ -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.
+ */
+
+#ifndef APACHE_HTRACE_TEST_SPAN_TABLE_H
+#define APACHE_HTRACE_TEST_SPAN_TABLE_H
+
+/**
+ * @file span_table.h
+ *
+ * Implements a hash table containing trace spans. They are indexed by span
+ * description.
+ *
+ * This is an internal header, not intended for external use.
+ */
+
+struct htrace_span;
+struct span_table;
+
+/**
+ * Allocate a span table.
+ *
+ * @return NULL on OOM; the span table otherwise.
+ */
+struct span_table *span_table_alloc(void);
+
+/**
+ * Retrieve a span from the table.
+ *
+ * @param st The span table.
+ * @param out (out param) the span. This pointer will be valid until
+ * the span table is freed.
+ * @param desc The span description to look for.
+ * @param prid The process ID to verify that the span has.
+ *
+ * @return 0 on success; nonzero otherwise.
+ */
+int span_table_get(struct span_table *st, struct htrace_span **out,
+ const char *desc, const char *prid);
+
+/**
+ * Add a span to the table.
+ *
+ * @param st The span table.
+ * @param span The span to add. Its memory will be managed by the
+ * span table after span_table_put is called.
+ *
+ * @return 0 on success; nonzero otherwise.
+ */
+int span_table_put(struct span_table *st, struct htrace_span *span);
+
+/**
+ * Free a span table. All spans inside will be freed.
+ *
+ * @param st The span table.
+ */
+void span_table_free(struct span_table *st);
+
+/**
+ * Get the size of the span table.
+ *
+ * @return The number of entries in the span table.
+ */
+uint32_t span_table_size(struct span_table *st);
+
+/**
+ * Load a file with newline-separated trace spans in JSON format into a span
+ * table. Note that this function assumes that every line contains a complete
+ * span, and that each line is less than 8196 bytes.
+ *
+ * @param path The path to read the file from.
+ * @param st The span table we will fill in.
+ *
+ * @return Negative numbers on failure; the number of lines we
+ * read otherwise.
+ */
+int load_trace_span_file(const char *path, struct span_table *st);
+
+#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/span_util-unit.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/test/span_util-unit.c b/htrace-c/src/test/span_util-unit.c
new file mode 100644
index 0000000..fc2a56c
--- /dev/null
+++ b/htrace-c/src/test/span_util-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 "test/span_util.h"
+#include "test/test.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static int test_parse_hex_id_error(const char *in)
+{
+ char err[128];
+
+ err[0] = '\0';
+ parse_hex_id(in, err, sizeof(err));
+ if (!err[0]) {
+ fprintf(stderr, "test_parse_hex_id_error(%s): expected error, but "
+ "was successful.\n", in);
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
+}
+
+static int test_parse_hex_id(uint64_t expected, const char *in)
+{
+ char err[128];
+ size_t err_len = sizeof(err);
+ uint64_t val;
+
+ err[0] = '\0';
+ val = parse_hex_id(in, err, err_len);
+ if (err[0]) {
+ fprintf(stderr, "test_parse_hex_id(%s): got error %s\n",
+ in, err);
+ return EXIT_FAILURE;
+ }
+ EXPECT_UINT64_EQ(expected, val);
+ return EXIT_SUCCESS;
+}
+
+int main(void)
+{
+ EXPECT_INT_ZERO(test_parse_hex_id_error(""));
+ EXPECT_INT_ZERO(test_parse_hex_id_error("z"));
+ EXPECT_INT_ZERO(test_parse_hex_id_error("achoo"));
+ EXPECT_INT_ZERO(test_parse_hex_id(1LLU, "00000000000000001"));
+ EXPECT_INT_ZERO(test_parse_hex_id(0xffffffffffffffffLLU,
+ "ffffffffffffffff"));
+ EXPECT_INT_ZERO(test_parse_hex_id(0x8000000000000000LLU,
+ "8000000000000000"));
+ EXPECT_INT_ZERO(test_parse_hex_id(0x6297421fe159345fLLU,
+ "6297421fe159345f"));
+ EXPECT_INT_ZERO(test_parse_hex_id_error("6297421fe159345fzoo"));
+ 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/span_util.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/test/span_util.c b/htrace-c/src/test/span_util.c
new file mode 100644
index 0000000..fc4b041
--- /dev/null
+++ b/htrace-c/src/test/span_util.c
@@ -0,0 +1,294 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "core/span.h"
+#include "test/span_util.h"
+#include "util/log.h"
+
+#include <errno.h>
+#include <json/json_object.h>
+#include <json/json_tokener.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+uint64_t parse_hex_id(const char *in, char *err, size_t err_len)
+{
+ char *endptr;
+ unsigned long long int ret;
+
+ err[0] = '\0';
+ errno = 0;
+ ret = strtoull(in, &endptr, 16);
+ if (errno) {
+ int e = errno;
+ snprintf(err, err_len, "parse_hex_id(%s) failed: error %s",
+ in, terror(e));
+ return 0;
+ }
+ if (endptr == in) {
+ snprintf(err, err_len, "parse_hex_id(%s) failed: empty string "
+ "found.", in);
+ return 0;
+ }
+ while (1) {
+ char c = *endptr++;
+ if (c == '\0') {
+ break;
+ }
+ if ((c != ' ') || (c != '\t')) {
+ snprintf(err, err_len, "parse_hex_id(%s) failed: garbage at end "
+ "of string.", in);
+ return 0;
+ }
+ }
+ return ret;
+}
+
+static void span_json_parse_parents(struct json_object *root,
+ struct htrace_span *span, char *err, size_t err_len)
+{
+ char err2[128];
+ struct json_object *p = NULL, *e = NULL;
+ int i, np;
+
+ if (!json_object_object_get_ex(root, "p", &p)) {
+ return; // no parents
+ }
+ if (json_object_get_type(p) != json_type_array) {
+ snprintf(err, err_len, "p element was not an array..");
+ return;
+ }
+ np = json_object_array_length(p);
+ if (np == 1) {
+ span->num_parents = 1;
+ e = json_object_array_get_idx(p, 0);
+ span->parent.single = parse_hex_id(json_object_get_string(e),
+ err2, sizeof(err2));
+ if (err2[0]) {
+ snprintf(err, err_len, "failed to parse parent ID 1/1: %s.", err2);
+ return;
+ }
+ } else if (np > 1) {
+ span->parent.list = malloc(sizeof(uint64_t) * np);
+ if (!span->parent.list) {
+ snprintf(err, err_len, "failed to allocate parent ID array of "
+ "%d elements", np);
+ return;
+ }
+ span->num_parents = np;
+ for (i = 0; i < np; i++) {
+ e = json_object_array_get_idx(p, i);
+ span->parent.list[i] = parse_hex_id(json_object_get_string(e),
+ err2, sizeof(err2));
+ if (err2[0]) {
+ snprintf(err, err_len, "failed to parse parent ID %d/%d: %s",
+ i + 1, np, err2);
+ return;
+ }
+ }
+ }
+}
+
+static void span_json_parse_impl(struct json_object *root,
+ struct htrace_span *span, char *err, size_t err_len)
+{
+ char err2[128];
+ struct json_object *d = NULL, *b = NULL, *e = NULL, *s = NULL, *r = NULL;
+ int res;
+
+ err[0] = '\0';
+ if (!json_object_object_get_ex(root, "d", &d)) {
+ d = NULL;
+ }
+ span->desc = strdup(d ? json_object_get_string(d) : "");
+ if (!span->desc) {
+ snprintf(err, err_len, "out of memory allocating description");
+ return;
+ }
+ if (json_object_object_get_ex(root, "b", &b)) {
+ errno = 0;
+ span->begin_ms = json_object_get_int64(b);
+ res = errno;
+ if (res) {
+ snprintf(err, err_len, "error parsing begin_ms: %s", terror(res));
+ return;
+ }
+ }
+ if (json_object_object_get_ex(root, "e", &e)) {
+ errno = 0;
+ span->end_ms = json_object_get_int64(e);
+ res = errno;
+ if (res) {
+ snprintf(err, err_len, "error parsing end_ms: %s", terror(res));
+ return;
+ }
+ }
+ if (json_object_object_get_ex(root, "s", &s)) {
+ span->span_id = parse_hex_id(json_object_get_string(s),
+ err2, sizeof(err2));
+ if (err2[0]) {
+ snprintf(err, err_len, "error parsing span_id: %s", err2);
+ return;
+ }
+ }
+ if (json_object_object_get_ex(root, "r", &r)) {
+ span->prid = strdup(json_object_get_string(r));
+ } else {
+ span->prid = strdup("");
+ }
+ if (!span->prid) {
+ snprintf(err, err_len, "out of memory allocating process id");
+ return;
+ }
+ span_json_parse_parents(root, span, err, err_len);
+ if (err[0]) {
+ return;
+ }
+}
+
+void span_json_parse(const char *in, struct htrace_span **rspan,
+ char *err, size_t err_len)
+{
+ struct json_object *root = NULL;
+ enum json_tokener_error jerr;
+ struct htrace_span *span = NULL;
+
+ err[0] = '\0';
+ root = json_tokener_parse_verbose(in, &jerr);
+ if (!root) {
+ snprintf(err, err_len, "json_tokener_parse_verbose failed: %s",
+ json_tokener_error_desc(jerr));
+ goto done;
+ }
+ span = calloc(1, sizeof(*span));
+ if (!span) {
+ snprintf(err, err_len, "failed to malloc span.");
+ goto done;
+ }
+ span_json_parse_impl(root, span, err, err_len);
+
+done:
+ if (root) {
+ json_object_put(root);
+ }
+ if (err[0]) {
+ htrace_span_free(span);
+ *rspan = NULL;
+ } else {
+ *rspan = span;
+ }
+}
+
+/**
+ * Compare two 64-bit numbers.
+ *
+ * We don't use subtraction here in order to avoid numeric overflow.
+ */
+static int uint64_cmp(uint64_t a, uint64_t b)
+{
+ if (a < b) {
+ return -1;
+ } else if (a > b) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int strcmp_handle_null(const char *a, const char *b)
+{
+ if (a == NULL) {
+ a = "";
+ }
+ if (b == NULL) {
+ b = "";
+ }
+ return strcmp(a, b);
+}
+
+static int compare_parents(struct htrace_span *a, struct htrace_span *b)
+{
+ int na, nb, i;
+
+ htrace_span_sort_and_dedupe_parents(a);
+ na = a->num_parents;
+ htrace_span_sort_and_dedupe_parents(b);
+ nb = b->num_parents;
+
+ for (i = 0; ; i++) {
+ uint64_t sa, sb;
+
+ if (i >= na) {
+ if (i >= nb) {
+ return 0;
+ } else {
+ return -1;
+ }
+ } else if (i >= nb) {
+ return 1;
+ }
+ if ((i == 0) && (na == 1)) {
+ sa = a->parent.single;
+ } else {
+ sa = a->parent.list[i];
+ }
+ if ((i == 0) && (nb == 1)) {
+ sb = b->parent.single;
+ } else {
+ sb = b->parent.list[i];
+ }
+ // Use explicit comparison rather than subtraction to avoid numeric
+ // overflow issues.
+ if (sa < sb) {
+ return -1;
+ } else if (sa > sb) {
+ return 1;
+ }
+ }
+}
+
+int span_compare(struct htrace_span *a, struct htrace_span *b)
+{
+ int c;
+
+ c = uint64_cmp(a->span_id, b->span_id);
+ if (c) {
+ return c;
+ }
+ c = strcmp(a->desc, b->desc);
+ if (c) {
+ return c;
+ }
+ c = uint64_cmp(a->begin_ms, b->begin_ms);
+ if (c) {
+ return c;
+ }
+ c = uint64_cmp(a->end_ms, b->end_ms);
+ if (c) {
+ return c;
+ }
+ c = strcmp_handle_null(a->prid, b->prid);
+ if (c) {
+ return c;
+ }
+ return compare_parents(a, b);
+}
+
+// vim:ts=4:sw=4:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/test/span_util.h
----------------------------------------------------------------------
diff --git a/htrace-c/src/test/span_util.h b/htrace-c/src/test/span_util.h
new file mode 100644
index 0000000..393e213
--- /dev/null
+++ b/htrace-c/src/test/span_util.h
@@ -0,0 +1,66 @@
+/**
+ * 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_SPAN_UTIL_H
+#define APACHE_HTRACE_TEST_SPAN_UTIL_H
+
+#include <stdint.h>
+#include <unistd.h> /* for size_t */
+
+struct htrace_span;
+
+/**
+ * Parses 64-bit hex ID.
+ *
+ * @param in The hex ID string.
+ * @param err (out param) On error, where the error message will be
+ * written. Will be set to the empty string on success.
+ * @param err_len The length of the error buffer. Must be nonzero.
+ */
+uint64_t parse_hex_id(const char *in, char *err, size_t err_len);
+
+/**
+ * Parses a span JSON entry back into a span.
+ *
+ * This function is just used in unit tests and is not optimized.
+ *
+ * @param in The span string.
+ * @param span (out param) On success, the dynamically allocated span
+ * object.
+ * @param err (out param) On error, where the error message will be
+ * written. Will be set to the empty string on success.
+ * @param err_len The length of the error buffer. Must be nonzero.
+ */
+void span_json_parse(const char *in, struct htrace_span **span,
+ char *err, size_t err_len);
+
+/**
+ * Compare two spans.
+ *
+ * @param a The first span.
+ * @param b The second span.
+ *
+ * @return A negative number if the first span is less;
+ * 0 if the spans are equivalent;
+ * A positive number if the first span is greater.
+ */
+int span_compare(struct htrace_span *a, struct htrace_span *b);
+
+#endif
+
+// vim:ts=4:sw=4:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/test/string-unit.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/test/string-unit.c b/htrace-c/src/test/string-unit.c
new file mode 100644
index 0000000..56155ea
--- /dev/null
+++ b/htrace-c/src/test/string-unit.c
@@ -0,0 +1,73 @@
+/**
+ * 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 <errno.h>
+#include <inttypes.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#pragma GCC diagnostic ignored "-Wformat-zero-length"
+
+static int test_fwdprintf(void)
+{
+ char *b, buf[8];
+ int rem = sizeof(buf);
+
+ memset(&buf, 0, sizeof(buf));
+ b = buf;
+ EXPECT_INT_EQ(3, fwdprintf(NULL, NULL, "ab%c", 'c'));
+ EXPECT_INT_EQ(3, fwdprintf(&b, &rem, "ab%c", 'c'));
+ EXPECT_STR_EQ("abc", buf);
+ EXPECT_INT_EQ(5, rem);
+ EXPECT_INT_EQ(0, fwdprintf(NULL, NULL, ""));
+ EXPECT_INT_EQ(0, fwdprintf(&b, &rem, ""));
+ EXPECT_INT_EQ(5, rem);
+ EXPECT_INT_EQ(2, fwdprintf(NULL, NULL, "de"));
+ EXPECT_INT_EQ(2, fwdprintf(&b, &rem, "de"));
+ EXPECT_STR_EQ("abcde", buf);
+ EXPECT_INT_EQ(3, rem);
+ EXPECT_INT_EQ(6, fwdprintf(NULL, NULL, "fghijk"));
+ EXPECT_INT_EQ(6, fwdprintf(&b, &rem, "fghijk"));
+ EXPECT_INT_EQ(0, rem);
+ EXPECT_STR_EQ("abcdefg", buf);
+ return EXIT_SUCCESS;
+}
+
+static int test_validate_json_string(void)
+{
+ EXPECT_INT_EQ(1, validate_json_string(NULL, ""));
+ EXPECT_INT_EQ(1, validate_json_string(NULL, "abc"));
+ EXPECT_INT_EQ(0, validate_json_string(NULL, "\\"));
+ EXPECT_INT_EQ(0, validate_json_string(NULL, "\"FooBar\""));
+ EXPECT_INT_EQ(1, validate_json_string(NULL, "Foo:bar:baz-whatever"));
+ EXPECT_INT_EQ(0, validate_json_string(NULL, "\x01"));
+ return EXIT_SUCCESS;
+}
+
+int main(void)
+{
+ EXPECT_INT_ZERO(test_fwdprintf());
+ EXPECT_INT_ZERO(test_validate_json_string());
+ 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/temp_dir-unit.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/test/temp_dir-unit.c b/htrace-c/src/test/temp_dir-unit.c
new file mode 100644
index 0000000..1c04dae
--- /dev/null
+++ b/htrace-c/src/test/temp_dir-unit.c
@@ -0,0 +1,89 @@
+/**
+ * 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/temp_dir.h"
+#include "test/test.h"
+
+#include <limits.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+static int test_create_tempdir(void)
+{
+ char *tdir = NULL;
+ struct stat st_buf;
+ char err[128];
+ size_t err_len = sizeof(err);
+ int ret;
+
+ tdir = create_tempdir("test_create_tempdir", 0775,
+ err, err_len);
+ EXPECT_STR_EQ("", err);
+ ret = register_tempdir_for_cleanup(tdir);
+ if (ret)
+ return EXIT_FAILURE;
+ if (stat(tdir, &st_buf) == -1) {
+ return EXIT_FAILURE;
+ }
+ if (!S_ISDIR(st_buf.st_mode)) {
+ return EXIT_FAILURE;
+ }
+ free(tdir);
+ return 0;
+}
+
+static int test_create_tempdir_and_delete(void)
+{
+ char *tdir = NULL;
+ struct stat st_buf;
+ int ret;
+ char err[128];
+ size_t err_len = sizeof(err);
+
+ tdir = create_tempdir("test_create_tempdir_and_delete", 0775,
+ err, err_len);
+ EXPECT_STR_EQ("", err);
+ ret = register_tempdir_for_cleanup(tdir);
+ if (ret)
+ return EXIT_FAILURE;
+ if (stat(tdir, &st_buf) == -1) {
+ return EXIT_FAILURE;
+ }
+ if (!S_ISDIR(st_buf.st_mode)) {
+ return EXIT_FAILURE;
+ }
+ recursive_unlink(tdir);
+ unregister_tempdir_for_cleanup(tdir);
+ free(tdir);
+ return 0;
+}
+
+int main(void)
+{
+ EXPECT_INT_ZERO(test_create_tempdir());
+ EXPECT_INT_ZERO(test_create_tempdir());
+ EXPECT_INT_ZERO(test_create_tempdir());
+ EXPECT_INT_ZERO(test_create_tempdir_and_delete());
+ EXPECT_INT_ZERO(test_create_tempdir_and_delete());
+ EXPECT_INT_ZERO(test_create_tempdir_and_delete());
+ 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/temp_dir.c
----------------------------------------------------------------------
diff --git a/htrace-c/src/test/temp_dir.c b/htrace-c/src/test/temp_dir.c
new file mode 100644
index 0000000..b11c8b6
--- /dev/null
+++ b/htrace-c/src/test/temp_dir.c
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2011-2012 the Redfish authors
+ *
+ * Licensed 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/temp_dir.h"
+#include "util/log.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+// Globals
+static int g_tempdir_nonce = 0;
+
+static int g_num_tempdirs = 0;
+
+static char **g_tempdirs = NULL;
+
+static pthread_mutex_t tempdir_lock = PTHREAD_MUTEX_INITIALIZER;
+
+// Functions
+static void cleanup_registered_tempdirs(void)
+{
+ int i;
+ const char *skip_cleanup;
+ skip_cleanup = getenv("SKIP_CLEANUP");
+ if (skip_cleanup)
+ return;
+ pthread_mutex_lock(&tempdir_lock);
+ for (i = 0; i < g_num_tempdirs; ++i) {
+ recursive_unlink(g_tempdirs[i]);
+ free(g_tempdirs[i]);
+ }
+ free(g_tempdirs);
+ g_tempdirs = NULL;
+ pthread_mutex_unlock(&tempdir_lock);
+}
+
+char *create_tempdir(const char *name, int mode, char *err, size_t err_len)
+{
+ char *tdir = NULL, tmp[PATH_MAX];
+ int nonce, pid;
+ const char *base = getenv("TMPDIR");
+
+ err[0] = '\0';
+ if (!base) {
+ base = "/tmp";
+ }
+ if (base[0] != '/') {
+ // canonicalize non-abosolute TMPDIR
+ if (realpath(base, tmp) == NULL) {
+ int e = errno;
+ snprintf(err, err_len, "realpath(%s) failed: %s",
+ base, terror(e));
+ return NULL;
+ }
+ base = tmp;
+ }
+ pthread_mutex_lock(&tempdir_lock);
+ nonce = g_tempdir_nonce++;
+ pthread_mutex_unlock(&tempdir_lock);
+ pid = getpid();
+ if (asprintf(&tdir, "%s/%s.tmp.%08d.%08d",
+ base, name, pid, nonce) < 0) {
+ snprintf(err, err_len, "asprintf failed");
+ return NULL;
+ }
+ if (mkdir(tdir, mode) == -1) {
+ int e = errno;
+ snprintf(err, err_len, "mkdir(%s) failed: %s", tdir, terror(e));
+ free(tdir);
+ return NULL;
+ }
+ return tdir;
+}
+
+int register_tempdir_for_cleanup(const char *tdir)
+{
+ char **tempdirs;
+ pthread_mutex_lock(&tempdir_lock);
+ tempdirs = realloc(g_tempdirs, sizeof(char*) * (g_num_tempdirs + 1));
+ if (!tempdirs)
+ return -ENOMEM;
+ g_tempdirs = tempdirs;
+ g_tempdirs[g_num_tempdirs] = strdup(tdir);
+ if (g_num_tempdirs == 0)
+ atexit(cleanup_registered_tempdirs);
+ g_num_tempdirs++;
+ pthread_mutex_unlock(&tempdir_lock);
+ return 0;
+}
+
+void unregister_tempdir_for_cleanup(const char *tdir)
+{
+ int i;
+ char **tempdirs;
+ pthread_mutex_lock(&tempdir_lock);
+ if (g_num_tempdirs == 0) {
+ pthread_mutex_unlock(&tempdir_lock);
+ return;
+ }
+ for (i = 0; i < g_num_tempdirs; ++i) {
+ if (strcmp(g_tempdirs[i], tdir) == 0)
+ break;
+ }
+ if (i == g_num_tempdirs) {
+ pthread_mutex_unlock(&tempdir_lock);
+ return;
+ }
+ free(g_tempdirs[i]);
+ g_tempdirs[i] = g_tempdirs[g_num_tempdirs - 1];
+ tempdirs = realloc(g_tempdirs, sizeof(char*) * g_num_tempdirs - 1);
+ if (tempdirs) {
+ g_tempdirs = tempdirs;
+ }
+ g_num_tempdirs--;
+ pthread_mutex_unlock(&tempdir_lock);
+}
+
+static int recursive_unlink_helper(int dirfd, const char *name)
+{
+ int fd = -1, ret = 0;
+ DIR *dfd = NULL;
+ struct stat stat;
+ struct dirent *de;
+
+ if (dirfd >= 0) {
+ fd = openat(dirfd, name, O_RDONLY);
+ } else {
+ fd = open(name, O_RDONLY);
+ }
+ if (fd < 0) {
+ ret = errno;
+ fprintf(stderr, "error opening %s: %s\n", name, terror(ret));
+ goto done;
+ }
+ if (fstat(fd, &stat) < 0) {
+ ret = errno;
+ fprintf(stderr, "failed to stat %s: %s\n", name, terror(ret));
+ goto done;
+ }
+ if (!(S_ISDIR(stat.st_mode))) {
+ if (unlinkat(dirfd, name, 0)) {
+ ret = errno;
+ fprintf(stderr, "failed to unlink %s: %s\n", name, terror(ret));
+ goto done;
+ }
+ } else {
+ dfd = fdopendir(fd);
+ if (!dfd) {
+ ret = errno;
+ fprintf(stderr, "fopendir(%s) failed: %s\n", name, terror(ret));
+ goto done;
+ }
+ while ((de = readdir(dfd))) {
+ if (!strcmp(de->d_name, "."))
+ continue;
+ if (!strcmp(de->d_name, ".."))
+ continue;
+ ret = recursive_unlink_helper(fd, de->d_name);
+ if (ret)
+ goto done;
+ }
+ if (unlinkat(dirfd, name, AT_REMOVEDIR) < 0) {
+ ret = errno;
+ goto done;
+ }
+ }
+done:
+ if (fd >= 0) {
+ close(fd);
+ }
+ if (dfd) {
+ closedir(dfd);
+ }
+ return -ret;
+}
+
+int recursive_unlink(const char *path)
+{
+ return recursive_unlink_helper(-1, path);
+}
+
+// vim:ts=4:sw=4:et
http://git-wip-us.apache.org/repos/asf/incubator-htrace/blob/a9d85254/htrace-c/src/test/temp_dir.h
----------------------------------------------------------------------
diff --git a/htrace-c/src/test/temp_dir.h b/htrace-c/src/test/temp_dir.h
new file mode 100644
index 0000000..d12e961
--- /dev/null
+++ b/htrace-c/src/test/temp_dir.h
@@ -0,0 +1,67 @@
+/**
+ * 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_TEMP_DIR_H
+#define APACHE_HTRACE_TEST_TEMP_DIR_H
+
+#include <unistd.h> /* for size_t */
+
+/**
+ * Create a temporary directory
+ *
+ * @param tempdir The identifier to use for this temporary directory. We will
+ * add a random string to this identifier to create the
+ * full path.
+ * @param mode The mode to use in mkdir. Typically 0755.
+ * @param err The error buffer to be set on failure.
+ * @param err_len Length of the error buffer.
+ *
+ * @return A malloc'ed string with the path to the temporary directory,
+ * or NULL on failure.
+ */
+char *create_tempdir(const char *name, int mode, char *err, size_t err_len);
+
+/**
+ * Register a temporary directory to be deleted at the end of the program
+ *
+ * @param tempdir The path of the temporary directory to register. The string
+ * will be deep-copied.
+ *
+ * @return 0 on success; error code otherwise
+ */
+int register_tempdir_for_cleanup(const char *tempdir);
+
+/**
+ * Unregister a temporary directory to be deleted at the end of the program
+ *
+ * @param tempdir The path of the temporary directory to unregister.
+ */
+void unregister_tempdir_for_cleanup(const char *tempdir);
+
+/**
+ * Recursively unlink a directory.
+ *
+ * @param tempdir The directory to remove.
+ *
+ * @return Zero on success; the error code otherwise.
+ */
+int recursive_unlink(const char *path);
+
+#endif
+
+// vim:ts=4:sw=4:et