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>
