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:22 UTC

[4/4] incubator-htrace git commit: HTRACE-106: htrace: add C / C++ native client (cmccabe)

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