diff options
57 files changed, 7374 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2a4a88e --- /dev/null +++ b/.gitignore @@ -0,0 +1,28 @@ +Makefile +Makefile.in +aclocal.m4 +config.log +config.status +config/ +configure +core +libtool +m4/libtool.m4 +m4/ltoptions.m4 +m4/ltsugar.m4 +m4/ltversion.m4 +m4/lt~obsolete.m4 +package +stamp-h1 +test/ot-c-wrapper-test* +version.h +.deps/ +.libs/ +*.la +*.lo +*.o +*.orig +*.pc +*.rej +*.so +_* @@ -0,0 +1 @@ +Miroslav Zagorac <mzagorac@haproxy.com> @@ -0,0 +1 @@ +Copyright 2020 HAProxy Technologies diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..82368aa --- /dev/null +++ b/ChangeLog @@ -0,0 +1,3 @@ +Wed Jun 9 15:47:38 CEST 2021 + - added functions otc_tracer_load() and otc_tracer_start() + @@ -0,0 +1,368 @@ +Installation Instructions +************************* + + Copyright (C) 1994-1996, 1999-2002, 2004-2016 Free Software +Foundation, Inc. + + Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided the copyright +notice and this notice are preserved. This file is offered as-is, +without warranty of any kind. + +Basic Installation +================== + + Briefly, the shell command './configure && make && make install' +should configure, build, and install this package. The following +more-detailed instructions are generic; see the 'README' file for +instructions specific to this package. Some packages provide this +'INSTALL' file but do not implement all of the features documented +below. The lack of an optional feature in a given package is not +necessarily a bug. More recommendations for GNU packages can be found +in *note Makefile Conventions: (standards)Makefile Conventions. + + The 'configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a 'Makefile' in each directory of the package. +It may also create one or more '.h' files containing system-dependent +definitions. Finally, it creates a shell script 'config.status' that +you can run in the future to recreate the current configuration, and a +file 'config.log' containing compiler output (useful mainly for +debugging 'configure'). + + It can also use an optional file (typically called 'config.cache' and +enabled with '--cache-file=config.cache' or simply '-C') that saves the +results of its tests to speed up reconfiguring. Caching is disabled by +default to prevent problems with accidental use of stale cache files. + + If you need to do unusual things to compile the package, please try +to figure out how 'configure' could check whether to do them, and mail +diffs or instructions to the address given in the 'README' so they can +be considered for the next release. If you are using the cache, and at +some point 'config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file 'configure.ac' (or 'configure.in') is used to create +'configure' by a program called 'autoconf'. You need 'configure.ac' if +you want to change it or regenerate 'configure' using a newer version of +'autoconf'. + + The simplest way to compile this package is: + + 1. 'cd' to the directory containing the package's source code and type + './configure' to configure the package for your system. + + Running 'configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type 'make' to compile the package. + + 3. Optionally, type 'make check' to run any self-tests that come with + the package, generally using the just-built uninstalled binaries. + + 4. Type 'make install' to install the programs and any data files and + documentation. When installing into a prefix owned by root, it is + recommended that the package be configured and built as a regular + user, and only the 'make install' phase executed with root + privileges. + + 5. Optionally, type 'make installcheck' to repeat any self-tests, but + this time using the binaries in their final installed location. + This target does not install anything. Running this target as a + regular user, particularly if the prior 'make install' required + root privileges, verifies that the installation completed + correctly. + + 6. You can remove the program binaries and object files from the + source code directory by typing 'make clean'. To also remove the + files that 'configure' created (so you can compile the package for + a different kind of computer), type 'make distclean'. There is + also a 'make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + 7. Often, you can also type 'make uninstall' to remove the installed + files again. In practice, not all packages have tested that + uninstallation works correctly, even though it is required by the + GNU Coding Standards. + + 8. Some packages, particularly those that use Automake, provide 'make + distcheck', which can by used by developers to test that all other + targets like 'make install' and 'make uninstall' work correctly. + This target is generally not run by end users. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the 'configure' script does not know about. Run './configure --help' +for details on some of the pertinent environment variables. + + You can give 'configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here is +an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU 'make'. 'cd' to the +directory where you want the object files and executables to go and run +the 'configure' script. 'configure' automatically checks for the source +code in the directory that 'configure' is in and in '..'. This is known +as a "VPATH" build. + + With a non-GNU 'make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use 'make distclean' before +reconfiguring for another architecture. + + On MacOS X 10.5 and later systems, you can create libraries and +executables that work on multiple system types--known as "fat" or +"universal" binaries--by specifying multiple '-arch' options to the +compiler but only a single '-arch' option to the preprocessor. Like +this: + + ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CPP="gcc -E" CXXCPP="g++ -E" + + This is not guaranteed to produce working output in all cases, you +may have to build one architecture at a time and combine the results +using the 'lipo' tool if you have problems. + +Installation Names +================== + + By default, 'make install' installs the package's commands under +'/usr/local/bin', include files under '/usr/local/include', etc. You +can specify an installation prefix other than '/usr/local' by giving +'configure' the option '--prefix=PREFIX', where PREFIX must be an +absolute file name. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option '--exec-prefix=PREFIX' to 'configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like '--bindir=DIR' to specify different values for particular +kinds of files. Run 'configure --help' for a list of the directories +you can set and what kinds of files go in them. In general, the default +for these options is expressed in terms of '${prefix}', so that +specifying just '--prefix' will affect all of the other directory +specifications that were not explicitly provided. + + The most portable way to affect installation locations is to pass the +correct locations to 'configure'; however, many packages provide one or +both of the following shortcuts of passing variable assignments to the +'make install' command line to change installation locations without +having to reconfigure or recompile. + + The first method involves providing an override variable for each +affected directory. For example, 'make install +prefix=/alternate/directory' will choose an alternate location for all +directory configuration variables that were expressed in terms of +'${prefix}'. Any directories that were specified during 'configure', +but not in terms of '${prefix}', must each be overridden at install time +for the entire installation to be relocated. The approach of makefile +variable overrides for each directory variable is required by the GNU +Coding Standards, and ideally causes no recompilation. However, some +platforms have known limitations with the semantics of shared libraries +that end up requiring recompilation when using this method, particularly +noticeable in packages that use GNU Libtool. + + The second method involves providing the 'DESTDIR' variable. For +example, 'make install DESTDIR=/alternate/directory' will prepend +'/alternate/directory' before all installation names. The approach of +'DESTDIR' overrides is not required by the GNU Coding Standards, and +does not work on platforms that have drive letters. On the other hand, +it does better at avoiding recompilation issues, and works well even +when some directory options were not specified in terms of '${prefix}' +at 'configure' time. + +Optional Features +================= + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving 'configure' the +option '--program-prefix=PREFIX' or '--program-suffix=SUFFIX'. + + Some packages pay attention to '--enable-FEATURE' options to +'configure', where FEATURE indicates an optional part of the package. +They may also pay attention to '--with-PACKAGE' options, where PACKAGE +is something like 'gnu-as' or 'x' (for the X Window System). The +'README' should mention any '--enable-' and '--with-' options that the +package recognizes. + + For packages that use the X Window System, 'configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the 'configure' options '--x-includes=DIR' and +'--x-libraries=DIR' to specify their locations. + + Some packages offer the ability to configure how verbose the +execution of 'make' will be. For these packages, running './configure +--enable-silent-rules' sets the default to minimal output, which can be +overridden with 'make V=1'; while running './configure +--disable-silent-rules' sets the default to verbose, which can be +overridden with 'make V=0'. + +Particular systems +================== + + On HP-UX, the default C compiler is not ANSI C compatible. If GNU CC +is not installed, it is recommended to use the following options in +order to use an ANSI C compiler: + + ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" + +and if that doesn't work, install pre-built binaries of GCC for HP-UX. + + HP-UX 'make' updates targets which have the same time stamps as their +prerequisites, which makes it generally unusable when shipped generated +files such as 'configure' are involved. Use GNU 'make' instead. + + On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot +parse its '<wchar.h>' header file. The option '-nodtk' can be used as a +workaround. If GNU CC is not installed, it is therefore recommended to +try + + ./configure CC="cc" + +and if that doesn't work, try + + ./configure CC="cc -nodtk" + + On Solaris, don't put '/usr/ucb' early in your 'PATH'. This +directory contains several dysfunctional programs; working variants of +these programs are available in '/usr/bin'. So, if you need '/usr/ucb' +in your 'PATH', put it _after_ '/usr/bin'. + + On Haiku, software installed for all users goes in '/boot/common', +not '/usr/local'. It is recommended to use the following options: + + ./configure --prefix=/boot/common + +Specifying the System Type +========================== + + There may be some features 'configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, 'configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +'--build=TYPE' option. TYPE can either be a short name for the system +type, such as 'sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS + KERNEL-OS + + See the file 'config.sub' for the possible values of each field. If +'config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option '--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with '--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for 'configure' scripts to share, +you can create a site shell script called 'config.site' that gives +default values for variables like 'CC', 'cache_file', and 'prefix'. +'configure' looks for 'PREFIX/share/config.site' if it exists, then +'PREFIX/etc/config.site' if it exists. Or, you can set the +'CONFIG_SITE' environment variable to the location of the site script. +A warning: not all 'configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to 'configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the 'configure' command line, using 'VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified 'gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for 'CONFIG_SHELL' due to an +Autoconf limitation. Until the limitation is lifted, you can use this +workaround: + + CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash + +'configure' Invocation +====================== + + 'configure' recognizes the following options to control how it +operates. + +'--help' +'-h' + Print a summary of all of the options to 'configure', and exit. + +'--help=short' +'--help=recursive' + Print a summary of the options unique to this package's + 'configure', and exit. The 'short' variant lists options used only + in the top level, while the 'recursive' variant lists options also + present in any nested packages. + +'--version' +'-V' + Print the version of Autoconf used to generate the 'configure' + script, and exit. + +'--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally 'config.cache'. FILE defaults to '/dev/null' to + disable caching. + +'--config-cache' +'-C' + Alias for '--cache-file=config.cache'. + +'--quiet' +'--silent' +'-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to '/dev/null' (any error + messages will still be shown). + +'--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + 'configure' can determine that directory automatically. + +'--prefix=DIR' + Use DIR as the installation prefix. *note Installation Names:: for + more details, including other options available for fine-tuning the + installation locations. + +'--no-create' +'-n' + Run the configure checks, but stop before creating any output + files. + +'configure' also accepts some other, not widely useful, options. Run +'configure --help' for more details. @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2020 HAProxy Technologies + + 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. diff --git a/MAINTAINERS b/MAINTAINERS new file mode 100644 index 0000000..92b2831 --- /dev/null +++ b/MAINTAINERS @@ -0,0 +1 @@ +Miroslav Zagorac <mzagorac@haproxy.com> diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..0bbe80e --- /dev/null +++ b/Makefile.am @@ -0,0 +1,9 @@ +## Process this file with automake to produce Makefile.in +## +SUBDIRS = src test +DIST_SUBDIRS = src test +DISTCLEANFILES = _* *~ +CLEANFILES = a.out core gmon.out +ACLOCAL_AMFLAGS = -I m4 +## +## Makefile.am ends here @@ -0,0 +1,276 @@ + ------------------------------------------------------------- + C wrapper for the C++ implementation of the OpenTracing API + ------------------------------------------------------------- + + +Summary +------------------------------------------------------------------------ + + 1. Introduction + 2. Build instructions + 3. Testing the operation of the library + 4. Basic concepts in the OpenTracing + 5. Tracing example + 6. Known bugs and limitations + + +1. Introduction +------------------------------------------------------------------------ + +OpenTracing C Wrapper library was created due to the need to use distributed +tracing within C programs. + +The OpenTracing project (https://opentracing.io/) has published a +C-language implementation on https://github.com/opentracing/opentracing-c +but that implementation is incomplete, with the last update on Jun +27th 2018 + +This library takes inspiration from the header files in that project but +implements the proposed functionality fully by wrapping the functional +C++ library. + + +2. Build instructions +------------------------------------------------------------------------ + +Prerequisites for configuring and compiling the OpenTracing C Wrapper: +---------------------------------------------------------------------- + + * GNU GCC Compiler and Development Environment + The main components of this system are C standard library, C and C++ + compilers, make and cmake utilities and other development tools that are + not explicitly listed because they depend on the system on which we want + to compile the OpenTracing C Wrapper. + + The program itself is very likely possible to compile with another C + compiler (non-GCC), but I'm not sure about the other required libraries. + + On debian-like linux systems it is necessary to run the following, in + order to install development packages: + + # apt-get install automake autoconf make cmake libc6-dev gcc g++ binutils libtool pkgconf gawk git + + * GNU autoconf + https://www.gnu.org/software/autoconf/ + + * GNU automake + https://www.gnu.org/software/automake/ + + * POSIX threads library + + * C++ implementation of the OpenTracing API http://opentracing.io + https://github.com/opentracing/opentracing-cpp + + * the tracing plugins (one of the following may be used): + * Jaeger SDK with OpenTracing API for C++ binding + https://github.com/jaegertracing/jaeger-client-cpp + + * the LightStep distributed tracing library for C++ + https://github.com/lightstep/lightstep-tracer-cpp + + * OpenTracing implementation for Zipkin in C++ + https://github.com/rnburn/zipkin-cpp-opentracing + + * Datadog OpenTracing C++ Client + https://github.com/DataDog/dd-opentracing-cpp + + +Note: prompt '%' indicates that the command is executed under a unprivileged + user, while prompt '#' indicates that the command is executed under the + root user. + + +Compiling and installing the opentracing-cpp library: +----------------------------------------------------- + + The assumption is that we want to install the library in the /opt directory. + + % wget https://github.com/opentracing/opentracing-cpp/archive/v1.5.0.tar.gz + % tar xf v1.5.0.tar.gz + % cd opentracing-cpp-1.5.0 + % mkdir build + % cd build + % cmake -DCMAKE_INSTALL_PREFIX=/opt .. + % make + # make install + + Of course, we can take another version of the library (or download the master + branch via git). For example, this version is used here due to compatibility + with the Jaeger plugin, which I mostly used when testing the operation of the + program. + + +Compiling and installing the opentracing-c-wrapper library: +----------------------------------------------------------- + + We will also install this library in the /opt directory, and when configuring + the library we must specify where the opentracing-cpp library is located. + + In this example we will install two builds of the library, first the + release version and then the debug version. + + % git clone https://github.com/haproxytech/opentracing-c-wrapper.git + % cd opentracing-c-wrapper + % ./scripts/bootstrap + % ./configure --prefix=/opt --with-opentracing=/opt + % make + # make install + + % ./scripts/distclean + % ./scripts/bootstrap + % ./configure --prefix=/opt --enable-debug --with-opentracing=/opt + % make + # make install + + +Compiling the Jaeger tracing plugin: +------------------------------------ + + We will use the 1.5.0 version of the plugin, a newer one can be taken (or + the git master branch) if you want to try it (as is the case with previous + libraries, you can try a newer version). + + Important note: the GCC version must be at least 4.9 or later. + + % wget https://github.com/jaegertracing/jaeger-client-cpp/archive/v0.5.0.tar.gz + % tar xf v0.5.0.tar.gz + % cd jaeger-client-cpp-0.5.0 + % mkdir build + % cd build + % cmake -DCMAKE_INSTALL_PREFIX=/opt -DJAEGERTRACING_PLUGIN=ON -DHUNTER_CONFIGURATION_TYPES=Release -DHUNTER_BUILD_SHARED_LIBS=OFF .. + % make + + After the plugin is compiled, it will be in the current directory. + The name of the plugin is libjaegertracing_plugin.so. + + Of course, we can download the precompiled version of the Jaeger plugin + (unfortunately, the latest version that can be downloaded is 0.4.2). + + % wget https://github.com/jaegertracing/jaeger-client-cpp/releases/download/v0.4.2/libjaegertracing_plugin.linux_amd64.so + + +3. Testing the operation of the library +------------------------------------------------------------------------ + +Testing of the library can be done through the test program, which is located +in the eponymous directory 'test'. In this directory there is also a script +with which we can download opentracing plugins from all supported tracers. +As a script argument we can specify the directory to which the plugins are +downloaded. If we do not specify this argument then the download is done to +the current directory. In our example we will place the downloaded plugins +in the 'test' directory: + + % ./test/get-opentracing-plugins.sh test + +The options supported by the test program can be found using the '-h' option: + + % ./test/ot-c-wrapper-test_dbg -h + +--- help output ------- +Usage: ot-c-wrapper-test_dbg { -h --help } + ot-c-wrapper-test_dbg { -V --version } + ot-c-wrapper-test_dbg { [ -R --runcount=VALUE ] | [ -r --runtime=TIME ] } [OPTION]... + +Options are: + -c, --config=FILE Specify the configuration for the used tracer. + -d, --debug=LEVEL Enable and specify the debug mode level (default: 0). + -h, --help Show this text. + -p, --plugin=FILE Specify the OpenTracing compatible plugin library. + -R, --runcount=VALUE Execute this program a certain number of passes (0 = unlimited). + -r, --runtime=TIME Run this program for a certain amount of time (ms, 0 = unlimited). + -t, --threads=VALUE Specify the number of threads (default: 1000). + -V, --version Show program version. + +Copyright 2020 HAProxy Technologies +SPDX-License-Identifier: Apache-2.0 +--- help output ------- + +In case we did not use the '--enable-debug' option when configuring the +library, the test program will be named ot-c-wrapper-test. + + +Example of using the test program: + + % ./test/ot-c-wrapper-test -r 10000 -c test/cfg-jaeger.yml -p test/libjaeger_opentracing_plugin-0.4.2.so + +'-r' is the option that must be specified when launching a program. It is +used to prevent the test program from starting unnecessarily when testing the +program options, or printing the test program help. In addition, with this +option, the test program runtime is set. If the time is set to 0, the test +program runtime is unlimited. In the example above, the runtime is set to +10 seconds. + +With the '-c' option, we specify the configuration of the used tracer (in this +case it is Jeager); while the '-p' option selects the plugin library that the +selected tracer uses. + + +The test directory contains several configurations prepared for supported +tracers: + - cfg-dd.json - Datadog tracer + - cfg-jaeger.yml - Jaeger tracer + - cfg-zipkin.json - Zipkin tracer + + +Jaeger docker image installation: +--------------------------------- + + Installation instructions can be found on the website + https://www.jaegertracing.io/download/. For the impatient, here we will list + how the image to test the operation of the tracer system can be installed + without much reading of the documentation. + + # docker pull jaegertracing/all-in-one:latest + # docker run -d --name jaeger -e COLLECTOR_ZIPKIN_HTTP_PORT=9411 -p 5775:5775/udp -p 6831:6831/udp -p 6832:6832/udp -p 5778:5778 -p 16686:16686 -p 14268:14268 -p 9411:9411 jaegertracing/all-in-one:latest + + The last command will also initialize and run the Jaeger container. + If we want to use that container later, it can be started and stopped + in the classic way, using the 'docker container start/stop' commands. + + +4. Basic concepts in the OpenTracing +------------------------------------------------------------------------ + +Basic concepts of the OpenTracing can be read on the OpenTracing documentation +website https://opentracing.io/docs/overview/. + +Here we will list only the most important elements of distributed tracing and +these are 'trace', 'span' and 'span context'. Trace is a description of the +complete transaction we want to record in the tracing system. A span is an +operation that represents a unit of work that is recorded in a tracing system. +Span context is a group of information related to a particular span that is +passed on to the system (from service to service). Using this context, we can +add new spans to already open trace (or supplement data in already open spans). + +An individual span may contain one or more tags, logs and baggage items. +The tag is a key-value element that is valid for the entire span. Log is a +key-value element that allows you to write some data at a certain time, it +can be used for debugging. A baggage item is a key-value data pair that can +be used for the duration of an entire trace, from the moment it is added to +the span. + + +5. Tracing example +------------------------------------------------------------------------ + +In the example, whose source is in the 'test' directory, the operation of the +OpenTracing C Wrapper is checked. 5 spans are created in which tags, logs and +baggage are placed; and data propagation via text map, http header and binary +data is checked. + +root span |------------------------------------------------------------------| + span #1 |----------------------------------------------------------| + text map propagation |--------------------------------------------------| + http headers propagation |------------------------------------------| + binary data propagation |----------------------------------| + + +6. Known bugs and limitations +------------------------------------------------------------------------ + +The library does not know whether the data was sent to the tracer or not, +because sending is done in a separate thread that does not affect the +operation of the rest of the program. + +One should not exaggerate with the number of competing threads that send data +to their tracers as this can slow down the sending of data to the tracer. @@ -0,0 +1,2 @@ +Package Version: 1.1.3 +Library Version: 1:1:1 diff --git a/configure.ac b/configure.ac new file mode 100644 index 0000000..784a3f5 --- /dev/null +++ b/configure.ac @@ -0,0 +1,118 @@ +dnl Process this file with autoconf to produce a configure script. +dnl +dnl configure.ac - Miroslav Zagorac <mzagorac@haproxy.com> + +AC_PREREQ([2.69]) +AC_COPYRIGHT([Copyright (C) 2020 HAProxy Technologies]) +AC_INIT(m4_esyscmd_s([basename ${PWD}]), m4_esyscmd_s([awk '/Package/ { print $3 }' VERSION]), [mzagorac@haproxy.com]) +AC_CONFIG_MACRO_DIR([m4]) +AX_VARIABLE_SET([LIB_VERSION], m4_esyscmd_s([awk '/Library/ { print $3 }' VERSION])) + +echo "*** configuring for ${PACKAGE_NAME} v${PACKAGE_VERSION}, lib v${LIB_VERSION} ***" + +AC_PREFIX_DEFAULT([/usr]) +AC_CONFIG_AUX_DIR([config]) +AM_INIT_AUTOMAKE([1.13]) +AX_VARIABLES_INIT +AM_SILENT_RULES([yes]) +AX_HOST + +dnl Check for source directory. +dnl +AC_CONFIG_SRCDIR([src/tracer.cpp]) +AC_CONFIG_HEADERS([config/config.h]) + +AX_PROG_PKGCONFIG +PKG_INSTALLDIR + +dnl Set the compiler, preprocessor, and file extensions. +dnl +AC_LANG([C]) + +dnl Checking command line options. +dnl +AX_ENABLE_DEBUG +AX_ENABLE_GPROF +AX_ENABLE_THREADS +dnl +dnl Misc +dnl +AX_WITH_OPENTRACING + +dnl Checks for programs. +dnl +AC_PROG_CXX +AC_PROG_CC +AC_PROG_INSTALL + +dnl Initialize libtool +dnl +LT_INIT + +dnl Checks for header files. +dnl +AC_CHECK_HEADERS([fcntl.h inttypes.h malloc.h stdint.h stdlib.h string.h sys/time.h unistd.h]) + +dnl Checks for typedefs, structures, and compiler characteristics. +dnl +AC_CHECK_HEADER_STDBOOL +AC_TYPE_UINT8_T +AC_TYPE_INT64_T +AC_TYPE_UINT64_T +AC_TYPE_OFF_T +AC_TYPE_SIZE_T +AC_TYPE_SSIZE_T + +dnl Checks for libraries. +dnl + +dnl Checks for library functions. +dnl +AC_FUNC_ERROR_AT_LINE +AC_FUNC_STRERROR_R +AC_CHECK_FUNCS([clock_gettime gettimeofday memmove memset strchr strdup strndup]) +AC_CHECK_FUNCS([mallinfo]) + +dnl Set the compiler flags +dnl +AX_PROG_CC_SET([]) +AX_PROG_CXX_SET([]) + +AM_CONDITIONAL([WANT_DEBUG], [test "${enable_debug}" = "yes"]) + +AX_VARIABLES_SET + +AX_VARIABLE_SET([OPENTRACING_C_WRAPPER_CPPFLAGS], [ ${OPENTRACING_CPPFLAGS} ${THREADS_CPPFLAGS} ]) +AX_VARIABLE_SET([OPENTRACING_C_WRAPPER_CFLAGS], [ ${OPENTRACING_CFLAGS} ${THREADS_CFLAGS} ]) +AX_VARIABLE_SET([OPENTRACING_C_WRAPPER_CXXFLAGS], [ ${OPENTRACING_CXXFLAGS} ${THREADS_CXXFLAGS} ]) +AX_VARIABLE_SET([OPENTRACING_C_WRAPPER_LDFLAGS], [ ${OPENTRACING_LDFLAGS} ${THREADS_LDFLAGS} ]) +AX_VARIABLE_SET([OPENTRACING_C_WRAPPER_LIBS], [ ${OPENTRACING_LIBS} ${THREADS_LIBS} ]) + +AC_SUBST([OPENTRACING_C_WRAPPER_CPPFLAGS]) +AC_SUBST([OPENTRACING_C_WRAPPER_CFLAGS]) +AC_SUBST([OPENTRACING_C_WRAPPER_CXXFLAGS]) +AC_SUBST([OPENTRACING_C_WRAPPER_LDFLAGS]) +AC_SUBST([OPENTRACING_C_WRAPPER_LIBS]) + +dnl version.h +dnl +CONFIGURE_OPTIONS="${ac_configure_args}" +AC_SUBST([DATE]) +AC_SUBST([PACKAGE_VERSION]) +AC_SUBST([LIB_VERSION]) +AC_SUBST([CONFIGURE_OPTIONS]) + +AC_CONFIG_FILES([Makefile src/Makefile test/Makefile include/version.h opentracing-c-wrapper.pc opentracing-c-wrapper_dbg.pc]) +AC_OUTPUT + +AX_SHOW_CONFIG + + +AH_TOP([ +#ifndef _COMMON_CONFIG_H +#define _COMMON_CONFIG_H +]) + +AH_BOTTOM([ +#endif /* _COMMON_CONFIG_H */ +]) diff --git a/include/dbg_malloc.h b/include/dbg_malloc.h new file mode 100644 index 0000000..b0557be --- /dev/null +++ b/include/dbg_malloc.h @@ -0,0 +1,61 @@ +/*** + * Copyright 2020 HAProxy Technologies + * + * 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. + */ +#ifndef _OPENTRACING_C_WRAPPER_DBG_MALLOC_H_ +#define _OPENTRACING_C_WRAPPER_DBG_MALLOC_H_ + +#ifdef HAVE_MALLOC_H +# include <malloc.h> +#endif + + +#define DBG_MEM(l,s,f, ...) \ + do { \ + if (dbg_mem == nullptr) \ + /* Do nothing. */; \ + else if (!(l) || (dbg_mem->level & (1 << (l)))) \ + (void)fprintf((s), f "\n", ##__VA_ARGS__); \ + } while (0) +#define DBG_MEM_ERR(f, ...) DBG_MEM(0, stderr, "MEM_ERROR: " f, ##__VA_ARGS__) +#define DBG_MEM_INFO(l,f, ...) DBG_MEM((l), stdout, f, ##__VA_ARGS__) + +/* 8 bytes - dBgM (dBgM ^ 0xffffffff) */ +#define DBG_MEM_MAGIC UINT64_C(0x6442674d9bbd98b2) +#define DBG_MEM_SIZE(n) ((n) + sizeof(struct otc_dbg_mem_metadata)) +#define DBG_MEM_PTR(p) DBG_MEM_SIZE(OT_CAST_TYPEOF(uint8_t *, (p))) +#define DBG_MEM_DATA(p) OT_CAST_TYPEOF(struct otc_dbg_mem_metadata *, OT_CAST_TYPEOF(uint8_t *, (p)) - DBG_MEM_SIZE(0)) +#define DBG_MEM_RETURN(p) (((p) == nullptr) ? nullptr : DBG_MEM_PTR(p)) + +struct otc_dbg_mem_metadata { + struct otc_dbg_mem_data *data; + uint64_t magic; +}; + +#ifdef __sun +#define PRI_MI "lu" +#else +#define PRI_MI "d" +#endif + +#endif /* _OPENTRACING_C_WRAPPER_DBG_MALLOC_H_ */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/include/define.h b/include/define.h new file mode 100644 index 0000000..1e63fef --- /dev/null +++ b/include/define.h @@ -0,0 +1,69 @@ +/*** + * Copyright 2020 HAProxy Technologies + * + * 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. + */ +#ifndef _OPENTRACING_C_WRAPPER_DEFINE_H_ +#define _OPENTRACING_C_WRAPPER_DEFINE_H_ + +#undef OT_THREADS_NO_LOCKING + +#ifdef USE_THREADS +# define __THR __thread +#else +# define __THR +#endif + +#ifdef DEBUG +# define OTC_DBG_MEM +# define OT_IFDEF_DBG(a,b) a +# define OT_EXT_MALLOC(s) otc_ext_malloc(__func__, __LINE__, (s)) +# define OT_EXT_FREE_CLEAR(a) do { if ((a) != nullptr) { otc_ext_free(__func__, __LINE__, a); (a) = nullptr; } } while (0) +#else +# define OT_IFDEF_DBG(a,b) b +# define OT_EXT_MALLOC(s) otc_ext_malloc(s) +# define OT_EXT_FREE_CLEAR(a) do { if ((a) != nullptr) { otc_ext_free(a); (a) = nullptr; } } while (0) +#endif + +#define OT_FREE(a) do { if ((a) != nullptr) OTC_DBG_FREE(a); } while (0) +#define OT_FREE_CLEAR(a) do { if ((a) != nullptr) { OTC_DBG_FREE(a); (a) = nullptr; } } while (0) + +#define OT_IN_RANGE(v,a,b) (((v) >= (a)) && ((v) <= (b))) +#define OT_SPAN_KEY_IS_VALID(a) OT_IN_RANGE((a)->idx, 0, ot_span.key - 1) +#define OT_SPAN_IS_VALID(a) (((a) != nullptr) && OT_SPAN_KEY_IS_VALID(a)) +#define OT_CTX_KEY_IS_VALID(a) OT_IN_RANGE((a)->idx, 0, ot_span_context.key - 1) +#define OT_CTX_IS_VALID(a) (((a) != nullptr) && (OT_SPAN_IS_VALID((a)->span) || OT_CTX_KEY_IS_VALID(a))) + +#define OT_CAST_CONST(t,e) const_cast<t>(e) +#define OT_CAST_STAT(t,e) static_cast<t>(e) +#define OT_CAST_REINTERPRET(t,e) reinterpret_cast<t>(e) +#define OT_CAST_TYPEOF(t,e) OT_CAST_REINTERPRET(typeof(t), (e)) + +#ifdef __cplusplus +# define __CPLUSPLUS_DECL_BEGIN extern "C" { +# define __CPLUSPLUS_DECL_END } +#else +# define __CPLUSPLUS_DECL_BEGIN +# define __CPLUSPLUS_DECL_END +#endif + +#endif /* _OPENTRACING_C_WRAPPER_DEFINE_H_ */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/include/include.h b/include/include.h new file mode 100644 index 0000000..85c1b88 --- /dev/null +++ b/include/include.h @@ -0,0 +1,56 @@ +/*** + * Copyright 2020 HAProxy Technologies + * + * 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. + */ +#ifndef _OPENTRACING_C_WRAPPER_INCLUDE_H_ +#define _OPENTRACING_C_WRAPPER_INCLUDE_H_ + +#include <cstdio> +#include <cinttypes> +#include <stdbool.h> +#include <sstream> +#include <mutex> + +#include <opentracing/dynamic_load.h> +#include <opentracing/version.h> + +#include "config.h" +#include "define.h" +#ifdef DEBUG +# include "dbg_malloc.h" +#endif + +#include "opentracing-c-wrapper/define.h" +#include "opentracing-c-wrapper/dbg_malloc.h" +#include "opentracing-c-wrapper/common.h" +#include "opentracing-c-wrapper/util.h" +#include "opentracing-c-wrapper/value.h" +#include "opentracing-c-wrapper/span.h" +#include "opentracing-c-wrapper/propagation.h" +#include "opentracing-c-wrapper/tracer.h" + +#include "span.h" +#include "tracer.h" +#include "util.h" + +#endif /* _OPENTRACING_C_WRAPPER_INCLUDE_H_ */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/include/opentracing-c-wrapper/common.h b/include/opentracing-c-wrapper/common.h new file mode 100644 index 0000000..2bac541 --- /dev/null +++ b/include/opentracing-c-wrapper/common.h @@ -0,0 +1,53 @@ +/*** + * Copyright 2020 HAProxy Technologies + * + * 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. + */ +#ifndef OPENTRACING_C_WRAPPER_COMMON_H +#define OPENTRACING_C_WRAPPER_COMMON_H + +__CPLUSPLUS_DECL_BEGIN + +/*** + * boolean type + */ +typedef enum { + otc_false = 0, + otc_true = 1, +} otc_bool_t; + +/*** + * duration type for calculating intervals (monotonic) + */ +struct otc_duration { + struct timespec value; +}; + +/*** + * timestamp type for absolute time + */ +struct otc_timestamp { + struct timespec value; +}; + +__CPLUSPLUS_DECL_END +#endif /* OPENTRACING_C_WRAPPER_COMMON_H */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/include/opentracing-c-wrapper/dbg_malloc.h b/include/opentracing-c-wrapper/dbg_malloc.h new file mode 100644 index 0000000..4af56ca --- /dev/null +++ b/include/opentracing-c-wrapper/dbg_malloc.h @@ -0,0 +1,86 @@ +/*** + * Copyright 2020 HAProxy Technologies + * + * 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. + */ +#ifndef OPENTRACING_C_WRAPPER_DBG_MALLOC_H +#define OPENTRACING_C_WRAPPER_DBG_MALLOC_H + +__CPLUSPLUS_DECL_BEGIN + +#ifdef OTC_DBG_MEM + +#define OTC_DBG_MALLOC(s) otc_dbg_malloc(__func__, __LINE__, (s)) +#define OTC_DBG_CALLOC(n,e) otc_dbg_calloc(__func__, __LINE__, (n), (e)) +#define OTC_DBG_REALLOC(p,s) otc_dbg_realloc(__func__, __LINE__, (p), (s)) +#define OTC_DBG_FREE(p) otc_dbg_free(__func__, __LINE__, (p)) +#define OTC_DBG_STRDUP(s) otc_dbg_strdup(__func__, __LINE__, (s)) +#define OTC_DBG_STRNDUP(s,n) otc_dbg_strndup(__func__, __LINE__, (s), (n)) +#define OTC_DBG_MEMDUP(s,n) otc_dbg_memdup(__func__, __LINE__, (s), (n)) +#define OTC_DBG_MEMINFO() otc_dbg_mem_info() + + +struct otc_dbg_mem_data { + const void *ptr; + size_t size; + char func[63]; + bool used; +} __attribute__((packed)); + +struct otc_dbg_mem { + struct otc_dbg_mem_data *data; + size_t count; + size_t unused; + size_t reused; + uint64_t size; + uint64_t op_cnt[4]; + uint8_t level; + pthread_mutex_t mutex; +}; + + +void *otc_dbg_malloc(const char *func, int line, size_t size); +void *otc_dbg_calloc(const char *func, int line, size_t nelem, size_t elsize); +void *otc_dbg_realloc(const char *func, int line, void *ptr, size_t size); +void otc_dbg_free(const char *func, int line, void *ptr); +char *otc_dbg_strdup(const char *func, int line, const char *s); +char *otc_dbg_strndup(const char *func, int line, const char *s, size_t size); +void *otc_dbg_memdup(const char *func, int line, const void *s, size_t size); +int otc_dbg_mem_init(struct otc_dbg_mem *mem, struct otc_dbg_mem_data *data, size_t count, uint8_t level); +void otc_dbg_mem_disable(void); +void otc_dbg_mem_info(void); + +#else + +#define OTC_DBG_MALLOC(s) malloc(s) +#define OTC_DBG_CALLOC(n,e) calloc((n), (e)) +#define OTC_DBG_REALLOC(p,s) realloc((p), (s)) +#define OTC_DBG_FREE(p) free(p) +#define OTC_DBG_STRDUP(s) strdup(s) +#define OTC_DBG_STRNDUP(s,n) strndup((s), (n)) +#define OTC_DBG_MEMDUP(s,n) mem_dup((s), (n)) +#define OTC_DBG_MEMINFO() while (0) + +#endif /* OTC_DBG_MEM */ + +__CPLUSPLUS_DECL_END +#endif /* OPENTRACING_C_WRAPPER_DBG_MALLOC_H */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/include/opentracing-c-wrapper/define.h b/include/opentracing-c-wrapper/define.h new file mode 100644 index 0000000..457e549 --- /dev/null +++ b/include/opentracing-c-wrapper/define.h @@ -0,0 +1,46 @@ +/*** + * Copyright 2020 HAProxy Technologies + * + * 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. + */ +#ifndef OPENTRACING_C_WRAPPER_DEFINE_H +#define OPENTRACING_C_WRAPPER_DEFINE_H + +#define OTC_MAXLOGFIELDS 8 + +#ifdef __cplusplus +# define __CPLUSPLUS_DECL_BEGIN extern "C" { +# define __CPLUSPLUS_DECL_END } +#else +# define __CPLUSPLUS_DECL_BEGIN +# define __CPLUSPLUS_DECL_END +#endif + +#ifdef __GNUC__ +# define OTC_NONNULL(...) __attribute__((nonnull(__VA_ARGS__))) +# define OTC_NONNULL_ALL __attribute__((nonnull)) +#else +# define OTC_NONNULL(...) +# define OTC_NONNULL_ALL +#endif + +#endif /* OPENTRACING_C_WRAPPER_DEFINE_H */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/include/opentracing-c-wrapper/include.h b/include/opentracing-c-wrapper/include.h new file mode 100644 index 0000000..ac2f3e4 --- /dev/null +++ b/include/opentracing-c-wrapper/include.h @@ -0,0 +1,39 @@ +/*** + * Copyright 2020 HAProxy Technologies + * + * 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. + */ +#ifndef OPENTRACING_C_WRAPPER_INCLUDE_H +#define OPENTRACING_C_WRAPPER_INCLUDE_H + +#include <stdbool.h> + +#include <opentracing-c-wrapper/define.h> +#include <opentracing-c-wrapper/dbg_malloc.h> +#include <opentracing-c-wrapper/common.h> +#include <opentracing-c-wrapper/util.h> +#include <opentracing-c-wrapper/value.h> +#include <opentracing-c-wrapper/span.h> +#include <opentracing-c-wrapper/propagation.h> +#include <opentracing-c-wrapper/tracer.h> + +#endif /* OPENTRACING_C_WRAPPER_INCLUDE_H */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/include/opentracing-c-wrapper/propagation.h b/include/opentracing-c-wrapper/propagation.h new file mode 100644 index 0000000..636ca37 --- /dev/null +++ b/include/opentracing-c-wrapper/propagation.h @@ -0,0 +1,201 @@ +/*** + * Copyright 2020 HAProxy Technologies + * + * 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. + */ +#ifndef OPENTRACING_C_WRAPPER_PROPAGATION_H +#define OPENTRACING_C_WRAPPER_PROPAGATION_H + +__CPLUSPLUS_DECL_BEGIN + +/*** + * span context propagation error codes + */ +typedef enum { + /*** + * success + */ + otc_propagation_error_code_success = 0, + + /*** + * not enough information to extract a span context + */ + otc_propagation_error_code_span_context_not_found = -2, + + /*** + * invalid/incompatible span context + */ + otc_propagation_error_code_invalid_span_context = -3, + + /*** + * invalid carrier + */ + otc_propagation_error_code_invalid_carrier = -4, + + /*** + * corrupted carrier + */ + otc_propagation_error_code_span_context_corrupted = -5, + + /*** + * unknown error + */ + otc_propagation_error_code_unknown = -6, + + /*** + * tracer not initialized + */ + otc_propagation_error_code_invalid_tracer = -7, + +} otc_propagation_error_code_t; + + +/*** + * Used to set information into a span context for propagation, entries + * are strings in a map of string pointers + * + * set and get implementations should use the same convention + * for naming the keys they manipulate + */ +struct otc_text_map_writer { + struct otc_text_map text_map; + + /*** + * NAME + * set - + * + * ARGUMENTS + * writer - writer instance + * key - string key + * value - string value + * + * DECRTIPTION + * set a key-value pair + * + * RETURN VALUE + * otc_propagation_error_code_t - indicates success or failure + */ + otc_propagation_error_code_t (*set)(struct otc_text_map_writer *writer, const char *key, const char *value) + OTC_NONNULL(1); +}; + +/*** + * Used to get information from a propagated span context + * + * set and get implementations should use the same convention + * for naming the keys they manipulate + */ +struct otc_text_map_reader { + struct otc_text_map text_map; + + /*** + * NAME + * foreach_key - + * + * ARGUMENTS + * reader - reader instance + * handler - handler function for each key-value pair + * arg - user-defined contents + * + * DESCRIPTION + * gets map entries by colling the handler function repeatedly. Returns immediately upon first error + * + * RETURN VALUE + * - error code indicating success or failure. + */ + otc_propagation_error_code_t (*foreach_key)(struct otc_text_map_reader *reader, otc_propagation_error_code_t (*handler)(void *arg, const char *key, const char *value), void *arg) + OTC_NONNULL(1, 2); +}; + +/*** + * Used to set HTTP headers + */ +struct otc_http_headers_writer { + struct otc_text_map text_map; + + otc_propagation_error_code_t (*set)(struct otc_http_headers_writer *writer, const char *key, const char *value) + OTC_NONNULL(1); +}; + +/*** + * USed to get HTTP headers + */ +struct otc_http_headers_reader { + struct otc_text_map text_map; + + otc_propagation_error_code_t (*foreach_key)(struct otc_http_headers_reader *reader, otc_propagation_error_code_t (*handler)(void *arg, const char *key, const char *value), void *arg) + OTC_NONNULL(1, 2); +}; + +/*** + * Set/write to a custom format + */ +struct otc_custom_carrier_writer { + struct otc_binary_data binary_data; + + /*** + * NAME + * inject - + * + * ARGUMENTS + * writer - writer instance + * tracer - tracer instance + * span_context - span context to write + * DESCRIPTION + * write a span context into a custom format + * + * RETURN VALUE + * - error code indicating success or failure + */ + otc_propagation_error_code_t (*inject)(struct otc_custom_carrier_writer *writer, const struct otc_tracer *tracer, const struct otc_span_context *span_context) + OTC_NONNULL_ALL; +}; + +/*** + * Get from a custom format + */ +struct otc_custom_carrier_reader { + struct otc_binary_data binary_data; + + /*** + * NAME + * extract - + * + * ARGUMENTS + * reader - reader instance + * tracer - tracer instance + * span_context - span context pointer to return the decoded + * span (can be NULL if propagation failed + * or OOM) + * + * DESCRIPTION + * - get a span context from a custom format + * + * RETURN VALUE + * - error code indicating success or failure + */ + otc_propagation_error_code_t (*extract)(struct otc_custom_carrier_reader *reader, const struct otc_tracer *tracer, struct otc_span_context **span_context) + OTC_NONNULL_ALL; +}; + +__CPLUSPLUS_DECL_END +#endif /* OPENTRACING_C_WRAPPER_PROPAGATION_H */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/include/opentracing-c-wrapper/span.h b/include/opentracing-c-wrapper/span.h new file mode 100644 index 0000000..2ecf8b5 --- /dev/null +++ b/include/opentracing-c-wrapper/span.h @@ -0,0 +1,274 @@ +/*** + * Copyright 2020 HAProxy Technologies + * + * 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. + */ +#ifndef OPENTRACING_C_WRAPPER_SPAN_H +#define OPENTRACING_C_WRAPPER_SPAN_H + +__CPLUSPLUS_DECL_BEGIN + +/*** + * Encode a key-value for logging + */ +struct otc_log_field { + /*** + * key string + */ + const char *key; + + /*** + * representation of the value + */ + struct otc_value value; +}; + +/*** + * A log entry for events that occured during the span lifetime + */ +struct otc_log_record { + /*** + * event timestamp + */ + struct otc_timestamp timestamp; + + /*** + * array of log fields + */ + struct otc_log_field *fields; + + /*** + * number of log fields, must be set to zero if fields is NULL + */ + int num_fields; +}; + +typedef enum { + /*** + * parent span that created and also depends on the child span + */ + otc_span_reference_child_of = 1, + + /*** + * parent span that doesn't dpeend on the child span + * + * more details in http://opentracing.io/spec/ + */ + otc_span_reference_follows_from = 2 +} otc_span_reference_type_t; + +struct otc_span_reference { + otc_span_reference_type_t type; + struct otc_span_context *referenced_context; +}; + +struct otc_finish_span_options { + /*** + * time when the span finished (monotonic clock) + */ + struct otc_duration finish_time; + + /*** + * array with otc_log_record entries + */ + const struct otc_log_record *log_records; + + /*** + * number of log records, must be set to zero if fields is NULL + */ + int num_log_records; +}; + +/*** + * The span interface + */ +struct otc_span { + int64_t idx; + + /*** + * NAME + * finish - + * + * ARGUMENTS + * span - span instance + * + * DESCRIPTION + * sets the ending timestamp and finalizes span. Must be the last call for a span instance (except calls to context) + */ + void (*finish)(struct otc_span *span) + OTC_NONNULL_ALL; + + /*** + * NAME + * finish_with_options - + * + * ARGUMENTS + * span - span instance + * options - override span completion with these options + * + * DESCRIPTION + * finalizes the span but allows for customizing the timestamp and log data + */ + void (*finish_with_options)(struct otc_span *span, const struct otc_finish_span_options *options) + OTC_NONNULL(1); + + /*** + * NAME + * otc_span_context - + * + * ARGUMENTS + * span - span instance + * + * DESCRIPTION + * returns an otc_span_context for the span + * + * RETURN VALUE + * span - context for this span + */ + struct otc_span_context *(*span_context)(struct otc_span *span) + OTC_NONNULL_ALL; + + /*** + * NAME + * set_operation_name - + * + * ARGUMENTS + * span - span instance + * operation_name - new operation name + * + * DESCRIPTION + * set or change the operation name + */ + void (*set_operation_name)(struct otc_span *span, const char *operation_name) + OTC_NONNULL_ALL; + + /*** + * NAME + * set_tag + * + * ARGUMENTS + * span - span instance + * key - tag key to copy into the span + * value - tag value to copy into the span + * + * DESCRIPTION + * add a tag to a span, overwriting any existing tag with the + * same key. Tag values supported are strings, numbers and bools + */ + void (*set_tag)(struct otc_span *span, const char *key, const struct otc_value *value) + OTC_NONNULL_ALL; + + /*** + * NAME + * log_fields - + * + * ARGUMENTS + * span - span instance + * fields - log fields as an array, values should be copied by the implementation + * num_fields - number of log fields in the log field array + * + * DESCRIPTION + * store log data for a span + */ + void (*log_fields)(struct otc_span *span, const struct otc_log_field *fields, int num_fields) + OTC_NONNULL(1); + + /*** + * NAME + * set_baggage_item - + * + * ARGUMENTS + * span - span instance + * key - baggage key + * value - baggage value + * + * DESCRIPTION + * store a key-value pair to this span and the associated span + * context, also propagating to descendents + */ + void (*set_baggage_item)(struct otc_span *span, const char *key, const char *value) + OTC_NONNULL_ALL; + + /*** + * NAME + * baggage_item - + * + * ARGUMENTS + * span - span instance + * key - baggage key + * + * DESCRIPTION + * get the baggage for a baggage key + * + * RETURN VALUE + * baggage value for the key, or empty string if the key doesn't exist + */ + const char *(*baggage_item)(const struct otc_span *span, const char *key) + OTC_NONNULL_ALL; + + /*** + * NAME + * tracer - + * + * ARGUMENTS + * span - span instance + * + * DESCRIPTION + * returns the tracer that created the span + * + * RETURN VALUE + * tracer instance that creaated the span + */ + struct otc_tracer *(*tracer)(const struct otc_span *span) + OTC_NONNULL_ALL; + + /*** + * NAME + * destroy - + * + * ARGUMENTS + * span - span instance + */ + void (*destroy)(struct otc_span **span) + OTC_NONNULL_ALL; +}; + +/*** + * interface for span context + */ +struct otc_span_context { + int64_t idx; + const struct otc_span *span; + + /*** + * NAME + * destroy - + * + * ARGUMENTS + * context - instance of span context + */ + void (*destroy)(struct otc_span_context **context) + OTC_NONNULL_ALL; +}; + +__CPLUSPLUS_DECL_END +#endif /* OPENTRACING_C_WRAPPER_SPAN_H */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/include/opentracing-c-wrapper/tracer.h b/include/opentracing-c-wrapper/tracer.h new file mode 100644 index 0000000..ca27e1e --- /dev/null +++ b/include/opentracing-c-wrapper/tracer.h @@ -0,0 +1,93 @@ +/*** + * Copyright 2020 HAProxy Technologies + * + * 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. + */ +#ifndef OPENTRACING_C_WRAPPER_TRACER_H +#define OPENTRACING_C_WRAPPER_TRACER_H + +__CPLUSPLUS_DECL_BEGIN + +struct otc_tag { + const char *key; + struct otc_value value; +}; + +struct otc_start_span_options { + struct otc_duration start_time_steady; + struct otc_timestamp start_time_system; + const struct otc_span_reference *references; + int num_references; + const struct otc_tag *tags; + int num_tags; +}; + +/*** + * tracer interface + */ +struct otc_tracer { + void (*close)(struct otc_tracer *tracer) + OTC_NONNULL_ALL; + + struct otc_span *(*start_span)(struct otc_tracer *tracer, const char *operation_name) + OTC_NONNULL_ALL; + + struct otc_span *(*start_span_with_options)(struct otc_tracer *tracer, const char *operation_name, const struct otc_start_span_options *options) + OTC_NONNULL(1, 2); + + otc_propagation_error_code_t (*inject_text_map)(struct otc_tracer *tracer, struct otc_text_map_writer *carrier, const struct otc_span_context *span_context) + OTC_NONNULL_ALL; + + otc_propagation_error_code_t (*inject_http_headers)(struct otc_tracer *tracer, struct otc_http_headers_writer *carrier, const struct otc_span_context *span_context) + OTC_NONNULL_ALL; + + otc_propagation_error_code_t (*inject_binary)(struct otc_tracer *tracer, struct otc_custom_carrier_writer *carrier, const struct otc_span_context *span_context) + OTC_NONNULL_ALL; + + otc_propagation_error_code_t (*inject_custom)(struct otc_tracer *tracer, struct otc_custom_carrier_writer *carrier, const struct otc_span_context *span_context) + OTC_NONNULL_ALL; + + otc_propagation_error_code_t (*extract_text_map)(struct otc_tracer *tracer, const struct otc_text_map_reader *carrier, struct otc_span_context **span_context) + OTC_NONNULL_ALL; + + otc_propagation_error_code_t (*extract_http_headers)(struct otc_tracer *tracer, const struct otc_http_headers_reader *carrier, struct otc_span_context **span_context) + OTC_NONNULL_ALL; + + otc_propagation_error_code_t (*extract_binary)(struct otc_tracer *tracer, const struct otc_custom_carrier_reader *carrier, struct otc_span_context **span_context) + OTC_NONNULL_ALL; + + otc_propagation_error_code_t (*extract_custom)(struct otc_tracer *tracer, const struct otc_custom_carrier_reader *carrier, struct otc_span_context **span_context) + OTC_NONNULL_ALL; + + void (*destroy)(struct otc_tracer **tracer) + OTC_NONNULL_ALL; +}; + + +struct otc_tracer *otc_tracer_init(const char *library, const char *cfgfile, const char *cfgbuf, char *errbuf, int errbufsiz); +struct otc_tracer *otc_tracer_load(const char *library, char *errbuf, int errbufsiz); +int otc_tracer_start(const char *cfgfile, const char *cfgbuf, char *errbuf, int errbufsiz); +void otc_tracer_global(struct otc_tracer *tracer); +void otc_tracer_init_global(struct otc_tracer *tracer); + +__CPLUSPLUS_DECL_END +#endif /* OPENTRACING_C_WRAPPER_TRACER_H */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/include/opentracing-c-wrapper/util.h b/include/opentracing-c-wrapper/util.h new file mode 100644 index 0000000..c47c1e4 --- /dev/null +++ b/include/opentracing-c-wrapper/util.h @@ -0,0 +1,110 @@ +/*** + * Copyright 2020 HAProxy Technologies + * + * 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. + */ +#ifndef OPENTRACING_C_WRAPPER_UTIL_H +#define OPENTRACING_C_WRAPPER_UTIL_H + +__CPLUSPLUS_DECL_BEGIN + +typedef enum { + OTC_TEXT_MAP_DUP_KEY = 0x01, /* Duplicate the key data. */ + OTC_TEXT_MAP_DUP_VALUE = 0x02, /* Duplicate the value data. */ + OTC_TEXT_MAP_FREE_KEY = 0x04, /* Release the key data. */ + OTC_TEXT_MAP_FREE_VALUE = 0x08, /* Release the value data. */ +} otc_text_map_flags_t; + +struct otc_text_map { + char **key; + char **value; + size_t count; + size_t size; + bool is_dynamic; +}; + +struct otc_binary_data { + void *data; + size_t size; + bool is_dynamic; +}; + +/* + * Jaeger Trace/Span Identity + * uber-trace-id: {trace-id}:{span-id}:{parent-span-id}:{flags} + * uberctx-{baggage-key}: {value} + * + * Zipkin + * x-b3-traceid: {trace-id} + * x-b3-spanid: {span-id} + * x-b3-parentspanid: {parent-span-id} + * x-b3-flags: {flags} + * x-b3-sampled: {value} + * ot-baggage-{baggage-key}: {value} + * + * DataDog + * x-datadog-trace-id: {trace-id} + * x-datadog-parent-id: {parent-id} + * x-datadog-sampling-priority: {value} + * ot-baggage-{baggage-key}: {value} + */ +struct otc_jaeger_trace_context { + uint64_t trace_id[2]; /* 128-bit random number, value of 0 is not valid. */ + uint64_t span_id; /* 64-bit random number, value of 0 is not valid. */ + uint64_t parent_span_id; /* 64-bit parent span id, 0 value is valid and means 'root span' */ + uint8_t flags; /* 8-bit bitmap, only bits 0 and 1 are used. */ + uint8_t baggage[0]; +} __attribute__((packed)); + +struct otc_zipkin_trace_context { + uint8_t data[0]; +} __attribute__((packed)); + +struct otc_dd_trace_context { + uint8_t data[0]; +} __attribute__((packed)); + + +#ifdef OTC_DBG_MEM +typedef void *(*otc_ext_malloc_t)(const char *, int, size_t); +typedef void (*otc_ext_free_t)(const char *, int, void *); +#else +typedef void *(*otc_ext_malloc_t)(size_t); +typedef void (*otc_ext_free_t)(void *); +#endif + + +void otc_ext_init(otc_ext_malloc_t func_malloc, otc_ext_free_t func_free); + +struct otc_text_map *otc_text_map_new(struct otc_text_map *text_map, size_t size); +int otc_text_map_add(struct otc_text_map *text_map, const char *key, size_t key_len, const char *value, size_t value_len, otc_text_map_flags_t flags); +void otc_text_map_destroy(struct otc_text_map **text_map, otc_text_map_flags_t flags); + +struct otc_binary_data *otc_binary_data_new(struct otc_binary_data *binary_data, const void *data, size_t size); +void otc_binary_data_destroy(struct otc_binary_data **binary_data); + +char *otc_file_read(const char *filename, const char *comment, char *errbuf, int errbufsiz); + +void otc_statistics(char *buffer, size_t bufsiz); + +__CPLUSPLUS_DECL_END +#endif /* OPENTRACING_C_WRAPPER_UTIL_H */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/include/opentracing-c-wrapper/value.h b/include/opentracing-c-wrapper/value.h new file mode 100644 index 0000000..b9a55fd --- /dev/null +++ b/include/opentracing-c-wrapper/value.h @@ -0,0 +1,60 @@ +/*** + * Copyright 2020 HAProxy Technologies + * + * 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. + */ +#ifndef OPENTRACING_C_WRAPPER_VALUE_H +#define OPENTRACING_C_WRAPPER_VALUE_H + +__CPLUSPLUS_DECL_BEGIN + +/*** + * value types + */ +typedef enum { + otc_value_bool = 0, + otc_value_double, + otc_value_int64, + otc_value_uint64, + otc_value_string, + otc_value_null, +} otc_value_type_t; + + +/*** + * union for representing various value types + */ +struct otc_value { + + otc_value_type_t type; + + union { + otc_bool_t bool_value; + double double_value; + int64_t int64_value; + uint64_t uint64_value; + const char *string_value; + } value; +}; + +__CPLUSPLUS_DECL_END +#endif /* OPENTRACING_C_WRAPPER_VALUE_H */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/include/span.h b/include/span.h new file mode 100644 index 0000000..14c0b6c --- /dev/null +++ b/include/span.h @@ -0,0 +1,94 @@ +/*** + * Copyright 2020 HAProxy Technologies + * + * 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. + */ +#ifndef _OPENTRACING_C_WRAPPER_SPAN_H_ +#define _OPENTRACING_C_WRAPPER_SPAN_H_ + +#define OT_LF(a) { fields[a].key, str_value[a] } + + +class otc_hash { + public: + size_t operator() (int64_t key) const { return key; } +}; + + +class otc_equal_to { + public: + bool operator() (int64_t a, int64_t b) const { return a == b; } +}; + + +# ifdef OT_THREADS_NO_LOCKING +template<typename T> + using Handle = std::unordered_map< + int64_t, + std::unique_ptr<T>, + otc_hash, + otc_equal_to + >; +struct HandleData { + int64_t key; + int64_t alloc_fail_cnt; + int64_t erase_cnt; + int64_t destroy_cnt; +}; + +# define OT_LOCK_GUARD(a,...) +# define OT_LOCK(a,b) + +extern thread_local Handle<opentracing::Span> ot_span_handle; +extern thread_local Handle<opentracing::SpanContext> ot_span_context_handle; +extern struct HandleData ot_span; +extern struct HandleData ot_span_context; +# else +template<typename T> struct Handle { + std::unordered_map< + int64_t, + std::unique_ptr<T>, + otc_hash, + otc_equal_to + > handle; + int64_t key; + int64_t alloc_fail_cnt; + int64_t erase_cnt; + int64_t destroy_cnt; + std::mutex mutex; +}; + +# define ot_span_handle ot_span.handle +# define ot_span_context_handle ot_span_context.handle +# define OT_LOCK_GUARD(a,...) const std::lock_guard<std::mutex> guard_##a(ot_##a.mutex, ##__VA_ARGS__) +# define OT_LOCK(a,b) std::lock(ot_##a.mutex, ot_##b.mutex); OT_LOCK_GUARD(a, std::adopt_lock); OT_LOCK_GUARD(b, std::adopt_lock); + +extern struct Handle<opentracing::Span> ot_span; +extern struct Handle<opentracing::SpanContext> ot_span_context; +# endif /* OT_THREADS_NO_LOCKING */ + + +struct otc_span *ot_span_new(void); +void ot_nolock_span_destroy(struct otc_span **span); +struct otc_span_context *ot_span_context_new(const struct otc_span *span); + +#endif /* _OPENTRACING_C_WRAPPER_SPAN_H_ */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/include/tracer.h b/include/tracer.h new file mode 100644 index 0000000..371f2b5 --- /dev/null +++ b/include/tracer.h @@ -0,0 +1,133 @@ +/*** + * Copyright 2020 HAProxy Technologies + * + * 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. + */ +#ifndef _OPENTRACING_C_WRAPPER_TRACER_H_ +#define _OPENTRACING_C_WRAPPER_TRACER_H_ + +using TextMap = std::unordered_map<std::string, std::string>; + + +class TextMapCarrier : public opentracing::TextMapReader, public opentracing::TextMapWriter { + public: + TextMapCarrier(TextMap &text_map) : tm_data(text_map) {} + + /*** + * TextMapWriter: Set a key:value pair to the carrier. Multiple calls + * to Set() for the same key leads to undefined behavior. + */ + opentracing::expected<void> Set(opentracing::string_view key, opentracing::string_view value) const override + { + tm_data[key] = value; + + return {}; + } + + /*** + * TextMapReader: LookupKey() returns the value for the specified + * key if available. If no such key is present, it returns + * key_not_found_error. + */ + opentracing::expected<opentracing::string_view> LookupKey(opentracing::string_view key) const override + { + auto iter = tm_data.find(key); + if (iter != tm_data.end()) + return opentracing::string_view{iter->second}; + + return opentracing::make_unexpected(opentracing::key_not_found_error); + } + + /*** + * TextMapReader: ForeachKey() returns TextMap contents via repeated + * calls to the f() function. If any call to f() returns an error, + * ForeachKey() terminates and returns that error. + */ + opentracing::expected<void> ForeachKey(std::function<opentracing::expected<void>(opentracing::string_view key, opentracing::string_view value)> f) const override + { + for (const auto &text_map : tm_data) { + auto result = f(text_map.first, text_map.second); + if (!result) + return result; + } + + return {}; + } + + private: + TextMap &tm_data; +}; + + +class HTTPHeadersCarrier : public opentracing::HTTPHeadersReader, public opentracing::HTTPHeadersWriter { + public: + HTTPHeadersCarrier(TextMap &text_map) : tm_data(text_map) {} + + /*** + * HTTPHeadersWriter: Set a key:value pair to the carrier. Multiple calls + * to Set() for the same key leads to undefined behavior. + */ + opentracing::expected<void> Set(opentracing::string_view key, opentracing::string_view value) const override + { + tm_data[key] = value; + + return {}; + } + + /*** + * HTTPHeadersReader: LookupKey() returns the value for the specified + * key if available. If no such key is present, it returns + * key_not_found_error. + */ + opentracing::expected<opentracing::string_view> LookupKey(opentracing::string_view key) const override + { + auto iter = tm_data.find(key); + if (iter != tm_data.end()) + return opentracing::string_view{iter->second}; + + return opentracing::make_unexpected(opentracing::key_not_found_error); + } + + /*** + * HTTPHeadersReader: ForeachKey() returns TextMap contents via repeated + * calls to the f() function. If any call to f() returns an error, + * ForeachKey() terminates and returns that error. + */ + opentracing::expected<void> ForeachKey(std::function<opentracing::expected<void>(opentracing::string_view key, opentracing::string_view value)> f) const override + { + for (const auto &text_map : tm_data) { + auto result = f(text_map.first, text_map.second); + if (!result) + return result; + } + + return {}; + } + + private: + TextMap &tm_data; +}; + + +struct otc_tracer *ot_tracer_new(void); + +#endif /* _OPENTRACING_C_WRAPPER_TRACER_H_ */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/include/util.h b/include/util.h new file mode 100644 index 0000000..50a4109 --- /dev/null +++ b/include/util.h @@ -0,0 +1,46 @@ +/*** + * Copyright 2020 HAProxy Technologies + * + * 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. + */ +#ifndef _OPENTRACING_C_WRAPPER_UTIL_H_ +#define _OPENTRACING_C_WRAPPER_UTIL_H_ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> + + +/* Parameter 'p' must not be in parentheses! */ +#define OT_TEXT_MAP_SIZE(p,n) (sizeof(text_map->p) * (text_map->size + (n))) + + +extern otc_ext_malloc_t otc_ext_malloc; +extern otc_ext_free_t otc_ext_free; + + +std::chrono::microseconds timespec_to_duration_us(const struct timespec *ts); +std::chrono::nanoseconds timespec_to_duration(const struct timespec *ts); +const char *otc_strerror(int errnum); + +#endif /* _OPENTRACING_C_WRAPPER_UTIL_H_ */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/include/version.h.in b/include/version.h.in new file mode 100644 index 0000000..ea00000 --- /dev/null +++ b/include/version.h.in @@ -0,0 +1,19 @@ +#ifndef _OPENTRACING_C_WRAPPER_VERSION_H_ +#define _OPENTRACING_C_WRAPPER_VERSION_H_ + +#define PACKAGE_AUTHOR "Miroslav Zagorac <mzagorac@haproxy.com>" +#define PACKAGE_CPP_FLAGS "@CPPFLAGS@" +#define PACKAGE_DEFS "@DEFS@" +#define PACKAGE_C_FLAGS "@CFLAGS@" +#define PACKAGE_CONFIGURE_OPTIONS "@CONFIGURE_OPTIONS@" + +#endif /* _OPENTRACING_C_WRAPPER_VERSION_H_ */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/m4/am-common.m4 b/m4/am-common.m4 new file mode 100644 index 0000000..11c310b --- /dev/null +++ b/m4/am-common.m4 @@ -0,0 +1,375 @@ +dnl am-common.m4 by Miroslav Zagorac <mzagorac@haproxy.com> +dnl +AC_DEFUN([AX_TYPE_BOOL_T], [AC_CHECK_TYPE([bool_t], [unsigned char])]) +AC_DEFUN([AX_TYPE_UNCHAR], [AC_CHECK_TYPE([unchar], [unsigned char])]) +AC_DEFUN([AX_TYPE_USHORT], [AC_CHECK_TYPE([ushort], [unsigned short])]) +AC_DEFUN([AX_TYPE_ULONG], [AC_CHECK_TYPE([ulong], [unsigned long])]) +AC_DEFUN([AX_TYPE_UINT], [AC_CHECK_TYPE([uint], [unsigned int])]) + +AC_DEFUN([AX_VARIABLES_STORE], [ + _saved_cppflags="${CPPFLAGS}" + _saved_cflags="${CFLAGS}" + _saved_cxxflags="${CXXFLAGS}" + _saved_ldflags="${LDFLAGS}" + _saved_libs="${LIBS}" +]) + +AC_DEFUN([AX_VARIABLES_RESTORE], [ + CPPFLAGS="${_saved_cppflags}" + CFLAGS="${_saved_cflags}" + CXXFLAGS="${_saved_cxxflags}" + LDFLAGS="${_saved_ldflags}" + LIBS="${_saved_libs}" +]) + +AC_DEFUN([AX_VARIABLES_INIT], [ + SET_CPPFLAGS= + SET_CFLAGS= + SET_CXXFLAGS= + SET_LDFLAGS= + SET_LIBS= +]) + +AC_DEFUN([AX_VARIABLES_SET], [ + CPPFLAGS="${CPPFLAGS} ${SET_CPPFLAGS}" + CFLAGS="${CFLAGS} ${SET_CFLAGS}" + CXXFLAGS="${CXXFLAGS} ${SET_CXXFLAGS}" + LDFLAGS="${LDFLAGS} ${SET_LDFLAGS}" + LIBS="${LIBS} ${SET_LIBS}" +]) + +dnl Check which options the C compiler supports. +dnl +AC_DEFUN([AX_PROG_CC_SET], [ + _var_cflags= + _loop_cflags= + + AX_VARIABLES_STORE + + case "${CC}" in + *gcc*) + _var_cflags="\ + -Wall \ + -Wextra \ + -Waggregate-return \ + -Wbad-function-cast \ + -Wcast-align \ + -Wchar-subscripts \ + -Wcomment \ + -Wfloat-equal \ + -Wimplicit \ + -Winline \ + -Wmain \ + -Wmissing-braces \ + -Wmissing-declarations \ + -Wmissing-noreturn \ + -Wmissing-prototypes \ + -Wnested-externs \ + -Wparentheses \ + -Wpointer-arith \ + -Wreturn-type \ + -Wsequence-point \ + -Wshadow \ + -Wstrict-prototypes \ + -Wswitch \ + -Wtrigraphs \ + -Wundef \ + -Wuninitialized \ + -Wunused \ + -Wwrite-strings" + if test "${enable_debug}" = "yes"; then + _var_cflags="${_var_cflags} \ + -Wformat=2" + else + _var_cflags="${_var_cflags} \ + -Wformat-security \ + -Wformat-y2k" + fi + ;; + cc|clang) + case "${host_os}" in + *solaris*) + _var_cflags="-xCC" + ;; + *freebsd1?.*) + _var_cflags= + ;; + esac + ;; + esac + + TMP_CFLAGS="${CFLAGS}" + AC_LANG_PUSH([C]) + for _loop_cflags in ${_var_cflags} $1; do + AC_MSG_CHECKING([whether ${CC} accepts ${_loop_cflags}]) + CFLAGS="${TMP_CFLAGS} ${_loop_cflags}" + AC_TRY_COMPILE([], [], + [AC_MSG_RESULT([ yes]) + SET_CFLAGS="${SET_CFLAGS} ${_loop_cflags}"], + [AC_MSG_RESULT([ no])] + ) + done + AC_LANG_POP([C]) + CFLAGS="${TMP_CFLAGS}" + + AX_VARIABLES_RESTORE +]) + +dnl Check which options the C++ compiler supports. +dnl +AC_DEFUN([AX_PROG_CXX_SET], [ + _var_cxxflags= + _loop_cxxflags= + + AX_VARIABLES_STORE + + case "${CXX}" in + c++|clang++) + case "${host_os}" in + *freebsd1?.*) + _var_cxxflags="-Wno-extern-c-compat" + ;; + esac + ;; + *g++*) + _var_cxxflags="\ + -Wall \ + -Wextra \ + -Wcast-align \ + -Wchar-subscripts \ + -Wcomment \ + -Wfloat-equal \ + -Winline \ + -Wmain \ + -Wmissing-braces \ + -Wmissing-declarations \ + -Wmissing-noreturn \ + -Wparentheses \ + -Wpointer-arith \ + -Wreturn-type \ + -Wsequence-point \ + -Wshadow \ + -Wswitch \ + -Wtrigraphs \ + -Wundef \ + -Wuninitialized \ + -Wunused \ + -Wwrite-strings \ + \ + -Wconditionally-supported \ + -Wnamespaces \ + -Wno-invalid-offsetof \ + -Wno-terminate \ + -Wnoexcept \ + -Wold-style-cast \ + -Woverloaded-virtual \ + -Wsign-promo \ + -Wsized-deallocation \ + -Wstrict-null-sentinel \ + -Wuseless-cast \ + -Wvirtual-inheritance \ + -Wzero-as-null-pointer-constant" + if test "${enable_debug}" = "yes"; then + _var_cxxflags="${_var_cxxflags} \ + -Waggregate-return \ + -Wformat=2 \ + -Wmultiple-inheritance \ + -Wtemplates" + else + _var_cxxflags="${_var_cxxflags} \ + -Wformat-security \ + -Wformat-y2k" + fi + ;; + CC) + case "${host_os}" in + *solaris*) + _var_cxxflags= + ;; + esac + ;; + esac + + TMP_CXXFLAGS="${CXXFLAGS}" + AC_LANG_PUSH([C++]) + for _loop_cxxflags in ${_var_cxxflags} $1; do + AC_MSG_CHECKING([whether ${CXX} accepts ${_loop_cxxflags}]) + CXXFLAGS="${TMP_CXXFLAGS} ${_loop_cxxflags}" + AC_TRY_COMPILE([], [], + [AC_MSG_RESULT([ yes]) + SET_CXXFLAGS="${SET_CXXFLAGS} ${_loop_cxxflags}"], + [AC_MSG_RESULT([ no])] + ) + done + AC_LANG_POP([C++]) + CXXFLAGS="${TMP_CXXFLAGS}" + + AX_VARIABLES_RESTORE +]) + +dnl Check whether the C++ compiler has noexcept specifier. +dnl +AC_DEFUN([AX_CHECK_NOEXCEPT], [ + AC_MSG_CHECKING([whether the C++ compiler (${CXX}) has noexcept specifier]) + AC_LANG_PUSH([C++]) + AC_TRY_COMPILE([], + [void (*fp)() noexcept(false);], + [AC_MSG_RESULT([ yes])], + [CXXFLAGS="-std=gnu++11" + AC_MSG_RESULT([ no])] + ) + AC_LANG_POP([C++]) +]) + +dnl Check whether the C compiler has __DATE__ macro. +dnl +AC_DEFUN([AX_CHECK___DATE__], [ + AC_MSG_CHECKING([whether the C compiler (${CC}) has __DATE__ macro]) + AC_TRY_COMPILE([], + [char *test=__DATE__;], + [AC_MSG_RESULT([ yes])], + [AC_DEFINE_UNQUOTED([__DATE__], ["`date`"], [Define if your C compiled doesn't have __DATE__ macro.]) + AC_MSG_RESULT([ no])] + ) +]) + +dnl Check whether the C compiler has __func__ variable. +dnl +AC_DEFUN([AX_CHECK___FUNC__], [ + AC_MSG_CHECKING([whether the C compiler (${CC}) has __func__ variable]) + AC_TRY_COMPILE([#include <stdio.h>], + [printf ("%s", __func__);], + [AC_MSG_RESULT([ yes])], + [AC_DEFINE_UNQUOTED([__func__], ["__unknown__"], [Define if your C compiler doesn't have __func__ variable.]) + AC_MSG_RESULT([ no])] + ) +]) + +dnl Check whether the C compiler defines __STDC__. +dnl +AC_DEFUN([AX_CHECK___STDC__], [ + AC_MSG_CHECKING([whether the C compiler (${CC}) defines __STDC__]) + AC_TRY_COMPILE([], + [#ifndef __STDC__ + test_stdc (); + #endif], + [AC_MSG_RESULT([ yes]) + AC_DEFINE_UNQUOTED([ANSI_FUNC], [1], [Define if you use an ANSI C compiler.]) + stdc_defined="yes"], + [AC_MSG_RESULT([ no])] + ) +]) + +dnl +dnl +AC_DEFUN([AX_PROG_PKGCONFIG], [ + AC_CHECK_PROG(PKG_CONFIG, [pkg-config], [yes], [no], [], []) + test "${PKG_CONFIG}" = "no" && AC_MSG_ERROR([pkg-config not found]) +]) + +dnl +dnl +AC_DEFUN([AX_PATH_PKGCONFIG], [ + PKG_CONFIG_PATH= + _pc_prefix="${ac_default_prefix}" + # ovdje se ne smije zamijeniti $n sa ${n} jer su to argumenti m4 funkcije AX_PATH_PKGCONFIG + test "$1" = "yes" -o "$1" = "check" || _pc_prefix="$1" + + for _loop_path in \ + ${_pc_prefix}/lib \ + ${_pc_prefix}/lib/i386-linux-gnu \ + ${_pc_prefix}/lib/x86_64-linux-gnu \ + ${_pc_prefix}/amd64 \ + ${_pc_prefix}/lib32 \ + ${_pc_prefix}/lib64 \ + ${_pc_prefix}/share + do + test -d "${_loop_path}/pkgconfig" && \ + PKG_CONFIG_PATH="${PKG_CONFIG_PATH}${PKG_CONFIG_PATH:+:}${_loop_path}/pkgconfig" + done + AC_MSG_NOTICE([PKG_CONFIG_PATH=${PKG_CONFIG_PATH}]) +]) + +dnl Check whether the C compiler has __attribute__ keyword. +dnl +AC_DEFUN([AX_CHECK___ATTRIBUTE__], [ + AC_MSG_CHECKING([whether the C compiler (${CC}) has __attribute__ keyword]) + AC_TRY_COMPILE([void t1 () __attribute__ ((noreturn)); void t1 () { return; };], + [t1 ();], + [AC_MSG_RESULT([ yes]) + AC_DEFINE_UNQUOTED([__ATTRIBUTE__], [1], [Define if your C compiler has __attribute__ keyword.])], + [AC_MSG_RESULT([ no])] + ) +]) + +AC_DEFUN([AX_ENABLE_DEBUG], [ + AC_ARG_ENABLE([debug], + [AS_HELP_STRING([--enable-debug], [compile with debugging symbols/functions])], + [if test "${enableval}" = "yes"; then + AC_DEFINE([DEBUG], [1], [Define to 1 if you want to include debugging options.]) + CFLAGS="${CFLAGS} -g -O0" + CXXFLAGS="${CXXFLAGS} -g -O0" + fi] + ) +]) + +AC_DEFUN([AX_ENABLE_GPROF], [ + AC_ARG_ENABLE([gprof], + [AS_HELP_STRING([--enable-gprof], [enable profiling with gprof])], + [if test "${enableval}" = "yes"; then + AC_DEFINE([GPROF], [1], [Define to 1 if you want to enable profiling with gprof.]) + CFLAGS="${CFLAGS} -pg" + CXXFLAGS="${CXXFLAGS} -pg" + LDFLAGS="${LDFLAGS} -pg" + fi] + ) +]) + +AC_DEFUN([AX_VARIABLE_SET], [ +_am_cache_test () +{ + _c= + + if test -n "${2}"; then + for _c in ${2}; do test "${_c}" = "${3}" && return 1; done + eval "${1}=\"${2} ${3}\"" + else + eval "${1}=\"${3}\"" + fi +} + + _am_var_resolved= + # ovdje se ne smije zamijeniti $n sa ${n} jer su to argumenti m4 funkcije AX_VARIABLE_SET + for _am_var_loop in $2; do + _am_cache_test _am_var_resolved "${_am_var_resolved}" "${_am_var_loop}" + done + $1=${_am_var_resolved} +]) + +AC_DEFUN([AX_SHOW_CONFIG], [ + eval "bindir=${bindir}" + eval "datadir=${datadir}" + eval "sysconfdir=${sysconfdir}" + eval "mandir=${mandir}" + + echo + echo "${PACKAGE_NAME} configuration:" + echo "--------------------------------------------------" + echo " package version : ${PACKAGE_VERSION}" + echo " library version : ${LIB_VERSION}" + echo " host operating system : ${host}" + echo " source code location : ${srcdir}" + echo " C compiler : ${CC}" + echo " C++ compiler : ${CXX}" + echo " preprocessor flags : ${CPPFLAGS}" + echo " C compiler flags : ${CFLAGS}" + echo " C++ compiler flags : ${CXXFLAGS}" + echo " linker flags : ${LDFLAGS}" + echo " libraries : ${LIBS}" + echo " configure options : ${ac_configure_args}" + echo " binary install path : ${bindir}" + echo " data install path : ${datadir}" + echo " configuration file path : ${sysconfdir}" + echo " man page install path : ${mandir}" + echo +]) diff --git a/m4/am-enable-threads.m4 b/m4/am-enable-threads.m4 new file mode 100644 index 0000000..629325f --- /dev/null +++ b/m4/am-enable-threads.m4 @@ -0,0 +1,44 @@ +dnl am-enable-threads.m4 by Miroslav Zagorac <mzagorac@haproxy.com> +dnl +AC_DEFUN([AX_ENABLE_THREADS], [ + AC_ARG_ENABLE([threads], + [AS_HELP_STRING([--enable-threads], [enable threads @<:@default=yes@:>@])], + [enable_threads="${enableval}"], + [enable_threads=yes] + ) + + if test "${enable_threads}" != "no"; then + HAVE_THREADS= + THREADS_CFLAGS= + THREADS_CPPFLAGS= + THREADS_LDFLAGS= + THREADS_LIBS= + + AX_VARIABLES_STORE + + LDFLAGS="${LDFLAGS} ${THREADS_LDFLAGS}" + CPPFLAGS="${CPPFLAGS} ${THREADS_CPPFLAGS}" + + AC_CHECK_LIB([pthread], [pthread_create], [], [AC_MSG_ERROR([THREADS library not found])]) + AC_CHECK_HEADER([pthread.h], [], [AC_MSG_ERROR([THREADS library headers not found])]) + + HAVE_THREADS=yes + THREADS_LIBS="-lpthread" + + AC_DEFINE([USE_THREADS], [1], [Define to 1 for multi-thread support.]) + AC_DEFINE([_REENTRANT], [1], [Define to 1 for multi-thread support.]) + + AX_VARIABLES_RESTORE + + AC_MSG_NOTICE([THREADS environment variables:]) + AC_MSG_NOTICE([ THREADS_CFLAGS=${THREADS_CFLAGS}]) + AC_MSG_NOTICE([ THREADS_CPPFLAGS=${THREADS_CPPFLAGS}]) + AC_MSG_NOTICE([ THREADS_LDFLAGS=${THREADS_LDFLAGS}]) + AC_MSG_NOTICE([ THREADS_LIBS=${THREADS_LIBS}]) + + AC_SUBST([THREADS_CFLAGS]) + AC_SUBST([THREADS_CPPFLAGS]) + AC_SUBST([THREADS_LDFLAGS]) + AC_SUBST([THREADS_LIBS]) + fi +]) diff --git a/m4/am-host.m4 b/m4/am-host.m4 new file mode 100644 index 0000000..07d6353 --- /dev/null +++ b/m4/am-host.m4 @@ -0,0 +1,44 @@ +dnl am-host.m4 by Miroslav Zagorac <mzagorac@haproxy.com> +dnl +AC_DEFUN([AX_HOST], [ + dnl Get current date and time. + dnl + DATE=`date` + + dnl Get cannonical host. + dnl + AC_CANONICAL_HOST + AC_DEFINE_UNQUOTED([OSTYPE], ["${host}"], [Guessed OS type.]) + + dnl Posix variants + dnl + AC_USE_SYSTEM_EXTENSIONS + + dnl because ${${host_os%-*}##*-} does not work on stupid bash... + dnl + _host_os=${host_os%-*} + _host_os=${_host_os##*-} + + case "${host_os}" in + *osf*) + test "${stdc_defined}" = "yes" || \ + AC_DEFINE([__STDC__], [1], [Define to 1 if you use ANSI C compiler.]) + AC_DEFINE([_REENTRANT], [1], [Define to 1 only for OSF Unix.]) + AC_DEFINE([ANSI_FUNC], [1], [Define to 1 if you use ANSI C compiler.]) + AC_DEFINE([OSF], [1], [Define to 1 for OSF Unix.]) + ;; + + *solaris*) + AC_DEFINE([ANSI_FUNC], [1], [Define to 1 if you use ANSI C compiler.]) + AC_DEFINE([SOLARIS], [1], [Define to 1 for Solaris UNIX system.]) + ;; + + *sunos*) + AC_DEFINE([SUNOS], [1], [Define to 1 for SUNOS UNIX system.]) + ;; + + *linux*) + AC_DEFINE([LINUX], [1], [Define to 1 for Linux system.]) + ;; + esac +]) diff --git a/m4/am-with-opentracing.m4 b/m4/am-with-opentracing.m4 new file mode 100644 index 0000000..3bdb5ee --- /dev/null +++ b/m4/am-with-opentracing.m4 @@ -0,0 +1,71 @@ +dnl am-with-opentracing.m4 by Miroslav Zagorac <mzagorac@haproxy.com> +dnl +AC_DEFUN([AX_WITH_OPENTRACING], [ + AC_ARG_WITH([opentracing], + [AS_HELP_STRING([--with-opentracing@<:@=DIR@:>@], [use OPENTRACING library @<:@default=yes@:>@])], + [with_opentracing="${withval}"], + [with_opentracing=yes] + ) + + AX_CHECK_NOEXCEPT([]) + + if test "${with_opentracing}" != "no"; then + HAVE_OPENTRACING= + OPENTRACING_CFLAGS= + OPENTRACING_CXXFLAGS= + OPENTRACING_CPPFLAGS= + OPENTRACING_LDFLAGS= + OPENTRACING_LIBS="-lopentracing" + + if test -n "${with_opentracing}" -a "${with_opentracing}" != "yes" -a "${with_opentracing}" != "yes"; then + OPENTRACING_CPPFLAGS="-I${with_opentracing}/include" + + if test "${with_opentracing}" != "/usr"; then + if test "`uname`" = "Linux"; then + OPENTRACING_LDFLAGS="-L${with_opentracing}/lib -Wl,--rpath,${with_opentracing}/lib" + elif test "`uname`" = "FreeBSD"; then + OPENTRACING_LDFLAGS="-L${with_opentracing}/lib -Wl,-rpath -Wl,${with_opentracing}/lib" + else + OPENTRACING_LDFLAGS="-L${with_opentracing}/lib -R${with_opentracing}/lib" + fi + fi + fi + + AX_VARIABLES_STORE + + LIBS="${LDFLAGS} ${OPENTRACING_LIBS}" + LDFLAGS="${LDFLAGS} ${OPENTRACING_LDFLAGS}" + CFLAGS="${CFLAGS} ${OPENTRACING_CFLAGS}" + CXXFLAGS="${CXXFLAGS} ${OPENTRACING_CXXFLAGS}" + CPPFLAGS="${CPPFLAGS} ${OPENTRACING_CPPFLAGS}" + + AC_LANG_PUSH([C++]) + AC_LINK_IFELSE([ + AC_LANG_PROGRAM( + [[#include <opentracing/dynamic_load.h>]], + [[std::string e;] [auto a = opentracing::DynamicallyLoadTracingLibrary("", e);]] + )], + [], + [AC_MSG_ERROR([OPENTRACING library not found])] + ) + AC_CHECK_HEADER([opentracing/version.h], [], [AC_MSG_ERROR([OPENTRACING library headers not found])]) + AC_LANG_POP([C++]) + + HAVE_OPENTRACING=yes + + AX_VARIABLES_RESTORE + + AC_MSG_NOTICE([OPENTRACING environment variables:]) + AC_MSG_NOTICE([ OPENTRACING_CFLAGS=${OPENTRACING_CFLAGS}]) + AC_MSG_NOTICE([ OPENTRACING_CXXFLAGS=${OPENTRACING_CXXFLAGS}]) + AC_MSG_NOTICE([ OPENTRACING_CPPFLAGS=${OPENTRACING_CPPFLAGS}]) + AC_MSG_NOTICE([ OPENTRACING_LDFLAGS=${OPENTRACING_LDFLAGS}]) + AC_MSG_NOTICE([ OPENTRACING_LIBS=${OPENTRACING_LIBS}]) + + AC_SUBST([OPENTRACING_CFLAGS]) + AC_SUBST([OPENTRACING_CXXFLAGS]) + AC_SUBST([OPENTRACING_CPPFLAGS]) + AC_SUBST([OPENTRACING_LDFLAGS]) + AC_SUBST([OPENTRACING_LIBS]) + fi +]) diff --git a/opentracing-c-wrapper.pc.in b/opentracing-c-wrapper.pc.in new file mode 100644 index 0000000..f1a7f43 --- /dev/null +++ b/opentracing-c-wrapper.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: opentracing-c-wrapper +Description: The OpenTracing C wrapper library. +Version: @VERSION@ + +Requires: +Libs: -L${libdir} -Wl,--rpath,${libdir} -lopentracing-c-wrapper +Cflags: -I${includedir} diff --git a/opentracing-c-wrapper_dbg.pc.in b/opentracing-c-wrapper_dbg.pc.in new file mode 100644 index 0000000..5d0e4b9 --- /dev/null +++ b/opentracing-c-wrapper_dbg.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: opentracing-c-wrapper_dbg +Description: The OpenTracing C wrapper library with debugging support enabled. +Version: @VERSION@ + +Requires: +Libs: -L${libdir} -Wl,--rpath,${libdir} -lopentracing-c-wrapper_dbg +Cflags: -I${includedir} -DOTC_DBG_MEM diff --git a/scripts/bootstrap b/scripts/bootstrap new file mode 100755 index 0000000..27c2cf2 --- /dev/null +++ b/scripts/bootstrap @@ -0,0 +1,12 @@ +#!/bin/sh -x +# +# bootstrap by Miroslav Zagorac <mzagorac@haproxy.com> +# +mkdir -p config +libtoolize +aclocal -I m4 +autoheader +automake --add-missing --copy +autoconf + +rm -rf autom4te.cache diff --git a/scripts/distclean b/scripts/distclean new file mode 100755 index 0000000..c75fd40 --- /dev/null +++ b/scripts/distclean @@ -0,0 +1,9 @@ +#!/bin/sh +# +# distclean Miroslav Zagorac <mzagorac@haproxy.com> +# +test -f Makefile && make maintainer-clean + +rm -rf autom4te.cache config package _* +rm -f autoscan.log configure.scan aclocal.m4 config.log config.status configure +rm -f Makefile.in */Makefile.in m4/l* src/.source-md5sums diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..bd72329 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,52 @@ +## Process this file with automake to produce Makefile.in +## +## +AM_CPPFLAGS = @OPENTRACING_C_WRAPPER_CPPFLAGS@ + AM_CFLAGS = @OPENTRACING_C_WRAPPER_CFLAGS@ +AM_CXXFLAGS = @OPENTRACING_C_WRAPPER_CXXFLAGS@ + AM_LDFLAGS = @OPENTRACING_C_WRAPPER_LDFLAGS@ + LIBS = @LIBS@ @OPENTRACING_C_WRAPPER_LIBS@ + +if WANT_DEBUG +lib_LTLIBRARIES = libopentracing-c-wrapper_dbg.la +pkgconfig_DATA = ../opentracing-c-wrapper_dbg.pc + +libopentracing_c_wrapper_dbg_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/../include +libopentracing_c_wrapper_dbg_la_CFLAGS = $(AM_CFLAGS) +libopentracing_c_wrapper_dbg_la_CXXFLAGS = $(AM_CXXFLAGS) +libopentracing_c_wrapper_dbg_la_LDFLAGS = $(AM_LDFLAGS) -version-info @LIB_VERSION@ -Wl,--version-script=$(srcdir)/export_dbg.map +libopentracing_c_wrapper_dbg_la_SOURCES = \ + dbg_malloc.cpp \ + span.cpp \ + tracer.cpp \ + util.cpp + +else + +lib_LTLIBRARIES = libopentracing-c-wrapper.la +pkgconfig_DATA = ../opentracing-c-wrapper.pc + +libopentracing_c_wrapper_la_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/../include +libopentracing_c_wrapper_la_CFLAGS = $(AM_CFLAGS) +libopentracing_c_wrapper_la_CXXFLAGS = $(AM_CXXFLAGS) +libopentracing_c_wrapper_la_LDFLAGS = $(AM_LDFLAGS) -version-info @LIB_VERSION@ -Wl,--version-script=$(srcdir)/export.map +libopentracing_c_wrapper_la_SOURCES = \ + span.cpp \ + tracer.cpp \ + util.cpp +endif + +pkginclude_HEADERS = \ + ../include/opentracing-c-wrapper/common.h \ + ../include/opentracing-c-wrapper/dbg_malloc.h \ + ../include/opentracing-c-wrapper/define.h \ + ../include/opentracing-c-wrapper/include.h \ + ../include/opentracing-c-wrapper/propagation.h \ + ../include/opentracing-c-wrapper/span.h \ + ../include/opentracing-c-wrapper/tracer.h \ + ../include/opentracing-c-wrapper/util.h \ + ../include/opentracing-c-wrapper/value.h + +CLEANFILES = a.out +## +## Makefile.am ends here diff --git a/src/dbg_malloc.cpp b/src/dbg_malloc.cpp new file mode 100644 index 0000000..18214b1 --- /dev/null +++ b/src/dbg_malloc.cpp @@ -0,0 +1,629 @@ +/*** + * Copyright 2020 HAProxy Technologies + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "include.h" + + +static struct otc_dbg_mem *dbg_mem = nullptr; + + +/*** + * NAME + * otc_dbg_set_metadata - + * + * ARGUMENTS + * ptr - the real address of the allocated data + * data - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void otc_dbg_set_metadata(void *ptr, struct otc_dbg_mem_data *data) +{ + struct otc_dbg_mem_metadata *metadata; + + if (ptr == nullptr) + return; + + metadata = OT_CAST_TYPEOF(metadata, ptr); + metadata->data = (data == nullptr) ? OT_CAST_TYPEOF(data, metadata) : data; + metadata->magic = DBG_MEM_MAGIC; +} + + +/*** + * NAME + * otc_dbg_mem_add - + * + * ARGUMENTS + * func - + * line - + * ptr - the real address of the allocated data + * size - + * data - + * op_idx - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void otc_dbg_mem_add(const char *func, int line, void *ptr, size_t size, struct otc_dbg_mem_data *data, int op_idx) +{ + (void)snprintf(data->func, sizeof(data->func), "%s:%d", func, line); + + data->ptr = ptr; + data->size = size; + data->used = 1; + + dbg_mem->size += size; + dbg_mem->op_cnt[op_idx]++; + + otc_dbg_set_metadata(ptr, data); +} + + +/*** + * NAME + * otc_dbg_mem_alloc - + * + * ARGUMENTS + * func - + * line - + * old_ptr - the address of the data returned to the program + * ptr - the real address of the allocated data + * size - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void otc_dbg_mem_alloc(const char *func, int line, void *old_ptr, void *ptr, size_t size) +{ + size_t i = 0; + int rc; + + if (dbg_mem == nullptr) { + return; + } + else if (ptr == nullptr) { + DBG_MEM_ERR("invalid memory address: %p", ptr); + + return; + } + + if ((rc = pthread_mutex_lock(&(dbg_mem->mutex))) != 0) { + DBG_MEM_ERR("cannot lock mutex: %s", otc_strerror(rc)); + + return; + } + + if (old_ptr != nullptr) { + /* Reallocating memory. */ + struct otc_dbg_mem_metadata *metadata = OT_CAST_TYPEOF(metadata, ptr); + + if (metadata == nullptr) { + DBG_MEM_ERR("no metadata: MEM_REALLOC %s:%d(%p -> %p %zu)", func, line, old_ptr, DBG_MEM_PTR(ptr), size); + } + else if (metadata->data == nullptr) { + DBG_MEM_ERR("invalid metadata: MEM_REALLOC %s:%d(%p -> %p %zu)", func, line, old_ptr, DBG_MEM_PTR(ptr), size); + } + else if (metadata->data == OT_CAST_TYPEOF(metadata->data, metadata)) { + DBG_MEM_ERR("unset metadata: MEM_REALLOC %s:%d(%p -> %p %zu)", func, line, old_ptr, DBG_MEM_PTR(ptr), size); + } + else if (metadata->magic != DBG_MEM_MAGIC) { + DBG_MEM_ERR("invalid magic: MEM_REALLOC %s:%d(%p -> %p %zu) 0x%016" PRIu64, func, line, old_ptr, DBG_MEM_PTR(ptr), size, metadata->magic); + } + else if (metadata->data->used && (metadata->data->ptr == DBG_MEM_DATA(old_ptr))) { + DBG_MEM_INFO(1, "MEM_REALLOC: %s:%d(%p %zu -> %p %zu)", func, line, old_ptr, metadata->data->size, DBG_MEM_PTR(ptr), size); + + dbg_mem->size -= metadata->data->size; + otc_dbg_mem_add(func, line, ptr, size, metadata->data, 1); + } + } else { + otc_dbg_set_metadata(ptr, nullptr); + + /* + * The first attempt is to find a location that has not been + * used at all so far. If such is not found, an attempt is + * made to find the first available location. + */ + if (dbg_mem->unused < dbg_mem->count) { + i = dbg_mem->unused++; + } else { + do { + if (dbg_mem->reused >= dbg_mem->count) + dbg_mem->reused = 0; + + if (!dbg_mem->data[dbg_mem->reused].used) { + i = dbg_mem->reused++; + + break; + } + + dbg_mem->reused++; + } while (++i <= dbg_mem->count); + } + + if (i < dbg_mem->count) { + DBG_MEM_INFO(1, "MEM_ALLOC: %s:%d(%p %zu %zu)", func, line, DBG_MEM_PTR(ptr), size, i); + + otc_dbg_mem_add(func, line, ptr, size, dbg_mem->data + i, 0); + } + } + + if ((rc = pthread_mutex_unlock(&(dbg_mem->mutex))) != 0) { + DBG_MEM_ERR("cannot unlock mutex: %s", otc_strerror(rc)); + + return; + } + + if (i >= dbg_mem->count) + DBG_MEM_ERR("alloc overflow: %s:%d(%p -> %p %zu)", func, line, old_ptr, DBG_MEM_PTR(ptr), size); +} + + +/*** + * NAME + * otc_dbg_mem_release - + * + * ARGUMENTS + * func - + * line - + * ptr - the address of the data returned to the program + * op_idx - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void otc_dbg_mem_release(const char *func, int line, void *ptr, int op_idx) +{ + struct otc_dbg_mem_metadata *metadata; + bool flag_invalid = 0; + size_t i; + int rc; + + if (dbg_mem == nullptr) { + return; + } + else if (ptr == nullptr) { + DBG_MEM_ERR("invalid memory address: %p", ptr); + + return; + } + + if ((rc = pthread_mutex_lock(&(dbg_mem->mutex))) != 0) { + DBG_MEM_ERR("cannot lock mutex: %s", otc_strerror(rc)); + + return; + } + + metadata = DBG_MEM_DATA(ptr); + if (metadata == nullptr) { + DBG_MEM_ERR("no metadata: MEM_%s %s:%d(%p)", (op_idx == 2) ? "FREE" : "RELEASE", func, line, ptr); + } + else if (metadata->data == nullptr) { + DBG_MEM_ERR("invalid metadata: MEM_%s %s:%d(%p)", (op_idx == 2) ? "FREE" : "RELEASE", func, line, ptr); + } + else if (metadata->data == OT_CAST_TYPEOF(metadata->data, metadata)) { + DBG_MEM_ERR("unset metadata: MEM_%s %s:%d(%p)", (op_idx == 2) ? "FREE" : "RELEASE", func, line, ptr); + } + else if (metadata->magic != DBG_MEM_MAGIC) { + DBG_MEM_ERR("invalid magic: MEM_%s %s:%d(%p) 0x%016" PRIu64, (op_idx == 2) ? "FREE" : "RELEASE", func, line, ptr, metadata->magic); + } + else if (metadata->data->used && (metadata->data->ptr == metadata)) { + DBG_MEM_INFO(1, "MEM_%s: %s:%d(%p %zu)", (op_idx == 2) ? "FREE" : "RELEASE", func, line, ptr, metadata->data->size); + + metadata->data->used = 0; + + dbg_mem->size -= metadata->data->size; + dbg_mem->op_cnt[op_idx]++; + } + else { + flag_invalid = 1; + } + + if ((rc = pthread_mutex_unlock(&(dbg_mem->mutex))) != 0) { + DBG_MEM_ERR("cannot unlock mutex: %s", otc_strerror(rc)); + + return; + } + + if (flag_invalid) { + DBG_MEM_ERR("invalid ptr: %s:%d(%p)", func, line, ptr); + + if (metadata != nullptr) + for (i = 0; i < dbg_mem->count; i++) + if (dbg_mem->data[i].ptr == metadata) + DBG_MEM_ERR("possible previous use: %s %hhu", dbg_mem->data[i].func, dbg_mem->data[i].used); + } +} + + +/*** + * NAME + * otc_dbg_malloc - + * + * ARGUMENTS + * func - + * line - + * size - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +void *otc_dbg_malloc(const char *func, int line, size_t size) +{ + void *retptr; + + retptr = malloc(DBG_MEM_SIZE(size)); + + otc_dbg_mem_alloc(func, line, nullptr, retptr, size); + + return DBG_MEM_RETURN(retptr); +} + + +/*** + * NAME + * otc_dbg_calloc - + * + * ARGUMENTS + * func - + * line - + * nelem - + * elsize - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +void *otc_dbg_calloc(const char *func, int line, size_t nelem, size_t elsize) +{ + void *retptr; + + retptr = malloc(DBG_MEM_SIZE(nelem * elsize)); + if (retptr != nullptr) + (void)memset(retptr, 0, DBG_MEM_SIZE(nelem * elsize)); + + otc_dbg_mem_alloc(func, line, nullptr, retptr, nelem * elsize); + + return DBG_MEM_RETURN(retptr); +} + + +/*** + * NAME + * otc_dbg_realloc - + * + * ARGUMENTS + * func - + * line - + * ptr - + * size - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +void *otc_dbg_realloc(const char *func, int line, void *ptr, size_t size) +{ + void *retptr; + + if (ptr == nullptr) { + retptr = malloc(DBG_MEM_SIZE(size)); + + otc_dbg_mem_alloc(func, line, nullptr, retptr, size); + } else { + struct otc_dbg_mem_metadata *metadata = DBG_MEM_DATA(ptr); + + /* + * If memory is not allocated via these debug functions, it + * must not be reallocated via them either. + */ + if ((metadata == nullptr) || (metadata->data == nullptr) || (metadata->magic != DBG_MEM_MAGIC)) { + retptr = realloc(ptr, size); + + return retptr; + } else { + retptr = realloc(DBG_MEM_DATA(ptr), DBG_MEM_SIZE(size)); + + otc_dbg_mem_alloc(func, line, ptr, retptr, size); + } + } + + return DBG_MEM_RETURN(retptr); +} + + +/*** + * NAME + * otc_dbg_free - + * + * ARGUMENTS + * func - + * line - + * ptr - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +void otc_dbg_free(const char *func, int line, void *ptr) +{ + struct otc_dbg_mem_metadata *metadata; + + otc_dbg_mem_release(func, line, ptr, 2); + + metadata = DBG_MEM_DATA(ptr); + if ((metadata == nullptr) || (metadata->data == nullptr) || (metadata->magic != DBG_MEM_MAGIC)) + free(ptr); + else + free(DBG_MEM_DATA(ptr)); +} + + +/*** + * NAME + * otc_dbg_strdup - + * + * ARGUMENTS + * func - + * line - + * s - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +char *otc_dbg_strdup(const char *func, int line, const char *s) +{ + size_t len = 0; + char *retptr = nullptr; + + if (s != nullptr) { + len = strlen(s) + 1; + retptr = OT_CAST_TYPEOF(retptr, malloc(DBG_MEM_SIZE(len))); + if (retptr != nullptr) + (void)memcpy(DBG_MEM_PTR(retptr), s, len); + } + + otc_dbg_mem_alloc(func, line, nullptr, retptr, len); + + return OT_CAST_TYPEOF(retptr, DBG_MEM_RETURN(retptr)); +} + + +/*** + * NAME + * otc_dbg_strndup - + * + * ARGUMENTS + * func - + * line - + * s - + * size - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +char *otc_dbg_strndup(const char *func, int line, const char *s, size_t size) +{ + size_t len = 0; + char *retptr = nullptr; + + if (s != nullptr) { + len = strlen(s); + len = (len < size) ? len : size; + retptr = OT_CAST_TYPEOF(retptr, malloc(DBG_MEM_SIZE(len + 1))); + if (retptr != nullptr) { + (void)memcpy(DBG_MEM_PTR(retptr), s, len); + DBG_MEM_PTR(retptr)[len] = '\0'; + } + } + + otc_dbg_mem_alloc(func, line, nullptr, retptr, len + 1); + + return OT_CAST_TYPEOF(retptr, DBG_MEM_RETURN(retptr)); +} + + +/*** + * NAME + * otc_dbg_mem_init - + * + * ARGUMENTS + * mem - + * data - + * count - + * level - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +int otc_dbg_mem_init(struct otc_dbg_mem *mem, struct otc_dbg_mem_data *data, size_t count, uint8_t level) +{ + pthread_mutexattr_t attr; + int rc, retval = -1; + + if ((mem == nullptr) || (data == nullptr) || (count == 0)) + return retval; + + (void)memset(mem, 0, sizeof(*mem)); + (void)memset(data, 0, sizeof(*data) * count); + + dbg_mem = mem; + dbg_mem->data = data; + dbg_mem->count = count; + dbg_mem->level = level; + + if ((rc = pthread_mutexattr_init(&attr)) == 0) + if ((rc = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) == 0) + if ((rc = pthread_mutex_init(&(dbg_mem->mutex), &attr)) == 0) + retval = 0; + + return retval; +} + + +/*** + * NAME + * otc_dbg_mem_disable - + * + * ARGUMENTS + * This function takes no arguments. + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +void otc_dbg_mem_disable(void) +{ + if (dbg_mem == nullptr) + return; + + (void)pthread_mutex_destroy(&(dbg_mem->mutex)); + + dbg_mem = nullptr; +} + + +/*** + * NAME + * otc_dbg_mem_info - + * + * ARGUMENTS + * This function takes no arguments. + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +void otc_dbg_mem_info(void) +{ +#ifdef HAVE_MALLINFO + struct mallinfo mi; +#endif + size_t i, n = 0; + uint64_t size = 0; + + if (dbg_mem == nullptr) + return; + + DBG_MEM_INFO(0, "--- Memory info -------------------------------------"); + DBG_MEM_INFO(0, " alloc/realloc: %" PRIu64 "/%" PRIu64 ", free/release: %" PRIu64 "/%" PRIu64, dbg_mem->op_cnt[0], dbg_mem->op_cnt[1], dbg_mem->op_cnt[2], dbg_mem->op_cnt[3]); + DBG_MEM_INFO(0, " unused: %zu, reused: %zu, count: %zu", dbg_mem->unused, dbg_mem->reused, dbg_mem->count); + for (i = 0; i < dbg_mem->count; i++) + if (dbg_mem->data[i].used) { + DBG_MEM_INFO(0, " %zu %s(%p %zu)", n, dbg_mem->data[i].func, dbg_mem->data[i].ptr, dbg_mem->data[i].size); + + size += dbg_mem->data[i].size; + n++; + } + + if (n > 0) + DBG_MEM_INFO(0, " allocated %zu byte(s) in %zu chunk(s)", size, n); + + if (dbg_mem->size != size) + DBG_MEM_INFO(0, " size does not match: %zu != %zu", dbg_mem->size, size); + +#ifdef HAVE_MALLINFO + mi = mallinfo(); + DBG_MEM_INFO(0, "--- Memory space usage ------------------------------"); + DBG_MEM_INFO(0, " Total non-mmapped bytes: %" PRI_MI, mi.arena); + DBG_MEM_INFO(0, " # of free chunks: %" PRI_MI, mi.ordblks); + DBG_MEM_INFO(0, " # of free fastbin blocks: %" PRI_MI, mi.smblks); + DBG_MEM_INFO(0, " Bytes in mapped regions: %" PRI_MI, mi.hblkhd); + DBG_MEM_INFO(0, " # of mapped regions: %" PRI_MI, mi.hblks); + DBG_MEM_INFO(0, " Max. total allocated space: %" PRI_MI, mi.usmblks); + DBG_MEM_INFO(0, " Free bytes held in fastbins: %" PRI_MI, mi.fsmblks); + DBG_MEM_INFO(0, " Total allocated space: %" PRI_MI, mi.uordblks); + DBG_MEM_INFO(0, " Total free space: %" PRI_MI, mi.fordblks); + DBG_MEM_INFO(0, " Topmost releasable block: %" PRI_MI, mi.keepcost); +#endif +} + + +/*** + * NAME + * otc_dbg_memdup - + * + * ARGUMENTS + * func - + * line - + * s - + * size - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +void *otc_dbg_memdup(const char *func, int line, const void *s, size_t size) +{ + void *retptr = nullptr; + + if (s != nullptr) { + retptr = malloc(DBG_MEM_SIZE(size + 1)); + if (retptr != nullptr) { + (void)memcpy(DBG_MEM_PTR(retptr), s, size); + DBG_MEM_PTR(retptr)[size] = '\0'; + } + } + + otc_dbg_mem_alloc(func, line, nullptr, retptr, size); + + return DBG_MEM_RETURN(retptr); +} + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/src/export.map b/src/export.map new file mode 100644 index 0000000..ab7c3a4 --- /dev/null +++ b/src/export.map @@ -0,0 +1,18 @@ +{ +global: + otc_tracer_init; + otc_tracer_load; + otc_tracer_start; + otc_tracer_global; + otc_tracer_init_global; + otc_text_map_new; + otc_text_map_add; + otc_text_map_destroy; + otc_binary_data_new; + otc_binary_data_destroy; + otc_ext_init; + otc_file_read; + otc_statistics; + +local: *; +}; diff --git a/src/export_dbg.map b/src/export_dbg.map new file mode 100644 index 0000000..4a99a3f --- /dev/null +++ b/src/export_dbg.map @@ -0,0 +1,29 @@ +{ +global: + otc_tracer_init; + otc_tracer_load; + otc_tracer_start; + otc_tracer_global; + otc_tracer_init_global; + otc_text_map_new; + otc_text_map_add; + otc_text_map_destroy; + otc_binary_data_new; + otc_binary_data_destroy; + otc_ext_init; + otc_file_read; + otc_statistics; + + otc_dbg_calloc; + otc_dbg_free; + otc_dbg_malloc; + otc_dbg_mem_disable; + otc_dbg_mem_info; + otc_dbg_mem_init; + otc_dbg_memdup; + otc_dbg_realloc; + otc_dbg_strdup; + otc_dbg_strndup; + +local: *; +}; diff --git a/src/span.cpp b/src/span.cpp new file mode 100644 index 0000000..3e9323d --- /dev/null +++ b/src/span.cpp @@ -0,0 +1,547 @@ +/*** + * Copyright 2020 HAProxy Technologies + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "include.h" + + +#ifdef OT_THREADS_NO_LOCKING +thread_local Handle<opentracing::Span> ot_span_handle; +thread_local Handle<opentracing::SpanContext> ot_span_context_handle; +struct HandleData ot_span; +struct HandleData ot_span_context; +#else +struct Handle<opentracing::Span> ot_span; +struct Handle<opentracing::SpanContext> ot_span_context; +#endif /* OT_THREADS_NO_LOCKING */ + + +/*** + * NAME + * ot_span_finish_with_options - + * + * ARGUMENTS + * span - + * options - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void ot_span_finish_with_options(struct otc_span *span, const struct otc_finish_span_options *options) +{ + OT_LOCK_GUARD(span); + + if (!OT_SPAN_IS_VALID(span)) + return; + + if (options == nullptr) { + ot_span_handle.at(span->idx)->Finish(); + } else { + struct opentracing::FinishSpanOptions span_options; + + if (options->finish_time.value.tv_sec > 0) { + auto dt = timespec_to_duration(&(options->finish_time.value)); + + span_options.finish_steady_timestamp = std::chrono::time_point<std::chrono::steady_clock>(dt); + } + + if (options->log_records != nullptr) { + for (int i = 0; i < options->num_log_records; i++) { + struct opentracing::LogRecord record; + + if (options->log_records[i].timestamp.value.tv_sec > 0) { +#ifdef __clang__ + auto dt = timespec_to_duration_us(&(options->log_records[i].timestamp.value)); +#else + auto dt = timespec_to_duration(&(options->log_records[i].timestamp.value)); +#endif + + record.timestamp = std::chrono::time_point<std::chrono::system_clock>(dt); + } + + for (int j = 0; j < options->log_records[i].num_fields; j++) + if (options->log_records[i].fields[j].value.type == otc_value_bool) { + record.fields.push_back(std::make_pair(options->log_records[i].fields[j].key, options->log_records[i].fields[j].value.value.bool_value)); + } + else if (options->log_records[i].fields[j].value.type == otc_value_double) { + record.fields.push_back(std::make_pair(options->log_records[i].fields[j].key, options->log_records[i].fields[j].value.value.double_value)); + } + else if (options->log_records[i].fields[j].value.type == otc_value_int64) { + record.fields.push_back(std::make_pair(options->log_records[i].fields[j].key, options->log_records[i].fields[j].value.value.int64_value)); + } + else if (options->log_records[i].fields[j].value.type == otc_value_uint64) { + record.fields.push_back(std::make_pair(options->log_records[i].fields[j].key, options->log_records[i].fields[j].value.value.uint64_value)); + } + else if (options->log_records[i].fields[j].value.type == otc_value_string) { + std::string str_value = options->log_records[i].fields[j].value.value.string_value; + + record.fields.push_back(std::make_pair(options->log_records[i].fields[j].key, str_value)); + } + else if (options->log_records[i].fields[j].value.type == otc_value_null) { + record.fields.push_back(std::make_pair(options->log_records[i].fields[j].key, nullptr)); + } + + span_options.log_records.push_back(record); + } + } + + ot_span_handle.at(span->idx)->FinishWithOptions(span_options); + } + + ot_nolock_span_destroy(&span); +} + + +/*** + * NAME + * ot_span_finish - + * + * ARGUMENTS + * span - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void ot_span_finish(struct otc_span *span) +{ + ot_span_finish_with_options(span, nullptr); +} + + +/*** + * NAME + * ot_span_get_context - + * + * ARGUMENTS + * span - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static struct otc_span_context *ot_span_get_context(struct otc_span *span) +{ + OT_LOCK(span, span_context); + + if (!OT_SPAN_IS_VALID(span)) + return nullptr; + + return ot_span_context_new(span); +} + + +/*** + * NAME + * ot_span_set_operation_name - + * + * ARGUMENTS + * span - + * operation_name - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void ot_span_set_operation_name(struct otc_span *span, const char *operation_name) +{ + OT_LOCK_GUARD(span); + + if (!OT_SPAN_IS_VALID(span) || (operation_name == nullptr)) + return; + + ot_span_handle.at(span->idx)->SetOperationName(operation_name); +} + + +/*** + * NAME + * ot_span_set_tag - + * + * ARGUMENTS + * span - + * key - + * value - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void ot_span_set_tag(struct otc_span *span, const char *key, const struct otc_value *value) +{ + OT_LOCK_GUARD(span); + + if (!OT_SPAN_IS_VALID(span) || (key == nullptr) || (value == nullptr)) + return; + + if (value->type == otc_value_bool) { + ot_span_handle.at(span->idx)->SetTag(key, value->value.bool_value); + } + else if (value->type == otc_value_double) { + ot_span_handle.at(span->idx)->SetTag(key, value->value.double_value); + } + else if (value->type == otc_value_int64) { + ot_span_handle.at(span->idx)->SetTag(key, value->value.int64_value); + } + else if (value->type == otc_value_uint64) { + ot_span_handle.at(span->idx)->SetTag(key, value->value.uint64_value); + } + else if (value->type == otc_value_string) { + std::string str_value = value->value.string_value; + + ot_span_handle.at(span->idx)->SetTag(key, str_value); + } + else if (value->type == otc_value_null) { + ot_span_handle.at(span->idx)->SetTag(key, nullptr); + } + else { + /* Do nothing. */ + } +} + + +/*** + * NAME + * ot_span_log_fields - + * + * ARGUMENTS + * span - + * fields - + * num_fields - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void ot_span_log_fields(struct otc_span *span, const struct otc_log_field *fields, int num_fields) +{ + OT_LOCK_GUARD(span); + std::string str_value[OTC_MAXLOGFIELDS]; + + if (!OT_SPAN_IS_VALID(span) || (fields == nullptr) || !OT_IN_RANGE(num_fields, 1, OTC_MAXLOGFIELDS)) + return; + + /* XXX The only data type supported in this function is string. */ + for (int i = 0; (i < num_fields) && (i < OTC_MAXLOGFIELDS); i++) { + if (fields[i].value.type != otc_value_string) + str_value[i] = "invalid data type"; + else if (fields[i].value.value.string_value != nullptr) + str_value[i] = fields[i].value.value.string_value; + else + str_value[i] = ""; + } + + if (num_fields == 1) + ot_span_handle.at(span->idx)->Log({ OT_LF(0) }); + else if (num_fields == 2) + ot_span_handle.at(span->idx)->Log({ OT_LF(0), OT_LF(1) }); + else if (num_fields == 3) + ot_span_handle.at(span->idx)->Log({ OT_LF(0), OT_LF(1), OT_LF(2) }); + else if (num_fields == 4) + ot_span_handle.at(span->idx)->Log({ OT_LF(0), OT_LF(1), OT_LF(2), OT_LF(3) }); + else if (num_fields == 5) + ot_span_handle.at(span->idx)->Log({ OT_LF(0), OT_LF(1), OT_LF(2), OT_LF(3), OT_LF(4) }); + else if (num_fields == 6) + ot_span_handle.at(span->idx)->Log({ OT_LF(0), OT_LF(1), OT_LF(2), OT_LF(3), OT_LF(4), OT_LF(5) }); + else if (num_fields == 7) + ot_span_handle.at(span->idx)->Log({ OT_LF(0), OT_LF(1), OT_LF(2), OT_LF(3), OT_LF(4), OT_LF(5), OT_LF(6) }); + else + ot_span_handle.at(span->idx)->Log({ OT_LF(0), OT_LF(1), OT_LF(2), OT_LF(3), OT_LF(4), OT_LF(5), OT_LF(6), OT_LF(7) }); +} + + +/*** + * NAME + * ot_span_set_baggage_item - + * + * ARGUMENTS + * span - + * key - + * value - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void ot_span_set_baggage_item(struct otc_span *span, const char *key, const char *value) +{ + OT_LOCK_GUARD(span); + + if (!OT_SPAN_IS_VALID(span) || (key == nullptr) || (value == nullptr)) + return; + + ot_span_handle.at(span->idx)->SetBaggageItem(key, value); +} + + +/*** + * NAME + * ot_span_baggage_item - + * + * ARGUMENTS + * span - + * key - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static const char *ot_span_baggage_item(const struct otc_span *span, const char *key) +{ + OT_LOCK_GUARD(span); + const char *retptr = ""; + + if (!OT_SPAN_IS_VALID(span) || (key == nullptr)) + return retptr; + + auto baggage = ot_span_handle.at(span->idx)->BaggageItem(key); + if (!baggage.empty()) + retptr = OTC_DBG_STRDUP(baggage.c_str()); + + return retptr; +} + + +/*** + * NAME + * ot_span_tracer - + * + * ARGUMENTS + * span - NOT USED + * + * DESCRIPTION + * - NOT IMPLEMENTED + * + * RETURN VALUE + * - + */ +static struct otc_tracer *ot_span_tracer(const struct otc_span *span) +{ + struct otc_tracer *retptr = nullptr; + + if (!OT_SPAN_IS_VALID(span)) + return retptr; + + return retptr; +} + + +/*** + * NAME + * ot_nolock_span_destroy - + * + * ARGUMENTS + * span - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +void ot_nolock_span_destroy(struct otc_span **span) +{ + if ((span == nullptr) || ((*span) == nullptr)) + return; + + if (OT_SPAN_KEY_IS_VALID(*span)) { + ot_span_handle.erase((*span)->idx); + ot_span.erase_cnt++; + } + + ot_span.destroy_cnt++; + + OT_EXT_FREE_CLEAR(*span); +} + + +/*** + * NAME + * ot_span_destroy - + * + * ARGUMENTS + * span - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void ot_span_destroy(struct otc_span **span) +{ + OT_LOCK_GUARD(span); + + ot_nolock_span_destroy(span); +} + + +/*** + * NAME + * ot_span_new - + * + * ARGUMENTS + * This function takes no arguments. + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +struct otc_span *ot_span_new(void) +{ + const static struct otc_span span_init = { + .idx = 0, + .finish = ot_span_finish, /* lock span */ + .finish_with_options = ot_span_finish_with_options, /* lock span */ + .span_context = ot_span_get_context, /* lock span and span_context */ + .set_operation_name = ot_span_set_operation_name, /* lock span */ + .set_tag = ot_span_set_tag, /* lock span */ + .log_fields = ot_span_log_fields, /* lock span */ + .set_baggage_item = ot_span_set_baggage_item, /* lock span */ + .baggage_item = ot_span_baggage_item, /* lock span */ + .tracer = ot_span_tracer, /* NOT IMPLEMENTED */ + .destroy = ot_span_destroy /* lock span */ + }; + int64_t idx = ot_span.key++; + struct otc_span *retptr; + + if (idx == 0) { + ot_span_handle.clear(); + ot_span_handle.reserve(8192); + } + + if ((retptr = OT_CAST_TYPEOF(retptr, OT_EXT_MALLOC(sizeof(*retptr)))) != nullptr) { + (void)memcpy(retptr, &span_init, sizeof(*retptr)); + retptr->idx = idx; + } else { + ot_span.alloc_fail_cnt++; + } + + return retptr; +} + + +/*** + * NAME + * ot_nolock_span_context_destroy - + * + * ARGUMENTS + * context - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void ot_nolock_span_context_destroy(struct otc_span_context **context) +{ + if ((context == nullptr) || (*context == nullptr)) + return; + + if (OT_CTX_KEY_IS_VALID(*context)) { + ot_span_context_handle.erase((*context)->idx); + ot_span_context.erase_cnt++; + } + + ot_span_context.destroy_cnt++; + + OT_EXT_FREE_CLEAR(*context); +} + + +/*** + * NAME + * ot_span_context_destroy - + * + * ARGUMENTS + * context - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void ot_span_context_destroy(struct otc_span_context **context) +{ + OT_LOCK_GUARD(span_context); + + ot_nolock_span_context_destroy(context); +} + + +/*** + * NAME + * ot_span_context_new - + * + * ARGUMENTS + * span - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +struct otc_span_context *ot_span_context_new(const struct otc_span *span) +{ + int64_t idx = ot_span_context.key++; + struct otc_span_context *retptr; + + if (idx == 0) { + ot_span_context_handle.clear(); + ot_span_context_handle.reserve(8192); + } + + if ((retptr = OT_CAST_TYPEOF(retptr, OT_EXT_MALLOC(sizeof(*retptr)))) == nullptr) { + ot_span_context.alloc_fail_cnt++; + + return retptr; + } + + retptr->idx = (span == nullptr) ? idx : -1; + retptr->span = span; + retptr->destroy = ot_span_context_destroy; /* lock span_context */ + + return retptr; +} + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/src/tracer.cpp b/src/tracer.cpp new file mode 100644 index 0000000..80f56b0 --- /dev/null +++ b/src/tracer.cpp @@ -0,0 +1,874 @@ +/*** + * Copyright 2020 HAProxy Technologies + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "include.h" + + +static std::unique_ptr<const opentracing::DynamicTracingLibraryHandle> ot_dynlib = nullptr; +static std::shared_ptr<opentracing::Tracer> ot_tracer = nullptr; + + +/*** + * NAME + * ot_tracer_load - + * + * ARGUMENTS + * library - + * errbuf - + * errbufsiz - + * handle - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static int ot_tracer_load(const char *library, char *errbuf, int errbufsiz, opentracing::DynamicTracingLibraryHandle &handle) +{ + std::string errmsg; + + /* Load the tracer library. */ + auto handle_maybe = opentracing::DynamicallyLoadTracingLibrary(library, errmsg); + if (!handle_maybe) { + (void)snprintf(errbuf, errbufsiz, "Failed to load tracing library: %s", errmsg.empty() ? handle_maybe.error().message().c_str() : errmsg.c_str()); + + return -1; + } + + handle = std::move(*handle_maybe); + + return 0; +} + + +/*** + * NAME + * ot_tracer_start - + * + * ARGUMENTS + * config - + * errbuf - + * errbufsiz - + * tracer - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static int ot_tracer_start(const char *config, char *errbuf, int errbufsiz, std::shared_ptr<opentracing::Tracer> &tracer) +{ + std::string errmsg; + + auto &tracer_factory = ot_dynlib->tracer_factory(); + + /* Create a tracer with the requested configuration. */ + auto tracer_maybe = tracer_factory.MakeTracer(config, errmsg); + if (!tracer_maybe) { + (void)snprintf(errbuf, errbufsiz, "Failed to construct tracer: %s", errmsg.empty() ? tracer_maybe.error().message().c_str() : errmsg.c_str()); + + return -1; + } + + tracer = std::move(*tracer_maybe); + + return 0; +} + + +/*** + * NAME + * ot_tracer_close - + * + * ARGUMENTS + * tracer - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void ot_tracer_close(struct otc_tracer *tracer) +{ + if (tracer == nullptr) + return; + + if (ot_dynlib == nullptr) + return; + + if (ot_tracer != nullptr) + ot_tracer->Close(); + tracer->destroy(&tracer); +} + + +/*** + * NAME + * ot_tracer_start_span_with_options - + * + * ARGUMENTS + * tracer - NOT USED + * operation_name - + * options - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static struct otc_span *ot_tracer_start_span_with_options(struct otc_tracer *tracer, const char *operation_name, const struct otc_start_span_options *options) +{ + OT_LOCK_GUARD(span); + std::unique_ptr<opentracing::Span> span_maybe = nullptr; + struct otc_span *retptr = nullptr; + + if (ot_tracer == nullptr) + return retptr; + else if ((tracer == nullptr) || (operation_name == nullptr)) + return retptr; + + /* Allocating memory for the span. */ + if ((retptr = ot_span_new()) == nullptr) + return retptr; + + if (options == nullptr) { + span_maybe = ot_tracer->StartSpan(operation_name); + } else { + struct opentracing::StartSpanOptions span_options; + + if (options->start_time_steady.value.tv_sec > 0) { + auto dt = timespec_to_duration(&(options->start_time_steady.value)); + + span_options.start_steady_timestamp = std::chrono::time_point<std::chrono::steady_clock>(dt); + } + + if (options->start_time_system.value.tv_sec > 0) { +#ifdef __clang__ + auto dt = timespec_to_duration_us(&(options->start_time_system.value)); +#else + auto dt = timespec_to_duration(&(options->start_time_system.value)); +#endif + + span_options.start_system_timestamp = std::chrono::time_point<std::chrono::system_clock>(dt); + } + + if (options->references != nullptr) { + int lock_cnt = 0; + + for (int i = 0; i < options->num_references; i++) { + const opentracing::SpanContext *context = nullptr; + + if (OT_SPAN_IS_VALID(options->references[i].referenced_context->span)) { + context = &(ot_span_handle.at(options->references[i].referenced_context->span->idx)->context()); + } + else if (OT_CTX_KEY_IS_VALID(options->references[i].referenced_context)) { +#ifdef OT_THREADS_NO_LOCKING + lock_cnt++; +#else + if (lock_cnt++ == 0) + ot_span_context.mutex.lock(); +#endif + + context = ot_span_context_handle.at(options->references[i].referenced_context->idx).get(); + } + + if (options->references[i].type == otc_span_reference_child_of) + span_options.references.push_back(std::make_pair(opentracing::SpanReferenceType::ChildOfRef, context)); + else if (options->references[i].type == otc_span_reference_follows_from) + span_options.references.push_back(std::make_pair(opentracing::SpanReferenceType::FollowsFromRef, context)); + } + +#ifndef OT_THREADS_NO_LOCKING + if (lock_cnt > 0) + ot_span_context.mutex.unlock(); +#endif + } + + if (options->tags != nullptr) { + for (int i = 0; i < options->num_tags; i++) + if (options->tags[i].value.type == otc_value_bool) { + span_options.tags.push_back(std::make_pair(options->tags[i].key, options->tags[i].value.value.bool_value)); + } + else if (options->tags[i].value.type == otc_value_double) { + span_options.tags.push_back(std::make_pair(options->tags[i].key, options->tags[i].value.value.double_value)); + } + else if (options->tags[i].value.type == otc_value_int64) { + span_options.tags.push_back(std::make_pair(options->tags[i].key, options->tags[i].value.value.int64_value)); + } + else if (options->tags[i].value.type == otc_value_uint64) { + span_options.tags.push_back(std::make_pair(options->tags[i].key, options->tags[i].value.value.uint64_value)); + } + else if (options->tags[i].value.type == otc_value_string) { + std::string str_value = options->tags[i].value.value.string_value; + + span_options.tags.push_back(std::make_pair(options->tags[i].key, str_value)); + } + else if (options->tags[i].value.type == otc_value_null) { + span_options.tags.push_back(std::make_pair(options->tags[i].key, nullptr)); + } + } + + span_maybe = ot_tracer->StartSpanWithOptions(operation_name, span_options); + } + + if (span_maybe != nullptr) + ot_span_handle.emplace(retptr->idx, std::move(span_maybe)); + else + ot_nolock_span_destroy(&retptr); + + return retptr; +} + + +/*** + * NAME + * ot_tracer_start_span - + * + * ARGUMENTS + * tracer - NOT USED + * operation_name - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static struct otc_span *ot_tracer_start_span(struct otc_tracer *tracer, const char *operation_name) +{ + return ot_tracer_start_span_with_options(tracer, operation_name, nullptr); +} + + +/*** + * NAME + * ot_tracer_inject_text_map - + * + * ARGUMENTS + * tracer - NOT USED + * carrier - + * span_context - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static otc_propagation_error_code_t ot_tracer_inject_text_map(struct otc_tracer *tracer, struct otc_text_map_writer *carrier, const struct otc_span_context *span_context) +{ + TextMap text_map; + TextMapCarrier text_map_carrier(text_map); + opentracing::expected<void> rc; + + if (ot_tracer == nullptr) + return otc_propagation_error_code_invalid_tracer; + else if ((tracer == nullptr) || (carrier == nullptr)) + return otc_propagation_error_code_invalid_carrier; + else if (!OT_CTX_IS_VALID(span_context)) + return otc_propagation_error_code_span_context_corrupted; + + if (OT_SPAN_IS_VALID(span_context->span)) { + OT_LOCK_GUARD(span); + + rc = ot_tracer->Inject(ot_span_handle.at(span_context->span->idx)->context(), text_map_carrier); + } + else if (OT_CTX_KEY_IS_VALID(span_context)) { + OT_LOCK_GUARD(span_context); + + rc = ot_tracer->Inject(*(ot_span_context_handle.at(span_context->idx)), text_map_carrier); + } + + if (!rc) + return otc_propagation_error_code_unknown; + else if (text_map.empty()) + return otc_propagation_error_code_unknown; + else if (otc_text_map_new(&(carrier->text_map), text_map.size()) == nullptr) + return otc_propagation_error_code_unknown; + + for (auto const &it : text_map) + if (carrier->set != nullptr) { + otc_propagation_error_code_t retval = carrier->set(carrier, it.first.c_str(), it.second.c_str()); + if (retval != otc_propagation_error_code_success) + return retval; + } + else if (otc_text_map_add(&(carrier->text_map), it.first.c_str(), 0, it.second.c_str(), 0, OT_CAST_STAT(otc_text_map_flags_t, OTC_TEXT_MAP_DUP_KEY | OTC_TEXT_MAP_DUP_VALUE)) == -1) + return otc_propagation_error_code_unknown; + + return otc_propagation_error_code_success; +} + + +/*** + * NAME + * ot_tracer_inject_http_headers - + * + * ARGUMENTS + * tracer - NOT USED + * carrier - + * span_context - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static otc_propagation_error_code_t ot_tracer_inject_http_headers(struct otc_tracer *tracer, struct otc_http_headers_writer *carrier, const struct otc_span_context *span_context) +{ + TextMap text_map; + HTTPHeadersCarrier http_headers_carrier(text_map); + opentracing::expected<void> rc; + + if (ot_tracer == nullptr) + return otc_propagation_error_code_invalid_tracer; + else if ((tracer == nullptr) || (carrier == nullptr)) + return otc_propagation_error_code_invalid_carrier; + else if (!OT_CTX_IS_VALID(span_context)) + return otc_propagation_error_code_span_context_corrupted; + + if (OT_SPAN_IS_VALID(span_context->span)) { + OT_LOCK_GUARD(span); + + rc = ot_tracer->Inject(ot_span_handle.at(span_context->span->idx)->context(), http_headers_carrier); + } + else if (OT_CTX_KEY_IS_VALID(span_context)) { + OT_LOCK_GUARD(span_context); + + rc = ot_tracer->Inject(*(ot_span_context_handle.at(span_context->idx)), http_headers_carrier); + } + + if (!rc) + return otc_propagation_error_code_unknown; + else if (text_map.empty()) + return otc_propagation_error_code_unknown; + else if (otc_text_map_new(&(carrier->text_map), text_map.size()) == nullptr) + return otc_propagation_error_code_unknown; + + for (auto const &it : text_map) + if (carrier->set != nullptr) { + otc_propagation_error_code_t retval = carrier->set(carrier, it.first.c_str(), it.second.c_str()); + if (retval != otc_propagation_error_code_success) + return retval; + } + else if (otc_text_map_add(&(carrier->text_map), it.first.c_str(), 0, it.second.c_str(), 0, OT_CAST_STAT(otc_text_map_flags_t, OTC_TEXT_MAP_DUP_KEY | OTC_TEXT_MAP_DUP_VALUE)) == -1) + return otc_propagation_error_code_unknown; + + return otc_propagation_error_code_success; +} + + +/*** + * NAME + * ot_tracer_inject_binary - + * + * ARGUMENTS + * tracer - NOT USED + * carrier - + * span_context - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static otc_propagation_error_code_t ot_tracer_inject_binary(struct otc_tracer *tracer, struct otc_custom_carrier_writer *carrier, const struct otc_span_context *span_context) +{ + std::ostringstream oss(std::ios::binary); + opentracing::expected<void> rc; + + if (ot_tracer == nullptr) + return otc_propagation_error_code_invalid_tracer; + else if ((tracer == nullptr) || (carrier == nullptr)) + return otc_propagation_error_code_invalid_carrier; + else if (!OT_CTX_IS_VALID(span_context)) + return otc_propagation_error_code_span_context_corrupted; + + if (OT_SPAN_IS_VALID(span_context->span)) { + OT_LOCK_GUARD(span); + + rc = ot_tracer->Inject(ot_span_handle.at(span_context->span->idx)->context(), oss); + } + else if (OT_CTX_KEY_IS_VALID(span_context)) { + OT_LOCK_GUARD(span_context); + + rc = ot_tracer->Inject(*(ot_span_context_handle.at(span_context->idx)), oss); + } + + if (rc) + if (otc_binary_data_new(&(carrier->binary_data), oss.str().c_str(), oss.str().size()) != nullptr) + return otc_propagation_error_code_success; + + return otc_propagation_error_code_unknown; +} + + +/*** + * NAME + * ot_tracer_inject_custom - + * + * ARGUMENTS + * tracer - NOT USED + * carrier - NOT USED + * span_context - NOT USED + * + * DESCRIPTION + * - NOT IMPLEMENTED + * + * RETURN VALUE + * - + */ +static otc_propagation_error_code_t ot_tracer_inject_custom(struct otc_tracer *tracer, struct otc_custom_carrier_writer *carrier, const struct otc_span_context *span_context) +{ + if ((tracer == nullptr) || (carrier == nullptr)) + return otc_propagation_error_code_invalid_carrier; + else if (!OT_CTX_IS_VALID(span_context)) + return otc_propagation_error_code_span_context_corrupted; + + return otc_propagation_error_code_success; +} + + +/*** + * NAME + * ot_span_context_add - + * + * ARGUMENTS + * span_context - + * span_context_maybe - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static otc_propagation_error_code_t ot_span_context_add(struct otc_span_context **span_context, std::unique_ptr<opentracing::SpanContext> &span_context_maybe) +{ + OT_LOCK_GUARD(span_context); + + if ((*span_context = ot_span_context_new(nullptr)) == nullptr) { + span_context_maybe.reset(nullptr); + + return otc_propagation_error_code_unknown; + } + + ot_span_context_handle.emplace((*span_context)->idx, std::move(span_context_maybe)); + + return otc_propagation_error_code_success; +} + + +/*** + * NAME + * ot_tracer_text_map_add - + * + * ARGUMENTS + * arg - + * key - + * value - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static otc_propagation_error_code_t ot_tracer_text_map_add(void *arg, const char *key, const char *value) +{ + TextMap *text_map = OT_CAST_REINTERPRET(TextMap *, arg); + + if ((arg == nullptr) || (key == nullptr) || (value == nullptr)) + return otc_propagation_error_code_unknown; + + text_map->emplace(key, value); + + return otc_propagation_error_code_success; +} + + +/*** + * NAME + * ot_tracer_extract_text_map - + * + * ARGUMENTS + * tracer - NOT USED + * carrier - + * span_context - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static otc_propagation_error_code_t ot_tracer_extract_text_map(struct otc_tracer *tracer, const struct otc_text_map_reader *carrier, struct otc_span_context **span_context) +{ + TextMap text_map; + TextMapCarrier text_map_carrier(text_map); + + if (ot_tracer == nullptr) + return otc_propagation_error_code_invalid_tracer; + else if ((tracer == nullptr) || (carrier == nullptr)) + return otc_propagation_error_code_invalid_carrier; + else if (span_context == nullptr) + return otc_propagation_error_code_invalid_span_context; + + if (carrier->foreach_key != nullptr) { + otc_propagation_error_code_t rc = carrier->foreach_key(OT_CAST_CONST(struct otc_text_map_reader *, carrier), ot_tracer_text_map_add, &text_map); + if (rc != otc_propagation_error_code_success) + return rc; + } else { + for (size_t i = 0; i < carrier->text_map.count; i++) + text_map[carrier->text_map.key[i]] = carrier->text_map.value[i]; + } + + auto span_context_maybe = ot_tracer->Extract(text_map_carrier); + if (!span_context_maybe) + return otc_propagation_error_code_span_context_not_found; + + return ot_span_context_add(span_context, *span_context_maybe); +} + + +/*** + * NAME + * ot_tracer_extract_http_headers - + * + * ARGUMENTS + * tracer - NOT USED + * carrier - + * span_context - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static otc_propagation_error_code_t ot_tracer_extract_http_headers(struct otc_tracer *tracer, const struct otc_http_headers_reader *carrier, struct otc_span_context **span_context) +{ + TextMap text_map; + HTTPHeadersCarrier http_headers_carrier(text_map); + + if (ot_tracer == nullptr) + return otc_propagation_error_code_invalid_tracer; + else if ((tracer == nullptr) || (carrier == nullptr)) + return otc_propagation_error_code_invalid_carrier; + else if (span_context == nullptr) + return otc_propagation_error_code_invalid_span_context; + + if (carrier->foreach_key != nullptr) { + otc_propagation_error_code_t rc = carrier->foreach_key(OT_CAST_CONST(struct otc_http_headers_reader *, carrier), ot_tracer_text_map_add, &text_map); + if (rc != otc_propagation_error_code_success) + return rc; + } else { + for (size_t i = 0; i < carrier->text_map.count; i++) + text_map[carrier->text_map.key[i]] = carrier->text_map.value[i]; + } + + auto span_context_maybe = ot_tracer->Extract(http_headers_carrier); + if (!span_context_maybe) + return otc_propagation_error_code_span_context_not_found; + + return ot_span_context_add(span_context, *span_context_maybe); +} + + +/*** + * NAME + * ot_tracer_extract_binary - + * + * ARGUMENTS + * tracer - NOT USED + * carrier - + * span_context - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static otc_propagation_error_code_t ot_tracer_extract_binary(struct otc_tracer *tracer, const struct otc_custom_carrier_reader *carrier, struct otc_span_context **span_context) +{ + if (ot_tracer == nullptr) + return otc_propagation_error_code_invalid_tracer; + else if ((tracer == nullptr) || (carrier == nullptr)) + return otc_propagation_error_code_invalid_carrier; + else if (span_context == nullptr) + return otc_propagation_error_code_invalid_span_context; + + if ((carrier->binary_data.data == nullptr) || (carrier->binary_data.size == 0)) + return otc_propagation_error_code_invalid_carrier; + + std::string iss_data(OT_CAST_REINTERPRET(const char *, carrier->binary_data.data), carrier->binary_data.size); + std::istringstream iss(iss_data, std::ios::binary); + + auto span_context_maybe = ot_tracer->Extract(iss); + if (!span_context_maybe) + return otc_propagation_error_code_span_context_not_found; + + return ot_span_context_add(span_context, *span_context_maybe); +} + + +/*** + * NAME + * ot_tracer_extract_custom - + * + * ARGUMENTS + * tracer - NOT USED + * carrier - NOT USED + * span_context - NOT USED + * + * DESCRIPTION + * - NOT IMPLEMENTED + * + * RETURN VALUE + * - + */ +static otc_propagation_error_code_t ot_tracer_extract_custom(struct otc_tracer *tracer, const struct otc_custom_carrier_reader *carrier, struct otc_span_context **span_context) +{ + if ((tracer == nullptr) || (carrier == nullptr) || (span_context == nullptr)) + return otc_propagation_error_code_unknown; + + return otc_propagation_error_code_success; +} + + +/*** + * NAME + * ot_tracer_destroy - + * + * ARGUMENTS + * tracer - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void ot_tracer_destroy(struct otc_tracer **tracer) +{ + if ((tracer == nullptr) || (*tracer == nullptr)) + return; + + OT_FREE_CLEAR(*tracer); +} + + +/*** + * NAME + * ot_tracer_new - + * + * ARGUMENTS + * This function takes no arguments. + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +struct otc_tracer *ot_tracer_new(void) +{ + const static struct otc_tracer tracer_init = { + .close = ot_tracer_close, /* lock not required */ + .start_span = ot_tracer_start_span, /* lock span */ + .start_span_with_options = ot_tracer_start_span_with_options, /* lock span */ + .inject_text_map = ot_tracer_inject_text_map, /* lock span and/or span_context */ + .inject_http_headers = ot_tracer_inject_http_headers, /* lock span and/or span_context */ + .inject_binary = ot_tracer_inject_binary, /* lock span and/or span_context */ + .inject_custom = ot_tracer_inject_custom, /* NOT IMPLEMENTED */ + .extract_text_map = ot_tracer_extract_text_map, /* lock span_context */ + .extract_http_headers = ot_tracer_extract_http_headers, /* lock span_context */ + .extract_binary = ot_tracer_extract_binary, /* lock span_context */ + .extract_custom = ot_tracer_extract_custom, /* NOT IMPLEMENTED */ + .destroy = ot_tracer_destroy /* lock not required */ + }; + struct otc_tracer *retptr; + + if ((retptr = OT_CAST_TYPEOF(retptr, OTC_DBG_CALLOC(1, sizeof(*retptr)))) != nullptr) + (void)memcpy(retptr, &tracer_init, sizeof(*retptr)); + + return retptr; +} + + +/*** + * NAME + * otc_tracer_load - + * + * ARGUMENTS + * library - + * errbuf - + * errbufsiz - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +struct otc_tracer *otc_tracer_load(const char *library, char *errbuf, int errbufsiz) +{ + std::unique_ptr<opentracing::DynamicTracingLibraryHandle> handle { + new opentracing::DynamicTracingLibraryHandle {} + }; + std::shared_ptr<opentracing::Tracer> tracer; + struct otc_tracer *retptr = nullptr; + + if ((retptr = ot_tracer_new()) == nullptr) { + /* Do nothing. */; + } + else if (ot_tracer_load(library, errbuf, errbufsiz, *handle) == -1) { + retptr->destroy(&retptr); + } + else { + ot_dynlib = std::move(handle); + } + + return retptr; +} + + +/*** + * NAME + * otc_tracer_start - + * + * ARGUMENTS + * cfgfile - + * cfgbuf - + * errbuf - + * errbufsiz - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +int otc_tracer_start(const char *cfgfile, const char *cfgbuf, char *errbuf, int errbufsiz) +{ + std::shared_ptr<opentracing::Tracer> tracer; + char *config = OT_CAST_CONST(char *, cfgbuf); + int retval = -1; + + if (cfgfile != nullptr) { + config = otc_file_read(cfgfile, "#", errbuf, errbufsiz); + if (config == nullptr) + return retval; + } + + if (ot_tracer_start(config, errbuf, errbufsiz, tracer) == -1) { + /* Do nothing. */; + } else { + ot_tracer = std::move(tracer); + + (void)opentracing::Tracer::InitGlobal(ot_tracer); + + retval = 0; + } + + if (config != cfgbuf) + OT_FREE(config); + + return retval; +} + + +/*** + * NAME + * otc_tracer_init - + * + * ARGUMENTS + * library - + * cfgfile - + * cfgbuf - + * errbuf - + * errbufsiz - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +struct otc_tracer *otc_tracer_init(const char *library, const char *cfgfile, const char *cfgbuf, char *errbuf, int errbufsiz) +{ + struct otc_tracer *retptr = nullptr; + + if ((retptr = otc_tracer_load(library, errbuf, errbufsiz)) != nullptr) + if (otc_tracer_start(cfgfile, cfgbuf, errbuf, errbufsiz) == -1) + retptr->destroy(&retptr); + + return retptr; +} + + +/*** + * NAME + * otc_tracer_global - + * + * ARGUMENTS + * tracer - NOT USED + * + * DESCRIPTION + * - NOT IMPLEMENTED + * + * RETURN VALUE + * This function does not return a value. + */ +void otc_tracer_global(struct otc_tracer *tracer) +{ + if (tracer == nullptr) + return; +} + + +/*** + * NAME + * otc_tracer_init_global - + * + * ARGUMENTS + * tracer - NOT USED + * + * DESCRIPTION + * - NOT IMPLEMENTED + * + * RETURN VALUE + * This function does not return a value. + */ +void otc_tracer_init_global(struct otc_tracer *tracer) +{ + if (tracer == nullptr) + return; +} + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/src/util.cpp b/src/util.cpp new file mode 100644 index 0000000..3236e9d --- /dev/null +++ b/src/util.cpp @@ -0,0 +1,451 @@ +/*** + * Copyright 2020 HAProxy Technologies + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "include.h" + + +otc_ext_malloc_t otc_ext_malloc = OT_IFDEF_DBG(otc_dbg_malloc, malloc); +otc_ext_free_t otc_ext_free = OT_IFDEF_DBG(otc_dbg_free, free); + + +/*** + * NAME + * timespec_to_duration_us - + * + * ARGUMENTS + * ts - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +std::chrono::microseconds timespec_to_duration_us(const struct timespec *ts) +{ + auto duration = std::chrono::seconds{ts->tv_sec} + std::chrono::microseconds{ts->tv_nsec / 1000}; + + return std::chrono::duration_cast<std::chrono::microseconds>(duration); +} + + +/*** + * NAME + * timespec_to_duration - + * + * ARGUMENTS + * ts - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +std::chrono::nanoseconds timespec_to_duration(const struct timespec *ts) +{ + auto duration = std::chrono::seconds{ts->tv_sec} + std::chrono::nanoseconds{ts->tv_nsec}; + + return std::chrono::duration_cast<std::chrono::nanoseconds>(duration); +} + + +/*** + * NAME + * otc_ext_init - + * + * ARGUMENTS + * func_malloc - + * func_free - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +void otc_ext_init(otc_ext_malloc_t func_malloc, otc_ext_free_t func_free) +{ + otc_ext_malloc = (func_malloc != nullptr) ? func_malloc : OT_IFDEF_DBG(otc_dbg_malloc, malloc); + otc_ext_free = (func_free != nullptr) ? func_free : OT_IFDEF_DBG(otc_dbg_free, free); +} + + +/*** + * NAME + * otc_text_map_new - + * + * ARGUMENTS + * text_map - + * size - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +struct otc_text_map *otc_text_map_new(struct otc_text_map *text_map, size_t size) +{ + struct otc_text_map *retptr = text_map; + + if (retptr == nullptr) + retptr = OT_CAST_TYPEOF(retptr, OTC_DBG_CALLOC(1, sizeof(*retptr))); + + if (retptr != nullptr) { + retptr->count = 0; + retptr->size = size; + retptr->is_dynamic = text_map == nullptr; + + if (size == 0) + /* Do nothing. */; + else if ((retptr->key = OT_CAST_TYPEOF(retptr->key, OTC_DBG_CALLOC(size, sizeof(*(retptr->key))))) == nullptr) + otc_text_map_destroy(&retptr, OT_CAST_STAT(otc_text_map_flags_t, 0)); + else if ((retptr->value = OT_CAST_TYPEOF(retptr->value, OTC_DBG_CALLOC(size, sizeof(*(retptr->value))))) == nullptr) + otc_text_map_destroy(&retptr, OT_CAST_STAT(otc_text_map_flags_t, 0)); + } + + return retptr; +} + + +/*** + * NAME + * otc_text_map_add - + * + * ARGUMENTS + * text_map - + * key - + * key_len - + * value - + * value_len - + * flags - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +int otc_text_map_add(struct otc_text_map *text_map, const char *key, size_t key_len, const char *value, size_t value_len, otc_text_map_flags_t flags) +{ + int retval = -1; + + if ((text_map == nullptr) || (key == nullptr) || (value == nullptr)) + return retval; + + /* + * Check if it is necessary to increase the number of key/value pairs. + * The number of pairs is increased by half the current number of pairs + * (for example: 8 -> 12 -> 18 -> 27 -> 40 -> 60 ...). + */ + if (text_map->count >= text_map->size) { + typeof(text_map->key) ptr_key; + typeof(text_map->value) ptr_value; + size_t size_add = (text_map->size > 1) ? (text_map->size / 2) : 1; + + if ((ptr_key = OT_CAST_TYPEOF(ptr_key, OTC_DBG_REALLOC(text_map->key, OT_TEXT_MAP_SIZE(key, size_add)))) == nullptr) + return retval; + + text_map->key = ptr_key; + (void)memset(text_map->key + OT_TEXT_MAP_SIZE(key, 0), 0, sizeof(*(text_map->key)) * size_add); + + if ((ptr_value = OT_CAST_TYPEOF(ptr_value, OTC_DBG_REALLOC(text_map->value, OT_TEXT_MAP_SIZE(value, size_add)))) == nullptr) + return retval; + + text_map->value = ptr_value; + (void)memset(text_map->value + OT_TEXT_MAP_SIZE(value, 0), 0, sizeof(*(text_map->value)) * size_add); + + text_map->size += size_add; + } + + text_map->key[text_map->count] = (flags & OTC_TEXT_MAP_DUP_KEY) ? ((key_len > 0) ? OTC_DBG_STRNDUP(key, key_len) : OTC_DBG_STRDUP(key)) : OT_CAST_CONST(char *, key); + text_map->value[text_map->count] = (flags & OTC_TEXT_MAP_DUP_VALUE) ? ((value_len > 0) ? OTC_DBG_STRNDUP(value, value_len) : OTC_DBG_STRDUP(value)) : OT_CAST_CONST(char *, value); + + if ((text_map->key[text_map->count] != nullptr) && (text_map->value[text_map->count] != nullptr)) + retval = text_map->count; + + text_map->count++; + + return retval; +} + + +/*** + * NAME + * otc_text_map_destroy - + * + * ARGUMENTS + * text_map - + * flags - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +void otc_text_map_destroy(struct otc_text_map **text_map, otc_text_map_flags_t flags) +{ + if ((text_map == nullptr) || (*text_map == nullptr)) + return; + + if ((*text_map)->key != nullptr) { + if (flags & OTC_TEXT_MAP_FREE_KEY) + for (size_t i = 0; i < (*text_map)->count; i++) + OT_FREE((*text_map)->key[i]); + + OT_FREE_CLEAR((*text_map)->key); + } + + if ((*text_map)->value != nullptr) { + if (flags & OTC_TEXT_MAP_FREE_VALUE) + for (size_t i = 0; i < (*text_map)->count; i++) + OT_FREE((*text_map)->value[i]); + + OT_FREE_CLEAR((*text_map)->value); + } + + if ((*text_map)->is_dynamic) { + OT_FREE_CLEAR(*text_map); + } else { + (*text_map)->count = 0; + (*text_map)->size = 0; + } +} + + +/*** + * NAME + * otc_binary_data_new - + * + * ARGUMENTS + * binary_data - + * data - + * size - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +struct otc_binary_data *otc_binary_data_new(struct otc_binary_data *binary_data, const void *data, size_t size) +{ + struct otc_binary_data *retptr = binary_data; + + if (retptr == nullptr) + retptr = OT_CAST_TYPEOF(retptr, OTC_DBG_CALLOC(1, sizeof(*retptr))); + + if (retptr != nullptr) { + retptr->size = size; + retptr->is_dynamic = binary_data == nullptr; + + if ((data == nullptr) || (size == 0)) + /* Do nothing. */; + else if ((retptr->data = OTC_DBG_MALLOC(size)) != nullptr) + (void)memcpy(retptr->data, data, size); + else + otc_binary_data_destroy(&retptr); + } + + return retptr; +} + + +/*** + * NAME + * otc_binary_data_destroy - + * + * ARGUMENTS + * binary_data - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +void otc_binary_data_destroy(struct otc_binary_data **binary_data) +{ + if ((binary_data == nullptr) || (*binary_data == nullptr)) + return; + + OT_FREE_CLEAR((*binary_data)->data); + + if ((*binary_data)->is_dynamic) + OT_FREE_CLEAR(*binary_data); + else + (*binary_data)->size = 0; +} + + +/*** + * NAME + * otc_strerror - + * + * ARGUMENTS + * errnum - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +const char *otc_strerror(int errnum) +{ + static __THR char retbuf[1024]; + const char *retptr = retbuf; + + errno = 0; + (void)strerror_r(errnum, retbuf, sizeof(retbuf)); + if (errno != 0) + retptr = "Unknown error"; + + return retptr; +} + + +/*** + * NAME + * otc_file_read - + * + * ARGUMENTS + * filename - + * comment - + * errbuf - + * errbufsiz - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +char *otc_file_read(const char *filename, const char *comment, char *errbuf, int errbufsiz) +{ + struct stat statbuf; + char *retptr = nullptr; + int fd, rc; + + if (filename == nullptr) + return retptr; + + if ((fd = open(filename, O_RDONLY)) == -1) { + (void)snprintf(errbuf, errbufsiz, "'%s': %s", filename, otc_strerror(errno)); + } + else if ((rc = fstat(fd, &statbuf)) == -1) { + (void)snprintf(errbuf, errbufsiz, "'%s': %s", filename, otc_strerror(errno)); + } + else if ((retptr = OT_CAST_TYPEOF(retptr, OTC_DBG_MALLOC(statbuf.st_size + 1))) == nullptr) { + (void)snprintf(errbuf, errbufsiz, "cannot allocate memory: %s", otc_strerror(errno)); + } + else { + char *buf = retptr; + off_t size = statbuf.st_size; + + while (size > 0) { + ssize_t n; + + if ((n = read(fd, buf, size)) > 0) { + size -= n; + buf += n; + } + else if (n == -1) { + if ((errno == EAGAIN) || (errno == EWOULDBLOCK)) { + break; + } + else if (errno != EINTR) + { + (void)snprintf(errbuf, errbufsiz, "'%s': %s", filename, otc_strerror(errno)); + OT_FREE_CLEAR(retptr); + + break; + } + } + else { + break; + } + } + + if (comment != nullptr) { + off_t i = 0, c = -1, n = statbuf.st_size; + + for (i = 0; i < n; i++) + if (c < 0) { + /* Remember the starting position of the comment line. */ + if ((strchr(comment, retptr[i]) != nullptr) && ((i == 0) || (retptr[i - 1] == '\n'))) + c = i; + } + else if ((retptr[i] == '\n') && ((i + 1) < n)) { + /* Delete the entire comment line. */ + (void)memmove(retptr + c, retptr + i + 1, n - i - 1); + + n -= i + 1 - c; + i = c - 1; + c = -1; + } + + /* If a comment remains in the last line, delete it. */ + if (c >= 0) { + n -= i - c; + i = c; + } + + retptr[i] = '\0'; + } + else if (size != 0) + OT_FREE_CLEAR(retptr); + } + + (void)close(fd); + + return retptr; +} + + +/*** + * NAME + * otc_statistics - + * + * ARGUMENTS + * buffer - + * bufsiz - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +void otc_statistics(char *buffer, size_t bufsiz) +{ + if ((buffer == nullptr) || (bufsiz < 24)) + return; + + (void)snprintf(buffer, bufsiz, "span: %" PRId64 "/%" PRId64 "+%" PRId64 "(%" PRId64 ")/%" PRId64 ", context: %" PRId64 "/%" PRId64 "+%" PRId64 "(%" PRId64 ")/%" PRId64, + ot_span.key, ot_span_handle.size(), ot_span.erase_cnt, ot_span.destroy_cnt, ot_span.alloc_fail_cnt, + ot_span_context.key, ot_span_context_handle.size(), ot_span_context.erase_cnt, ot_span_context.destroy_cnt, ot_span_context.alloc_fail_cnt); +} + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/test/Makefile.am b/test/Makefile.am new file mode 100644 index 0000000..72b61e7 --- /dev/null +++ b/test/Makefile.am @@ -0,0 +1,23 @@ +## Process this file with automake to produce Makefile.in +## + AM_CPPFLAGS = -I\$(top_builddir)/include -I$(srcdir)/../include @OPENTRACING_C_WRAPPER_CPPFLAGS@ + AM_CFLAGS = @OPENTRACING_C_WRAPPER_CFLAGS@ + AM_LDFLAGS = @OPENTRACING_C_WRAPPER_LDFLAGS@ + +if WANT_DEBUG + bin_PROGRAMS = ot-c-wrapper-test_dbg +ot_c_wrapper_test_dbg_SOURCES = opentracing.c test.c util.c + ot_c_wrapper_test_dbg_LDADD = -lstdc++ -lm @OPENTRACING_C_WRAPPER_LIBS@ $(top_builddir)/src/libopentracing-c-wrapper_dbg.la +ot_c_wrapper_test_dbg_LDFLAGS = @OPENTRACING_C_WRAPPER_LDFLAGS@ + +else + + bin_PROGRAMS = ot-c-wrapper-test +ot_c_wrapper_test_SOURCES = opentracing.c test.c util.c + ot_c_wrapper_test_LDADD = -lstdc++ -lm @OPENTRACING_C_WRAPPER_LIBS@ $(top_builddir)/src/libopentracing-c-wrapper.la +ot_c_wrapper_test_LDFLAGS = @OPENTRACING_C_WRAPPER_LDFLAGS@ +endif + +CLEANFILES = a.out +## +## Makefile.am ends here diff --git a/test/cfg-dd.json b/test/cfg-dd.json new file mode 100644 index 0000000..5763d06 --- /dev/null +++ b/test/cfg-dd.json @@ -0,0 +1,5 @@ +{ + "service": "opentracing-c-wrapper-test", + "agent_host": "localhost", + "agent_port": 8126 +} diff --git a/test/cfg-jaeger.yml b/test/cfg-jaeger.yml new file mode 100644 index 0000000..ca3904f --- /dev/null +++ b/test/cfg-jaeger.yml @@ -0,0 +1,34 @@ +service_name: + opentracing-c-wrapper-test + +### +# When using configuration object to instantiate the tracer, the type of +# sampling can be selected via sampler.type and sampler.param properties. +# Jaeger libraries support the following samplers: +# +# - Constant (sampler.type=const) sampler always makes the same decision for +# all traces. It either samples all traces (sampler.param=1) or none of +# them (sampler.param=0). +# +# - Probabilistic (sampler.type=probabilistic) sampler makes a random sampling +# decision with the probability of sampling equal to the value of +# sampler.param property. For example, with sampler.param=0.1 approximately +# 1 in 10 traces will be sampled. +# +# - Rate Limiting (sampler.type=ratelimiting) sampler uses a leaky bucket rate +# limiter to ensure that traces are sampled with a certain constant rate. +# For example, when sampler.param=2.0 it will sample requests with the rate +# of 2 traces per second. +# +# - Remote (sampler.type=remote, which is also the default) sampler consults +# Jaeger agent for the appropriate sampling strategy to use in the current +# service. This allows controlling the sampling strategies in the services +# from a central configuration in Jaeger backend, or even dynamically. +# +sampler: + type: ratelimiting + param: 10.0 + +reporter: + logSpans: true + localAgentHostPort: localhost:6831 diff --git a/test/cfg-zipkin.json b/test/cfg-zipkin.json new file mode 100644 index 0000000..e25634f --- /dev/null +++ b/test/cfg-zipkin.json @@ -0,0 +1,4 @@ +{ + "service_name": "opentracing-c-wrapper-test", + "collector_host": "localhost" +} diff --git a/test/debug.h b/test/debug.h new file mode 100644 index 0000000..c8d22b8 --- /dev/null +++ b/test/debug.h @@ -0,0 +1,58 @@ +/*** + * Copyright 2020 HAProxy Technologies + * + * 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. + */ +#ifndef TEST_DEBUG_H +#define TEST_DEBUG_H + +#define OT_LOG(f,...) (void)printf("[%4d] " f "\n", thread_id(), ##__VA_ARGS__) + +#ifdef DEBUG +enum DBG_LEVEL_enum { + DBG_LEVEL_FUNC = 0, /* Function debug level. */ + DBG_LEVEL_INFO, /* Generic info level. */ + DBG_LEVEL_DEBUG, /* Generic debug level. */ + DBG_LEVEL_OT, /* OpenTracing debug level. */ + DBG_LEVEL_WORKER, /* Worker debug level. */ + DBG_LEVEL_ENABLED, /* This have to be the last entry. */ +}; + +# define OT_FUNC(f,...) \ + do { \ + if (_nNULL(cfg_debug_level) && (*cfg_debug_level > 0)) \ + OT_LOG("%s(" f ")", __func__, ##__VA_ARGS__); \ + } while (0) +# define OT_DBG(l,f,...) \ + do { \ + if (_nNULL(cfg_debug_level) && (*cfg_debug_level & (1 << DBG_LEVEL_##l))) \ + OT_LOG(" " f, ##__VA_ARGS__); \ + } while (0) + + +extern uint8_t *cfg_debug_level; +#else +# define OT_FUNC(...) do { } while (0) +# define OT_DBG(...) do { } while (0) +#endif /* DEBUG */ + +#endif /* TEST_DEBUG_H */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/test/define.h b/test/define.h new file mode 100644 index 0000000..68dcf60 --- /dev/null +++ b/test/define.h @@ -0,0 +1,61 @@ +/*** + * Copyright 2020 HAProxy Technologies + * + * 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. + */ +#ifndef TEST_DEFINE_H +#define TEST_DEFINE_H + +#ifdef DEBUG +# define OTC_DBG_MEM +#endif + +#ifndef PACKAGE_BUILD +# define PACKAGE_BUILD 0 +#endif + +#define OT_USE_INJECT_CB +#define OT_USE_EXTRACT_CB + +#ifdef __linux__ +# define PRI_PIDT "d" +# define PRI_PTHREADT "lu" +#else +# define PRI_PIDT "ld" +# define PRI_PTHREADT "u" +#endif + +#ifndef MIN +# define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#define _NULL(a) ((a) == NULL) +#define _nNULL(a) ((a) != NULL) +#define TABLESIZE(a) ((int)(sizeof(a) / sizeof((a)[0]))) +#define IN_RANGE(v,a,b) (((v) >= (a)) && ((v) <= (b))) +#define TIMEVAL_DIFF_MS(a,b) (((a)->tv_sec - (b)->tv_sec) * 1000ULL + ((a)->tv_usec - (b)->tv_usec + 500) / 1000) +#define TIMEVAL_DIFF_US(a,b) (((a)->tv_sec - (b)->tv_sec) * 1000000ULL + (a)->tv_usec - (b)->tv_usec) +#define NIBBLE_TO_HEX(a) ((a) + (((a) < 10) ? '0' : ('a' - 10))) +#define SWAP(a,b) do { typeof(a) _a = (a); (a) = (b); (b) = _a; } while (0) +#define OT_VARGS(t,v) otc_value_##t, (v) + +#endif /* TEST_DEFINE_H */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/test/get-opentracing-plugins.sh b/test/get-opentracing-plugins.sh new file mode 100755 index 0000000..f2fe2d6 --- /dev/null +++ b/test/get-opentracing-plugins.sh @@ -0,0 +1,45 @@ +#!/bin/sh +# +_ARG_DIR="${1:-.}" + + +get () +{ + local _arg_tracer="${1}" + local _arg_version="${2}" + local _arg_url="${3}" + local _arg_file="${4}" + local _var_tmpfile="_tmpfile_" + local _var_plugin="lib${_arg_tracer}_opentracing_plugin-${_arg_version}.so" + + test -e "${_var_plugin}" && return 0 + + wget "https://github.com/${_arg_url}/releases/download/v${_arg_version}/${_arg_file}" -O "${_var_tmpfile}" || { + rm "${_var_tmpfile}" + return 1 + } + + case "$(file ${_var_tmpfile})" in + *shared\ object*) + mv "${_var_tmpfile}" "${_var_plugin}" ;; + + *gzip\ compressed\ data*) + gzip -cd "${_var_tmpfile}" > "${_var_plugin}" + rm "${_var_tmpfile}" ;; + esac +} + + +mkdir -p "${_ARG_DIR}" && cd "${_ARG_DIR}" || exit 1 + +get dd 1.1.2 DataDog/dd-opentracing-cpp linux-amd64-libdd_opentracing_plugin.so.gz +get dd 1.2.0 DataDog/dd-opentracing-cpp linux-amd64-libdd_opentracing_plugin.so.gz + +get jaeger 0.4.2 jaegertracing/jaeger-client-cpp libjaegertracing_plugin.linux_amd64.so +#et jaeger 0.5.0 jaegertracing/jaeger-client-cpp libjaegertracing_plugin.linux_amd64.so +#et jaeger 0.6.0 jaegertracing/jaeger-client-cpp libjaegertracing_plugin.linux_amd64.so + +get lightstep 0.12.0 lightstep/lightstep-tracer-cpp linux-amd64-liblightstep_tracer_plugin.so.gz +get lightstep 0.13.0 lightstep/lightstep-tracer-cpp linux-amd64-liblightstep_tracer_plugin.so.gz + +get zipkin 0.5.2 rnburn/zipkin-cpp-opentracing linux-amd64-libzipkin_opentracing_plugin.so.gz diff --git a/test/include.h b/test/include.h new file mode 100644 index 0000000..328ba72 --- /dev/null +++ b/test/include.h @@ -0,0 +1,67 @@ +/*** + * Copyright 2020 HAProxy Technologies + * + * 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. + */ +#ifndef TEST_INCLUDE_H +#define TEST_INCLUDE_H + +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <stdbool.h> +#include <errno.h> +#include <unistd.h> +#include <getopt.h> +#include <inttypes.h> +#include <libgen.h> +#include <pthread.h> +#include <sysexits.h> +#include <time.h> +#include <fcntl.h> +#include <sys/time.h> +#include <sys/types.h> +#include <sys/stat.h> +#ifdef __linux__ +# include <sys/syscall.h> +#endif + +#include "config.h" +#include "define.h" + +#include "opentracing-c-wrapper/define.h" +#include "opentracing-c-wrapper/dbg_malloc.h" +#include "opentracing-c-wrapper/common.h" +#include "opentracing-c-wrapper/util.h" +#include "opentracing-c-wrapper/value.h" +#include "opentracing-c-wrapper/span.h" +#include "opentracing-c-wrapper/propagation.h" +#include "opentracing-c-wrapper/tracer.h" + +#include "version.h" +#include "debug.h" +#include "opentracing.h" +#include "util.h" + +#endif /* TEST_INCLUDE_H */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/test/opentracing.c b/test/opentracing.c new file mode 100644 index 0000000..2e30fe5 --- /dev/null +++ b/test/opentracing.c @@ -0,0 +1,771 @@ +/*** + * Copyright 2020 HAProxy Technologies + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "include.h" + + +static void ot_text_map_show(const struct otc_text_map *text_map) +{ + OT_FUNC("%p", text_map); + + if (_NULL(text_map)) + return; + + OT_DBG(OT, "%p:{ %p %p %zu/%zu %hhu }", text_map, text_map->key, text_map->value, text_map->count, text_map->size, text_map->is_dynamic); + + if (_nNULL(text_map->key) && _nNULL(text_map->value) && (text_map->count > 0)) { + size_t i; + + for (i = 0; i < text_map->count; i++) + OT_DBG(OT, " \"%s\" -> \"%s\"", text_map->key[i], text_map->value[i]); + } +} + + +/*** + * NAME + * ot_span_init - + * + * ARGUMENTS + * operation_name - + * ref_type - + * ref_ctx_idx - + * ref_span - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +struct otc_span *ot_span_init(struct otc_tracer *tracer, const char *operation_name, int ref_type, int ref_ctx_idx, const struct otc_span *ref_span) +{ + struct otc_start_span_options options; + struct otc_span_context context = { .idx = ref_ctx_idx, .span = ref_span }; + struct otc_span_reference references = { ref_type, &context }; + struct otc_span *retptr = NULL; + + OT_FUNC("%p, \"%s\", %d, %d, %p", tracer, operation_name, ref_type, ref_ctx_idx, ref_span); + + (void)memset(&options, 0, sizeof(options)); + + if (IN_RANGE(ref_type, otc_span_reference_child_of, otc_span_reference_follows_from)) { + options.references = &references; + options.num_references = 1; + } + + retptr = tracer->start_span_with_options(tracer, operation_name, &options); + if (_NULL(retptr)) + OT_DBG(OT, "cannot init new span"); + else + OT_DBG(OT, "span %p:%zu initialized", retptr, retptr->idx); + + return retptr; +} + + +/*** + * NAME + * ot_span_tag - + * + * ARGUMENTS + * span - + * key - + * type - + * value - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +int ot_span_tag(struct otc_span *span, const char *key, int type, ...) +{ + va_list ap; + struct otc_value ot_value; + int retval = -1; + + OT_FUNC("%p, \"%s\", %d, ...", span, key, type); + + if (_NULL(span)) + return retval; + + va_start(ap, type); + for (retval = 0; _nNULL(key) && IN_RANGE(type, otc_value_bool, otc_value_null); retval++) { + ot_value.type = type; + if (type == otc_value_bool) + ot_value.value.bool_value = va_arg(ap, typeof(ot_value.value.bool_value)); + else if (type == otc_value_double) + ot_value.value.double_value = va_arg(ap, typeof(ot_value.value.double_value)); + else if (type == otc_value_int64) + ot_value.value.int64_value = va_arg(ap, typeof(ot_value.value.int64_value)); + else if (type == otc_value_uint64) + ot_value.value.uint64_value = va_arg(ap, typeof(ot_value.value.uint64_value)); + else if (type == otc_value_string) + ot_value.value.string_value = va_arg(ap, typeof(ot_value.value.string_value)); + else if (type == otc_value_null) + ot_value.value.string_value = va_arg(ap, typeof(ot_value.value.string_value)); + span->set_tag(span, key, &ot_value); + + if (_nNULL(key = va_arg(ap, typeof(key)))) + type = va_arg(ap, typeof(type)); + } + va_end(ap); + + return retval; +} + + +/*** + * NAME + * ot_span_set_baggage - + * + * ARGUMENTS + * span - + * key - + * value - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +int ot_span_set_baggage(struct otc_span *span, const char *key, const char *value, ...) +{ + va_list ap; + int retval = -1; + + OT_FUNC("%p, \"%s\", \"%s\", ...", span, key, value); + + if (_NULL(span)) + return retval; + + va_start(ap, value); + for (retval = 0; _nNULL(key); retval++) { + OT_DBG(OT, "set baggage: \"%s\" \"%s\"", key, value); + + span->set_baggage_item(span, key, value); + + if (_nNULL(key = va_arg(ap, typeof(key)))) + value = va_arg(ap, typeof(value)); + } + va_end(ap); + + return retval; +} + + +/*** + * NAME + * ot_span_baggage - + * + * ARGUMENTS + * span - + * key - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +struct otc_text_map *ot_span_baggage(const struct otc_span *span, const char *key, ...) +{ + va_list ap; + struct otc_text_map *retptr = NULL; + int i, n; + + OT_FUNC("%p, \"%s\", ...", span, key); + + if (_NULL(span) || _NULL(key)) + return retptr; + + va_start(ap, key); + for (n = 1; _nNULL(va_arg(ap, typeof(key))); n++); + va_end(ap); + + if (_NULL(retptr = otc_text_map_new(NULL, n))) + return retptr; + + va_start(ap, key); + for (i = 0; (i < n) && _nNULL(key); i++) { + char *value; + + if (_nNULL(value = (char *)span->baggage_item(span, key))) { + (void)otc_text_map_add(retptr, key, 0, value, 0, OTC_TEXT_MAP_DUP_KEY); + + OT_DBG(OT, "get baggage[%d]: \"%s\" -> \"%s\"", i, retptr->key[i], retptr->value[i]); + } else { + OT_DBG(OT, "get baggage[%d]: \"%s\" -> invalid key", i, key); + } + + key = va_arg(ap, typeof(key)); + } + va_end(ap); + + return retptr; +} + + +/*** + * NAME + * ot_span_log_kv - + * + * ARGUMENTS + * span - + * key - + * value - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +int ot_span_log_kv(struct otc_span *span, const char *key, const char *value, ...) +{ + va_list ap; + struct otc_log_field log_data[OTC_MAXLOGFIELDS]; + int retval = -1; + + OT_FUNC("%p, \"%s\", \"%s\", ...", span, key, value); + + if (_NULL(span) || _NULL(key) || _NULL(value)) + return retval; + + va_start(ap, value); + for (retval = 0; (retval < TABLESIZE(log_data)) && _nNULL(key); retval++) { + log_data[retval].key = key; + log_data[retval].value.type = otc_value_string; + log_data[retval].value.value.string_value = value; + + if (_nNULL(key = va_arg(ap, typeof(key)))) + value = va_arg(ap, typeof(value)); + } + va_end(ap); + + span->log_fields(span, log_data, retval); + + return retval; +} + + +/*** + * NAME + * ot_span_log - + * + * ARGUMENTS + * span - + * key - + * format - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +int ot_span_log(struct otc_span *span, const char *key, const char *format, ...) +{ + va_list ap; + char value[BUFSIZ]; + + OT_FUNC("%p, \"%s\", \"%s\", ...", span, key, format); + + if (_NULL(span) || _NULL(key) || _NULL(format)) + return -1; + + va_start(ap, format); + (void)vsnprintf(value, sizeof(value), format, ap); + va_end(ap); + + return ot_span_log_kv(span, key, value, NULL); +} + + +#ifdef OT_USE_INJECT_CB + +/*** + * NAME + * ot_text_map_writer_set_cb - + * + * ARGUMENTS + * writer - + * key - + * value - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static otc_propagation_error_code_t ot_text_map_writer_set_cb(struct otc_text_map_writer *writer, const char *key, const char *value) +{ + otc_propagation_error_code_t retval = otc_propagation_error_code_success; + + OT_FUNC("%p, \"%s\", \"%s\"", writer, key, value); + + if (otc_text_map_add(&(writer->text_map), key, 0, value, 0, OTC_TEXT_MAP_DUP_KEY | OTC_TEXT_MAP_DUP_VALUE) == -1) + retval = otc_propagation_error_code_unknown; + + return retval; +} + +#endif /* OT_USE_INJECT_CB */ + + +#ifdef OT_USE_EXTRACT_CB + +/*** + * NAME + * ot_text_map_reader_foreach_key_cb - + * + * ARGUMENTS + * reader - + * handler - + * arg - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static otc_propagation_error_code_t ot_text_map_reader_foreach_key_cb(struct otc_text_map_reader *reader, otc_propagation_error_code_t (*handler)(void *arg, const char *key, const char *value), void *arg) +{ + size_t i; + otc_propagation_error_code_t retval = otc_propagation_error_code_success; + + OT_FUNC("%p, %p, %p", reader, handler, arg); + + for (i = 0; (retval == otc_propagation_error_code_success) && (i < reader->text_map.count); i++) { + OT_DBG(OT, "\"%s\" -> \"%s\"", reader->text_map.key[i], reader->text_map.value[i]); + + retval = handler(arg, reader->text_map.key[i], reader->text_map.value[i]); + } + + return retval; +} + +#endif /* OT_USE_EXTRACT_CB */ + + +/*** + * NAME + * ot_inject_text_map - + * + * ARGUMENTS + * tracer - + * span - + * carrier - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +struct otc_span_context *ot_inject_text_map(struct otc_tracer *tracer, const struct otc_span *span, struct otc_text_map_writer *carrier) +{ + struct otc_span_context *retptr = NULL; + int rc; + + OT_FUNC("%p, %p, %p", tracer, span, carrier); + + if (_NULL(span)) + return retptr; + + if (_NULL(retptr = span->span_context((struct otc_span *)span))) + return retptr; + + (void)memset(carrier, 0, sizeof(*carrier)); +#ifdef OT_USE_INJECT_CB + carrier->set = ot_text_map_writer_set_cb; +#endif + + rc = tracer->inject_text_map(tracer, carrier, retptr); + if (rc != otc_propagation_error_code_success) { + OT_LOG(" ERROR: inject_text_map() failed: %d", rc); + + OTC_DBG_FREE(retptr); + } else { + OT_DBG(OT, "context %p: { %" PRId64 " %p %p }", retptr, retptr->idx, retptr->span, retptr->destroy); + } + + OT_DBG(OT, "carrier %p: { { %p %p %zu/%zu %hhu } %p }", carrier, carrier->text_map.key, carrier->text_map.value, carrier->text_map.count, carrier->text_map.size, carrier->text_map.is_dynamic, carrier->set); + ot_text_map_show(&(carrier->text_map)); + + return retptr; +} + + +/*** + * NAME + * ot_extract_text_map - + * + * ARGUMENTS + * tracer - + * carrier - + * text_map - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +struct otc_span_context *ot_extract_text_map(struct otc_tracer *tracer, struct otc_text_map_reader *carrier, const struct otc_text_map *text_map) +{ + struct otc_span_context *retptr = NULL; + int rc; + + OT_FUNC("%p, %p, %p", tracer, carrier, text_map); + + (void)memset(carrier, 0, sizeof(*carrier)); +#ifdef OT_USE_EXTRACT_CB + carrier->foreach_key = ot_text_map_reader_foreach_key_cb; +#endif + + if (_nNULL(text_map)) + (void)memcpy(&(carrier->text_map), text_map, sizeof(carrier->text_map)); + + OT_DBG(OT, "carrier %p: { { %p %p %zu/%zu %hhu } }", carrier, carrier->text_map.key, carrier->text_map.value, carrier->text_map.count, carrier->text_map.size, carrier->text_map.is_dynamic); + + rc = tracer->extract_text_map(tracer, carrier, &retptr); + if (rc != otc_propagation_error_code_success) { + OT_LOG(" ERROR: extract_text_map() failed: %d", rc); + + OTC_DBG_FREE(retptr); + } + else if (_nNULL(retptr)) { + OT_DBG(OT, "context %p: { %" PRId64 " %p %p }", retptr, retptr->idx, retptr->span, retptr->destroy); + } + + return retptr; +} + + +#ifdef OT_USE_INJECT_CB + +/*** + * NAME + * ot_http_headers_writer_set_cb - + * + * ARGUMENTS + * writer - + * key - + * value - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static otc_propagation_error_code_t ot_http_headers_writer_set_cb(struct otc_http_headers_writer *writer, const char *key, const char *value) +{ + otc_propagation_error_code_t retval = otc_propagation_error_code_success; + + OT_FUNC("%p, \"%s\", \"%s\"", writer, key, value); + + if (otc_text_map_add(&(writer->text_map), key, 0, value, 0, OTC_TEXT_MAP_DUP_KEY | OTC_TEXT_MAP_DUP_VALUE) == -1) + retval = otc_propagation_error_code_unknown; + + return retval; +} + +#endif /* OT_USE_INJECT_CB */ + + +#ifdef OT_USE_EXTRACT_CB + +/*** + * NAME + * ot_http_headers_reader_foreach_key_cb - + * + * ARGUMENTS + * reader - + * handler - + * arg - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static otc_propagation_error_code_t ot_http_headers_reader_foreach_key_cb(struct otc_http_headers_reader *reader, otc_propagation_error_code_t (*handler)(void *arg, const char *key, const char *value), void *arg) +{ + size_t i; + otc_propagation_error_code_t retval = otc_propagation_error_code_success; + + OT_FUNC("%p, %p, %p", reader, handler, arg); + + for (i = 0; (retval == otc_propagation_error_code_success) && (i < reader->text_map.count); i++) { + OT_DBG(OT, "\"%s\" -> \"%s\"", reader->text_map.key[i], reader->text_map.value[i]); + + retval = handler(arg, reader->text_map.key[i], reader->text_map.value[i]); + } + + return retval; +} + +#endif /* OT_USE_EXTRACT_CB */ + + +/*** + * NAME + * ot_inject_http_headers - + * + * ARGUMENTS + * tracer - + * span - + * carrier - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +struct otc_span_context *ot_inject_http_headers(struct otc_tracer *tracer, const struct otc_span *span, struct otc_http_headers_writer *carrier) +{ + struct otc_span_context *retptr = NULL; + int rc; + + OT_FUNC("%p, %p, %p", tracer, span, carrier); + + if (_NULL(span)) + return retptr; + + if (_NULL(retptr = span->span_context((struct otc_span *)span))) + return retptr; + + (void)memset(carrier, 0, sizeof(*carrier)); +#ifdef OT_USE_INJECT_CB + carrier->set = ot_http_headers_writer_set_cb; +#endif + + rc = tracer->inject_http_headers(tracer, carrier, retptr); + if (rc != otc_propagation_error_code_success) { + OT_LOG(" ERROR: inject_http_headers() failed: %d", rc); + + OTC_DBG_FREE(retptr); + } else { + OT_DBG(OT, "context %p: { %" PRId64 " %p %p }", retptr, retptr->idx, retptr->span, retptr->destroy); + } + + OT_DBG(OT, "carrier %p: { { %p %p %zu/%zu %hhu } %p }", carrier, carrier->text_map.key, carrier->text_map.value, carrier->text_map.count, carrier->text_map.size, carrier->text_map.is_dynamic, carrier->set); + ot_text_map_show(&(carrier->text_map)); + + return retptr; +} + + +/*** + * NAME + * ot_extract_http_headers - + * + * ARGUMENTS + * tracer - + * carrier - + * text_map - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +struct otc_span_context *ot_extract_http_headers(struct otc_tracer *tracer, struct otc_http_headers_reader *carrier, const struct otc_text_map *text_map) +{ + struct otc_span_context *retptr = NULL; + int rc; + + OT_FUNC("%p, %p, %p", tracer, carrier, text_map); + + (void)memset(carrier, 0, sizeof(*carrier)); +#ifdef OT_USE_EXTRACT_CB + carrier->foreach_key = ot_http_headers_reader_foreach_key_cb; +#endif + + if (_nNULL(text_map)) + (void)memcpy(&(carrier->text_map), text_map, sizeof(carrier->text_map)); + + OT_DBG(OT, "carrier %p: { { %p %p %zu/%zu %hhu } }", carrier, carrier->text_map.key, carrier->text_map.value, carrier->text_map.count, carrier->text_map.size, carrier->text_map.is_dynamic); + + rc = tracer->extract_http_headers(tracer, carrier, &retptr); + if (rc != otc_propagation_error_code_success) { + OT_LOG(" ERROR: extract_http_headers() failed: %d", rc); + + OTC_DBG_FREE(retptr); + } + else if (_nNULL(retptr)) { + OT_DBG(OT, "context %p: { %" PRId64 " %p %p }", retptr, retptr->idx, retptr->span, retptr->destroy); + } + + return retptr; +} + + +/*** + * NAME + * ot_inject_binary - + * + * ARGUMENTS + * tracer - + * span - + * carrier - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +struct otc_span_context *ot_inject_binary(struct otc_tracer *tracer, const struct otc_span *span, struct otc_custom_carrier_writer *carrier) +{ + struct otc_span_context *retptr = NULL; + int rc; + + OT_FUNC("%p, %p, %p", tracer, span, carrier); + + if (_NULL(span)) + return retptr; + + if (_NULL(retptr = span->span_context((struct otc_span *)span))) + return retptr; + + (void)memset(carrier, 0, sizeof(*carrier)); + + rc = tracer->inject_binary(tracer, carrier, retptr); + if (rc != otc_propagation_error_code_success) { + OT_LOG(" ERROR: inject_binary() failed: %d", rc); + + OTC_DBG_FREE(retptr); + } else { + OT_DBG(OT, "context %p: { %" PRId64 " %p %p }", retptr, retptr->idx, retptr->span, retptr->destroy); + } + + OT_DBG(OT, "carrier %p: { { %p %zu %hhu } %p }", carrier, carrier->binary_data.data, carrier->binary_data.size, carrier->binary_data.is_dynamic, carrier->inject); + +#ifdef DEBUG + if (carrier->binary_data.data != NULL) { + struct otc_jaeger_trace_context *ctx_jaeger = carrier->binary_data.data; + struct otc_dd_trace_context *ctx_dd = carrier->binary_data.data; + + OT_DBG(OT, "Jaeger trace context: %016" PRIx64 "%016" PRIx64 ":%016" PRIx64 ":%016" PRIx64 ":%02hhx <%s> <%s>", + ctx_jaeger->trace_id[0], ctx_jaeger->trace_id[1], ctx_jaeger->span_id, ctx_jaeger->parent_span_id, ctx_jaeger->flags, + str_hex(ctx_jaeger->baggage, carrier->binary_data.size - sizeof(*ctx_jaeger)), + str_ctrl(ctx_jaeger->baggage, carrier->binary_data.size - sizeof(*ctx_jaeger))); + OT_DBG(OT, "DataDog trace context: <%s> <%s>", + str_hex(ctx_dd->data, carrier->binary_data.size), + str_ctrl(ctx_dd->data, carrier->binary_data.size)); + } +#endif /* DEBUG */ + + return retptr; +} + + +/*** + * NAME + * ot_extract_binary - + * + * ARGUMENTS + * tracer - + * carrier - + * binary_data - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +struct otc_span_context *ot_extract_binary(struct otc_tracer *tracer, struct otc_custom_carrier_reader *carrier, const struct otc_binary_data *binary_data) +{ + struct otc_span_context *retptr = NULL; + int rc; + + OT_FUNC("%p, %p, %p", tracer, carrier, binary_data); + + (void)memset(carrier, 0, sizeof(*carrier)); + + if (_nNULL(binary_data) && _nNULL(binary_data->data) && (binary_data->size > 0)) + (void)memcpy(&(carrier->binary_data), binary_data, sizeof(carrier->binary_data)); + + OT_DBG(OT, "carrier %p: { { %p %zu %hhu } %p }", carrier, carrier->binary_data.data, carrier->binary_data.size, carrier->binary_data.is_dynamic, carrier->extract); + + rc = tracer->extract_binary(tracer, carrier, &retptr); + if (rc != otc_propagation_error_code_success) { + OT_LOG(" ERROR: extract_binary() failed: %d", rc); + + OTC_DBG_FREE(retptr); + } + else if (_nNULL(retptr)) { + OT_DBG(OT, "context %p: { %" PRId64 " %p %p }", retptr, retptr->idx, retptr->span, retptr->destroy); + } + + return retptr; +} + + +/*** + * NAME + * ot_span_finish - + * + * ARGUMENTS + * span - + * ts_finish - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +void ot_span_finish(struct otc_span **span, const struct timespec *ts_finish) +{ + struct otc_finish_span_options options; + + OT_FUNC("%p, %p", span, ts_finish); + + if (_NULL(span) || _NULL(*span)) + return; + + (void)memset(&options, 0, sizeof(options)); + + if (_nNULL(ts_finish)) + (void)memcpy(&(options.finish_time.value), ts_finish, sizeof(options.finish_time.value)); + + OT_DBG(OT, "span %p:%zu finished", *span, (*span)->idx); + + (*span)->finish_with_options(*span, &options); + + *span = NULL; +} + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/test/opentracing.h b/test/opentracing.h new file mode 100644 index 0000000..7cb4b99 --- /dev/null +++ b/test/opentracing.h @@ -0,0 +1,42 @@ +/*** + * Copyright 2020 HAProxy Technologies + * + * 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. + */ +#ifndef TEST_OPENTRACING_H +#define TEST_OPENTRACING_H + +struct otc_span *ot_span_init(struct otc_tracer *tracer, const char *operation_name, int ref_type, int ref_ctx_idx, const struct otc_span *ref_span); +int ot_span_tag(struct otc_span *span, const char *key, int type, ...); +int ot_span_set_baggage(struct otc_span *span, const char *key, const char *value, ...); +struct otc_text_map *ot_span_baggage(const struct otc_span *span, const char *key, ...); +int ot_span_log_kv(struct otc_span *span, const char *key, const char *value, ...); +int ot_span_log(struct otc_span *span, const char *key, const char *format, ...); +struct otc_span_context *ot_inject_text_map(struct otc_tracer *tracer, const struct otc_span *span, struct otc_text_map_writer *carrier); +struct otc_span_context *ot_extract_text_map(struct otc_tracer *tracer, struct otc_text_map_reader *carrier, const struct otc_text_map *text_map); +struct otc_span_context *ot_inject_http_headers(struct otc_tracer *tracer, const struct otc_span *span, struct otc_http_headers_writer *carrier); +struct otc_span_context *ot_extract_http_headers(struct otc_tracer *tracer, struct otc_http_headers_reader *carrier, const struct otc_text_map *text_map); +struct otc_span_context *ot_inject_binary(struct otc_tracer *tracer, const struct otc_span *span, struct otc_custom_carrier_writer *carrier); +struct otc_span_context *ot_extract_binary(struct otc_tracer *tracer, struct otc_custom_carrier_reader *carrier, const struct otc_binary_data *binary_data); +void ot_span_finish(struct otc_span **span, const struct timespec *ts_finish); + +#endif /* TEST_OPENTRACING_H */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/test/test.c b/test/test.c new file mode 100644 index 0000000..287e4e2 --- /dev/null +++ b/test/test.c @@ -0,0 +1,485 @@ +/*** + * Copyright 2020 HAProxy Technologies + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "include.h" + + +#define DEFAULT_DEBUG_LEVEL 0 +#define DEFAULT_THREADS_COUNT 1000 + + +typedef unsigned char bool_t; + +enum FLAG_OPT_enum { + FLAG_OPT_HELP = 0x01, + FLAG_OPT_VERSION = 0x02, +}; + +static struct { + uint8_t debug_level; + uint8_t opt_flags; + int runcount; + int runtime_ms; + int threads; + const char *ot_config; + const char *ot_plugin; + struct otc_tracer *ot_tracer; +} cfg = { + .debug_level = DEFAULT_DEBUG_LEVEL, + .runtime_ms = -1, + .threads = DEFAULT_THREADS_COUNT, +}; + +enum OT_SPAN_enum { + OT_SPAN_ROOT = 0, + OP_SPAN_BAGGAGE, + OT_SPAN_PROP_TM, + OT_SPAN_PROP_HH, + OT_SPAN_PROP_BD, + OT_SPAN_MAX, +}; + +struct worker { + pthread_t thread; + int id; + struct otc_span *ot_span[OT_SPAN_MAX]; + int ot_state; + uint64_t count; +}; + +static struct { + const char *name; + struct timeval start_time; + struct worker worker[8192]; + volatile bool_t flag_run; +} prg; + + +uint8_t *cfg_debug_level = &(cfg.debug_level); + + +/*** + * NAME + * thread_id - + * + * ARGUMENTS + * This function takes no arguments. + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +int thread_id(void) +{ + pthread_t id; + int i; + + id = pthread_self(); + + for (i = 0; i < MIN(cfg.threads, TABLESIZE(prg.worker)); i++) + if (pthread_equal(prg.worker[i].thread, id)) + return i + 1; + + return 0; +} + + +/*** + * NAME + * worker_finish_all_spans - + * + * ARGUMENTS + * worker - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static void worker_finish_all_spans(struct worker *worker) +{ + struct timespec ts; + int i; + + OT_FUNC("%p", worker); + + (void)clock_gettime(CLOCK_MONOTONIC, &ts); + + for (i = 0; i < TABLESIZE(worker->ot_span); i++) + if (_nNULL(worker->ot_span[i])) + ot_span_finish(worker->ot_span + i, &ts); +} + + +/*** + * NAME + * worker_thread - + * + * ARGUMENTS + * data - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +__attribute__((noreturn)) static void *worker_thread(void *data) +{ + struct otc_text_map_writer tm_wr; + struct otc_http_headers_writer hh_wr; + struct otc_custom_carrier_writer cc_wr; + struct otc_span_context *context; + struct timeval now; + char name[16]; + struct worker *worker = data; +#ifdef DEBUG + int n = 0; +#endif + + OT_FUNC("%p", data); + +#ifdef __linux__ + OT_DBG(WORKER, "Worker started, thread id: %" PRI_PTHREADT, syscall(SYS_gettid)); +#else + OT_DBG(WORKER, "Worker started, thread id: %" PRI_PTHREADT, worker->thread); +#endif + + (void)gettimeofday(&now, NULL); + (void)srandom(now.tv_usec); + + (void)snprintf(name, sizeof(name), "test/wrk: %d", worker->id); + (void)pthread_setname_np(worker->thread, name); + + while (!prg.flag_run) { + nsleep(0, 10000000); + +#ifdef DEBUG + n++; +#endif + } + + OT_DBG(DEBUG, "waiting loop count: %d", n); + + for ( ; 1; worker->ot_state++) { + if (worker->ot_state != 0) + /* Do nothing. */; + else if ((cfg.runtime_ms > 0) && (TIMEVAL_DIFF_MS(&now, &(prg.start_time)) >= (uint)cfg.runtime_ms)) + break; + else if ((cfg.runcount > 0) && ((uint)cfg.runcount <= worker->count)) + break; + + if (worker->ot_state == 0) { + worker->ot_span[OT_SPAN_ROOT] = ot_span_init(cfg.ot_tracer, "root span", -1, -1, NULL); + } + else if (worker->ot_state == 1) { + worker->ot_span[OP_SPAN_BAGGAGE] = ot_span_init(cfg.ot_tracer, "span #1", otc_span_reference_child_of, -1, worker->ot_span[OT_SPAN_ROOT]); + } + else if (worker->ot_state == 2) { + (void)ot_span_set_baggage(worker->ot_span[OP_SPAN_BAGGAGE], "baggage_1", "value_1", "baggage_2", "value_2", NULL); + } + else if (worker->ot_state == 3) { + if (_nNULL(context = ot_inject_text_map(cfg.ot_tracer, worker->ot_span[OP_SPAN_BAGGAGE], &tm_wr))) + context->destroy(&context); + } + else if (worker->ot_state == 4) { + struct otc_text_map_reader tm_rd; + struct otc_text_map *text_map = &(tm_wr.text_map); + + if (_nNULL(context = ot_extract_text_map(cfg.ot_tracer, &tm_rd, text_map))) { + worker->ot_span[OT_SPAN_PROP_TM] = ot_span_init(cfg.ot_tracer, "text map propagation", otc_span_reference_child_of, context->idx, NULL); + context->destroy(&context); + } + otc_text_map_destroy(&text_map, OTC_TEXT_MAP_FREE_KEY | OTC_TEXT_MAP_FREE_VALUE); + } + else if (worker->ot_state == 5) { + struct otc_text_map *baggage = ot_span_baggage(worker->ot_span[OP_SPAN_BAGGAGE], "baggage_1", "baggage_2", NULL); + + otc_text_map_destroy(&baggage, OTC_TEXT_MAP_FREE_KEY | OTC_TEXT_MAP_FREE_VALUE); + } + else if (worker->ot_state == 6) { + if (_nNULL(context = ot_inject_http_headers(cfg.ot_tracer, worker->ot_span[OP_SPAN_BAGGAGE], &hh_wr))) + context->destroy(&context); + } + else if (worker->ot_state == 7) { + struct otc_http_headers_reader hh_rd; + struct otc_text_map *text_map = &(hh_wr.text_map); + + if (_nNULL(context = ot_extract_http_headers(cfg.ot_tracer, &hh_rd, text_map))) { + worker->ot_span[OT_SPAN_PROP_HH] = ot_span_init(cfg.ot_tracer, "http headers propagation", otc_span_reference_child_of, context->idx, NULL); + context->destroy(&context); + } + otc_text_map_destroy(&text_map, OTC_TEXT_MAP_FREE_KEY | OTC_TEXT_MAP_FREE_VALUE); + } + else if (worker->ot_state == 8) { + if (_nNULL(context = ot_inject_binary(cfg.ot_tracer, worker->ot_span[OP_SPAN_BAGGAGE], &cc_wr))) + context->destroy(&context); + } + else if (worker->ot_state == 9) { + struct otc_custom_carrier_reader cc_rd; + struct otc_binary_data *binary_data = &(cc_wr.binary_data); + + if (_nNULL(context = ot_extract_binary(cfg.ot_tracer, &cc_rd, binary_data))) { + worker->ot_span[OT_SPAN_PROP_BD] = ot_span_init(cfg.ot_tracer, "binary data propagation", otc_span_reference_child_of, context->idx, NULL); + context->destroy(&context); + } + otc_binary_data_destroy(&binary_data); + } + else if (worker->ot_state == 10) { + (void)ot_span_tag(worker->ot_span[OT_SPAN_ROOT], "tag_1", OT_VARGS(string, "value_1"), "tag_2", OT_VARGS(string, "value_2"), NULL); + } + else if (worker->ot_state == 11) { + (void)ot_span_log_kv(worker->ot_span[OT_SPAN_PROP_TM], "log_1", "content_1", "log_2", "content_2", NULL); + } + else { + worker_finish_all_spans(worker); + + worker->ot_state = -1; + worker->count++; + } + + nsleep(0, ((random() % 100) + 1) * 10000); + (void)gettimeofday(&now, NULL); + } + + pthread_exit(NULL); +} + + +/*** + * NAME + * worker_run - + * + * ARGUMENTS + * This function takes no arguments. + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +static int worker_run(void) +{ + struct timeval now; + char ot_infbuf[BUFSIZ]; + uint64_t total_count = 0; + int i, num_threads = 0, retval = EX_OK; + + OT_FUNC(""); + + (void)pthread_setname_np(pthread_self(), "test/wrk: main"); + + for (i = 0; i < cfg.threads; i++) { + prg.worker[i].id = i + 1; + + if (pthread_create(&(prg.worker[i].thread), NULL, worker_thread, prg.worker + i) != 0) + (void)fprintf(stderr, "ERROR: Failed to start thread for worker %d: %m\n", prg.worker[i].id); + else + num_threads++; + } + + prg.flag_run = 1; + + (void)gettimeofday(&now, NULL); + OT_DBG(WORKER, "%d threads started in %llu ms", num_threads, TIMEVAL_DIFF_MS(&now, &(prg.start_time))); + + for (i = 0; i < cfg.threads; i++) { + if (pthread_join(prg.worker[i].thread, NULL) != 0) + (void)fprintf(stderr, "ERROR: Failed to join worker thread %d: %m\n", prg.worker[i].id); + else + OT_LOG("worker %d count: %" PRIu64, i, prg.worker[i].count); + + total_count += prg.worker[i].count; + } + + OT_LOG("%d worker(s) total count: %" PRIu64, cfg.threads, total_count); + + cfg.ot_tracer->close(cfg.ot_tracer); + + otc_statistics(ot_infbuf, sizeof(ot_infbuf)); + OT_LOG("OpenTracing statistics: %s", ot_infbuf); + + return retval; +} + + +/*** + * NAME + * usage - + * + * ARGUMENTS + * program_name - + * flag_verbose - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +static void usage(const char *program_name, bool_t flag_verbose) +{ + (void)printf("\nUsage: %s { -h --help }\n", program_name); + (void)printf(" %s { -V --version }\n", program_name); + (void)printf(" %s { [ -R --runcount=VALUE ] | [ -r --runtime=TIME ] } [OPTION]...\n\n", program_name); + + if (flag_verbose) { + (void)printf("Options are:\n"); + (void)printf(" -c, --config=FILE Specify the configuration for the used tracer.\n"); +#ifdef DEBUG + (void)printf(" -d, --debug=LEVEL Enable and specify the debug mode level (default: %d).\n", DEFAULT_DEBUG_LEVEL); +#endif + (void)printf(" -h, --help Show this text.\n"); + (void)printf(" -p, --plugin=FILE Specify the OpenTracing compatible plugin library.\n"); + (void)printf(" -R, --runcount=VALUE Execute this program a certain number of passes (0 = unlimited).\n"); + (void)printf(" -r, --runtime=TIME Run this program for a certain amount of time (ms, 0 = unlimited).\n"); + (void)printf(" -t, --threads=VALUE Specify the number of threads (default: %d).\n", DEFAULT_THREADS_COUNT); + (void)printf(" -V, --version Show program version.\n\n"); + (void)printf("Copyright 2020 HAProxy Technologies\n"); + (void)printf("SPDX-License-Identifier: Apache-2.0\n\n"); + } else { + (void)printf("For help type: %s -h\n\n", program_name); + } +} + + +int main(int argc, char **argv) +{ + static const struct option longopts[] = { + { "config", required_argument, NULL, 'c' }, +#ifdef DEBUG + { "debug", required_argument, NULL, 'd' }, +#endif + { "help", no_argument, NULL, 'h' }, + { "plugin", required_argument, NULL, 'p' }, + { "runcount", required_argument, NULL, 'R' }, + { "runtime", required_argument, NULL, 'r' }, + { "threads", required_argument, NULL, 't' }, + { "version", no_argument, NULL, 'V' }, + { NULL, 0, NULL, 0 } + }; +#ifdef OTC_DBG_MEM + static struct otc_dbg_mem_data dbg_mem_data[1000000]; + struct otc_dbg_mem dbg_mem; +#endif + const char *shortopts = "c:d:hp:R:r:t:V"; + struct timeval now; + int c, longopts_idx = -1, retval = EX_OK; + bool_t flag_error = 0; + char ot_errbuf[BUFSIZ]; + + (void)gettimeofday(&(prg.start_time), NULL); + + prg.name = basename(argv[0]); + +#ifdef OTC_DBG_MEM + retval = otc_dbg_mem_init(&dbg_mem, dbg_mem_data, TABLESIZE(dbg_mem_data), 0xff); + if (retval == -1) { + (void)fprintf(stderr, "ERROR: cannot initialize memory debugger\n"); + + return retval; + } +#endif + + while ((c = getopt_long(argc, argv, shortopts, longopts, &longopts_idx)) != EOF) { + if (c == 'c') + cfg.ot_config = optarg; +#ifdef DEBUG + else if (c == 'd') + cfg.debug_level = atoi(optarg) & UINT8_C(0xff); +#endif + else if (c == 'h') + cfg.opt_flags |= FLAG_OPT_HELP; + else if (c == 'p') + cfg.ot_plugin = optarg; + else if (c == 'R') + cfg.runcount = atoi(optarg); + else if (c == 'r') + cfg.runtime_ms = atoi(optarg); + else if (c == 't') + cfg.threads = atoi(optarg); + else if (c == 'V') + cfg.opt_flags |= FLAG_OPT_VERSION; + else + retval = EX_USAGE; + } + + if (cfg.opt_flags & FLAG_OPT_HELP) { + usage(prg.name, 1); + } + else if (cfg.opt_flags & FLAG_OPT_VERSION) { + (void)printf("\n%s v%s [build %d] by %s, %s\n\n", prg.name, PACKAGE_VERSION, PACKAGE_BUILD, PACKAGE_AUTHOR, __DATE__); + } + else { + if ((cfg.runcount < 0) && (cfg.runtime_ms < 0)) { + (void)fprintf(stderr, "ERROR: run count/time value not set\n"); + flag_error = 1; + } + + if (!IN_RANGE(cfg.threads, 1, TABLESIZE(prg.worker))) { + (void)fprintf(stderr, "ERROR: invalid number of threads '%d'\n", cfg.threads); + flag_error = 1; + } + + if (_NULL(cfg.ot_plugin) || _NULL(cfg.ot_config)) { + (void)fprintf(stderr, "ERROR: the OpenTracing configuration not set\n"); + flag_error = 1; + } + + if (flag_error) + usage(prg.name, 0); + } + + cfg_debug_level = &(cfg.debug_level); + + OT_FUNC("%d, %p", argc, argv); + + if (flag_error || (cfg.opt_flags & (FLAG_OPT_HELP | FLAG_OPT_VERSION))) + return flag_error ? EX_USAGE : EX_OK; + + if (_NULL(cfg.ot_tracer = otc_tracer_load(cfg.ot_plugin, ot_errbuf, sizeof(ot_errbuf)))) { + (void)fprintf(stderr, "ERROR: %s\n", (*ot_errbuf == '\0') ? "Unable to load tracing library" : ot_errbuf); + + retval = EX_SOFTWARE; + } + else if (otc_tracer_start(cfg.ot_config, NULL, ot_errbuf, sizeof(ot_errbuf)) == -1) { + (void)fprintf(stderr, "ERROR: %s\n", (*ot_errbuf == '\0') ? "Unable to start tracing" : ot_errbuf); + + retval = EX_SOFTWARE; + } + else { + retval = worker_run(); + } + + (void)gettimeofday(&now, NULL); + OT_DBG(INFO, "Program runtime: %llu ms", TIMEVAL_DIFF_MS(&now, &(prg.start_time))); + + OTC_DBG_MEMINFO(); + + return retval; +} + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/test/util.c b/test/util.c new file mode 100644 index 0000000..91c1af7 --- /dev/null +++ b/test/util.c @@ -0,0 +1,118 @@ +/*** + * Copyright 2020 HAProxy Technologies + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include "include.h" + + +/*** + * NAME + * nsleep - + * + * ARGUMENTS + * sec - + * nsec - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * This function does not return a value. + */ +void nsleep (uint sec, uint nsec) +{ + struct timespec ts1 = { sec, nsec }, ts2, *req = &ts1, *rem = &ts2; + + while ((nanosleep (req, rem) == -1) && (errno == EINTR)) + SWAP(req, rem); +} + + +/*** + * NAME + * str_hex - + * + * ARGUMENTS + * data - + * size - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +const char *str_hex(const void *data, size_t size) +{ + static __thread char retbuf[BUFSIZ]; + const uint8_t *ptr = data; + size_t i; + + if (_NULL(data)) + return "(null)"; + else if (size == 0) + return "()"; + + for (i = 0, size <<= 1; (i < (sizeof(retbuf) - 2)) && (i < size); ptr++) { + retbuf[i++] = NIBBLE_TO_HEX(*ptr >> 4); + retbuf[i++] = NIBBLE_TO_HEX(*ptr & 0x0f); + } + + retbuf[i] = '\0'; + + return retbuf; +} + + +/*** + * NAME + * str_ctrl - + * + * ARGUMENTS + * data - + * size - + * + * DESCRIPTION + * - + * + * RETURN VALUE + * - + */ +const char *str_ctrl(const void *data, size_t size) +{ + static __thread char retbuf[BUFSIZ]; + const uint8_t *ptr = data; + size_t i, n = 0; + + if (_NULL(data)) + return "(null)"; + else if (size == 0) + return "()"; + + for (i = 0; (n < (sizeof(retbuf) - 1)) && (i < size); i++) + retbuf[n++] = IN_RANGE(ptr[i], 0x20, 0x7e) ? ptr[i] : '.'; + + retbuf[n] = '\0'; + + return retbuf; +} + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ diff --git a/test/util.h b/test/util.h new file mode 100644 index 0000000..689a042 --- /dev/null +++ b/test/util.h @@ -0,0 +1,33 @@ +/*** + * Copyright 2020 HAProxy Technologies + * + * 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. + */ +#ifndef TEST_UTIL_H +#define TEST_UTIL_H + +int thread_id(void); +void nsleep (uint sec, uint nsec); +const char *str_hex(const void *data, size_t size); +const char *str_ctrl(const void *data, size_t size); + +#endif /* TEST_UTIL_H */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + * + * vi: noexpandtab shiftwidth=8 tabstop=8 + */ |