From 7fd5bef36ee1052571a50e296ee9853757558d29 Mon Sep 17 00:00:00 2001 From: Max Golovanov Date: Tue, 7 Jan 2020 23:12:01 -0800 Subject: [PATCH 01/15] Initial commit of changes to simplify creation of shared library --- CMakeLists.txt | 2 +- build.sh | 101 +++++++++++++++++--------- examples/cpp/SampleCpp/CMakeLists.txt | 27 ++++--- install.sh | 2 +- lib/CMakeLists.txt | 25 ++++++- tools/MakeDeb.cmake | 2 +- tools/MakeRpm.cmake | 2 +- tools/MakeTgz.cmake | 6 +- 8 files changed, 112 insertions(+), 55 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5dc14bf6a..b035caa66 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -191,7 +191,7 @@ option(BUILD_OBJC_WRAPPER "Build Obj-C wrapper" YES) option(BUILD_PACKAGE "Build package" YES) if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") - option(BUILD_APPLE_HTTP "Build Apple HTTP client" YES) + option(BUILD_APPLE_HTTP "Build Apple HTTP client" NO) endif() if(BUILD_UNIT_TESTS OR BUILD_FUNC_TESTS) diff --git a/build.sh b/build.sh index d345b8387..8e6ec296f 100755 --- a/build.sh +++ b/build.sh @@ -1,6 +1,7 @@ #!/bin/bash - export PATH=/usr/local/bin:$PATH +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +cd $DIR if [[ ! -z "${GIT_PULL_TOKEN}" ]]; then rm -rf lib/modules @@ -12,27 +13,59 @@ if [[ ! -z "${GIT_PULL_TOKEN}" ]]; then git clone https://${GIT_PULL_TOKEN}:x-oauth-basic@github.com/microsoft/cpp_client_telemetry_modules.git lib/modules fi -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -echo "Current directory: $DIR" -cd $DIR - export NOROOT=$NOROOT if [ "$1" == "clean" ]; then - rm -f CMakeCache.txt *.cmake - rm -rf out - rm -rf .buildtools -# make clean + rm -f CMakeCache.txt *.cmake + rm -rf out + rm -rf .buildtools + # make clean fi +# Don't elevate, don't deploy package if [ "$1" == "noroot" ] || [ "$2" == "noroot" ]; then -export NOROOT=true + export NOROOT=true fi +# Build release bits if [ "$1" == "release" ] || [ "$2" == "release" ]; then -BUILD_TYPE="Release" + BUILD_TYPE="Release" else -BUILD_TYPE="Debug" + BUILD_TYPE="Debug" +fi + +LINK_TYPE= +CMAKE_OPTS="${CMAKE_OPTS:-DBUILD_SHARED_LIBS=OFF}" +while getopts "h?vl:D:" opt; do + case "$opt" in + h|\?) + echo "Usage: build.sh [clean] [noroot] [release] [-h|-?] [-l (static|shared)] [-D CMAKE_OPTION]" + echo " " + echo "options: " + echo " " + echo " -h | -? - this help. " + echo " -l [static|shared] - build static (default) or shared library. " + echo " -D [CMAKE_OPTION] - additional option to pass to cmake. " + echo " " + echo "cmake options can be passed using CMAKE_OPTS environment variable. " + echo " " + exit 0 + ;; + :) echo "Invalid option: $OPTARG requires an argument" 1>&2 + exit 0 + ;; + v) verbose=1 + ;; + D) CMAKE_OPTS="$CMAKE_OPTS -D$OPTARG" + ;; + l) LINK_TYPE=$OPTARG + ;; + esac +done +shift $((OPTIND -1)) + +if [ "$LINK_TYPE" == "shared" ]; then + CMAKE_OPTS="$CMAKE_OPTS -DBUILD_SHARED_LIBS=ON" fi # Set target MacOS minver @@ -42,21 +75,21 @@ export MACOSX_DEPLOYMENT_TARGET=10.10 FILE=.buildtools OS_NAME=`uname -a` if [ ! -f $FILE ]; then -case "$OS_NAME" in - *Darwin*) tools/setup-buildtools-mac.sh ;; - *Linux*) [[ -z "$NOROOT" ]] && sudo tools/setup-buildtools.sh || echo "No root: skipping build tools installation." ;; - *) echo "WARNING: unsupported OS $OS_NAME , skipping build tools installation.." -esac -# Assume that the build tools have been successfully installed -echo > $FILE + case "$OS_NAME" in + *Darwin*) tools/setup-buildtools-mac.sh ;; + *Linux*) [[ -z "$NOROOT" ]] && sudo tools/setup-buildtools.sh || echo "No root: skipping build tools installation." ;; + *) echo "WARNING: unsupported OS $OS_NAME , skipping build tools installation." ;; + esac + # Assume that the build tools have been successfully installed + echo > $FILE fi if [ -f /usr/bin/gcc ]; then -echo "gcc version: `gcc --version`" + echo "gcc version: `gcc --version`" fi if [ -f /usr/bin/clang ]; then -echo "clang version: `clang --version`" + echo "clang version: `clang --version`" fi # Skip Version.hpp changes @@ -71,21 +104,19 @@ CMAKE_PACKAGE_TYPE=tgz # .deb package if [ -f /usr/bin/dpkg ]; then -export CMAKE_PACKAGE_TYPE=deb + export CMAKE_PACKAGE_TYPE=deb fi # .rpm package if [ -f /usr/bin/rpmbuild ]; then -export CMAKE_PACKAGE_TYPE=rpm + export CMAKE_PACKAGE_TYPE=rpm fi - # Fail on error set -e - # TODO: pass custom build flags? -cmake -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PACKAGE_TYPE=$CMAKE_PACKAGE_TYPE .. +cmake -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DCMAKE_PACKAGE_TYPE=$CMAKE_PACKAGE_TYPE $CMAKE_OPTS .. # TODO: strip symbols to minimize (release-only) # Build all @@ -104,13 +135,13 @@ make package # Debian / Ubuntu / Raspbian if [ -f /usr/bin/dpkg ]; then -# Install new package -[[ -z "$NOROOT" ]] && sudo dpkg -i *.deb || echo "No root: skipping package deployment." + # Install new package + [[ -z "$NOROOT" ]] && sudo dpkg -i *.deb || echo "No root: skipping package deployment." fi # RedHat / CentOS if [ -f /usr/bin/rpmbuild ]; then -[[ -z "$NOROOT" ]] && sudo rpm -i --force -v *.rpm || echo "No root: skipping package deployment." + [[ -z "$NOROOT" ]] && sudo rpm -i --force -v *.rpm || echo "No root: skipping package deployment." fi # Install SDK headers and lib to /usr/local @@ -120,10 +151,10 @@ fi ## strip -S --strip-unneeded --remove-section=.note.gnu.gold-version --remove-section=.comment --remove-section=.note --remove-section=.note.gnu.build-id --remove-section=.note.ABI-tag out/lib/libmat.so if [ "$CMAKE_PACKAGE_TYPE" == "tgz" ]; then -cd .. -MATSDK_INSTALL_DIR="${MATSDK_INSTALL_DIR:-/usr/local}" -echo "+-----------------------------------------------------------------------------------+" -echo " This step may prompt for your sudo password to deploy SDK to $MATSDK_INSTALL_DIR " -echo "+-----------------------------------------------------------------------------------+" -[[ -z "$NOROOT" ]] && sudo ./install.sh $MATSDK_INSTALL_DIR || echo "No root: skipping package deployment." + cd .. + MATSDK_INSTALL_DIR="${MATSDK_INSTALL_DIR:-/usr/local}" + echo "+-----------------------------------------------------------------------------------+" + echo " This step may prompt for your sudo password to deploy SDK to $MATSDK_INSTALL_DIR " + echo "+-----------------------------------------------------------------------------------+" + [[ -z "$NOROOT" ]] && sudo ./install.sh $MATSDK_INSTALL_DIR || echo "No root: skipping package deployment." fi diff --git a/examples/cpp/SampleCpp/CMakeLists.txt b/examples/cpp/SampleCpp/CMakeLists.txt index 46c1c4013..fbaeab3de 100644 --- a/examples/cpp/SampleCpp/CMakeLists.txt +++ b/examples/cpp/SampleCpp/CMakeLists.txt @@ -4,24 +4,27 @@ project(SampleCpp) # Uncomment for building i386 binary on x86_64 system #set(CMAKE_SYSTEM_PROCESSOR i386) +if (NOT TARGET_ARCH) + set(TARGET_ARCH ${CMAKE_SYSTEM_PROCESSOR}) +endif() + # For ARM / Raspberry Pi 3 cross-compile # set(MAT_SDK_LIB /usr/local/lib/armv7l-linux-gnu) -# Point example to SDK dirs for x86_64 Desktop -if(EXISTS "/usr/local/lib/libmat.a") -# Use local libmat.a -set(MAT_SDK_LIB /usr/local/lib/) -else() -# Use architecture-specific libmat.a -set(MAT_SDK_LIB /usr/local/lib/${CMAKE_SYSTEM_PROCESSOR}-linux-gnu) +# Search for static lib first +find_path(MAT_SDK_LIB_PATH NAMES libmat.a PATHS /usr/lib /usr/local/lib /usr/local/lib/${TARGET_ARCH}-linux-gnu) +if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") + # Search for .dylib on Mac OS X + find_path(MAT_SDK_DYLIB_PATH NAMES libmat.dylib PATHS /usr/lib /usr/local/lib) endif() +find_library(LIBMAT NAMES libmat.a libmat.dylib HINTS ${MAT_SDK_LIB_PATH} ${MAT_SDK_DYLIB_PATH}) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O0 -ggdb -gdwarf-2 -std=c11") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -ggdb -gdwarf-2 -std=c++11") find_package (Threads) -set(MAT_SDK_INCLUDE /usr/local/include/mat) +set(MAT_SDK_INCLUDE /usr/local/include/mat) # Aria SDK to include dirs include_directories( . ${MAT_SDK_INCLUDE} ) @@ -32,9 +35,9 @@ source_group(" " REGULAR_EXPRESSION "") # Prefer linking to more recent local sqlite3 if(EXISTS "/usr/local/lib/libsqlite3.a") -set (SQLITE3_LIB "/usr/local/lib/libsqlite3.a") + set (SQLITE3_LIB "/usr/local/lib/libsqlite3.a") else() -set (SQLITE3_LIB "sqlite3") + set (SQLITE3_LIB "sqlite3") endif() set (PLATFORM_LIBS "") @@ -51,4 +54,6 @@ endif() #tcmalloc turned off by default #target_link_libraries(SampleCpp ${MAT_SDK_LIB}/libmat.a curl z ${CMAKE_THREAD_LIBS_INIT} ${SQLITE3_LIB} dl tcmalloc) -target_link_libraries(SampleCpp ${MAT_SDK_LIB}/libmat.a curl z ${CMAKE_THREAD_LIBS_INIT} ${SQLITE3_LIB} ${PLATFORM_LIBS} dl) +# Static: +# target_link_libraries(SampleCpp ${MAT_SDK_LIB_STATIC} curl z ${CMAKE_THREAD_LIBS_INIT} ${SQLITE3_LIB} ${PLATFORM_LIBS} dl) +target_link_libraries(SampleCpp ${LIBMAT} curl z ${CMAKE_THREAD_LIBS_INIT} ${SQLITE3_LIB} ${PLATFORM_LIBS} dl) diff --git a/install.sh b/install.sh index ef390b49e..db0fa0f38 100755 --- a/install.sh +++ b/install.sh @@ -2,6 +2,6 @@ MATSDK_INSTALL_DIR=$1 echo "Install SDK to $MATSDK_INSTALL_DIR" mkdir -p $MATSDK_INSTALL_DIR/lib/mat -cp out/lib/libmat.a $MATSDK_INSTALL_DIR/lib +cp out/lib/libmat.* $MATSDK_INSTALL_DIR/lib mkdir -p $MATSDK_INSTALL_DIR/include/mat cp lib/include/public/* $MATSDK_INSTALL_DIR/include/mat diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index b9134d5d7..a487eb061 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -173,11 +173,30 @@ endif() if(BUILD_SHARED_LIBS STREQUAL "ON") message("-- Building shared SDK library") + + # include(FindCURL) + # find_package(CURL REQUIRED) + # set(CMAKE_REQUIRED_LIBRARIES "${CURL_LIBRARIES}") + + # find_package(sqlite3 REQUIRED) + add_library(mat SHARED ${SRCS}) - add_library(sqlite3 SHARED IMPORTED GLOBAL) - add_library(z SHARED IMPORTED GLOBAL) + add_library(sqlite3 STATIC IMPORTED GLOBAL) + # add_library(sqlite3 SHARED IMPORTED GLOBAL) + # add_library(z STATIC IMPORTED GLOBAL) + # add_library(z SHARED IMPORTED GLOBAL) + # add_library(curl SHARED IMPORTED GLOBAL) + + # Add flags for obtaining system UUID via IOKit + if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(CMAKE_SHARED_LINKER_FLAGS + "-framework CoreFoundation -framework IOKit -framework Foundation" + ) + endif() + #target_link_libraries(mat PUBLIC libsqlite3.so libz.so ${LIBS} "${CMAKE_THREAD_LIBS_INIT}" "${CMAKE_DL_LIBS}" "${Tcmalloc_LIBRARIES}") - target_link_libraries(mat PUBLIC libsqlite3.so libz.so ${LIBS} "${CMAKE_THREAD_LIBS_INIT}" "${CMAKE_DL_LIBS}") + target_link_libraries(mat PRIVATE libsqlite3.a PUBLIC z ${LIBS} "${CMAKE_THREAD_LIBS_INIT}" "${CMAKE_DL_LIBS}" "${CMAKE_REQUIRED_LIBRARIES}") + # target_link_libraries(mat PUBLIC libsqlite3 libcurl.a libz.a libssl.a libcrypto.a "${SQLITE_LIBRARY}" "${CMAKE_THREAD_LIBS_INIT}" "${CMAKE_DL_LIBS}" ) install(TARGETS mat EXPORT mat LIBRARY DESTINATION ${INSTALL_LIB_DIR}) else() diff --git a/tools/MakeDeb.cmake b/tools/MakeDeb.cmake index 28e6301d4..7a30ff09c 100644 --- a/tools/MakeDeb.cmake +++ b/tools/MakeDeb.cmake @@ -10,7 +10,7 @@ set(CPACK_PACKAGING_INSTALL_PREFIX "/usr/local") set(CPACK_GENERATOR "DEB") set(MAJOR_VERSION "3") -set(MINOR_VERSION "2") +set(MINOR_VERSION "3") string(TIMESTAMP DAYNUMBER "%j") set(PATCH_VERSION "${DAYNUMBER}") diff --git a/tools/MakeRpm.cmake b/tools/MakeRpm.cmake index 47e2f6fba..8b45f4150 100644 --- a/tools/MakeRpm.cmake +++ b/tools/MakeRpm.cmake @@ -8,7 +8,7 @@ set(CPACK_RPM_PACKAGE_DESCRIPTION_SUMMARY "Microsoft Applications Telemetry SDK set(CPACK_RPM_PACKAGE_CONTACT "1ds.sdk.cpp@service.microsoft.com") set(MAJOR_VERSION "3") -set(MINOR_VERSION "2") +set(MINOR_VERSION "3") string(TIMESTAMP DAYNUMBER "%j") set(PATCH_VERSION "${DAYNUMBER}") diff --git a/tools/MakeTgz.cmake b/tools/MakeTgz.cmake index 058b00340..8f0b8502e 100644 --- a/tools/MakeTgz.cmake +++ b/tools/MakeTgz.cmake @@ -7,7 +7,7 @@ set(CPACK_PACKAGING_INSTALL_PREFIX "/usr/local") set(CPACK_GENERATOR "TGZ") set(MAJOR_VERSION "3") -set(MINOR_VERSION "2") +set(MINOR_VERSION "3") string(TIMESTAMP DAYNUMBER "%j") set(PATCH_VERSION "${DAYNUMBER}") @@ -19,7 +19,9 @@ set(CPACK_PACKAGE_VERSION_MAJOR "${MAJOR_VERSION}") set(CPACK_PACKAGE_VERSION_MINOR "${MINOR_VERSION}") set(CPACK_PACKAGE_VERSION_PATCH "${PATCH_VERSION}") -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/lib/libmat.a DESTINATION lib COMPONENT libraries) +file(GLOB ALL_TARGET_LIBS "${CMAKE_CURRENT_BINARY_DIR}/lib/libmat.*") + +install(FILES ${ALL_TARGET_LIBS} DESTINATION lib COMPONENT libraries) #install(FILES ${MAT_SDK_INC_DIR}/*.* DESTINATION include/mat COMPONENT libraries) # FIXME: add architecture name in file name From 7a99957059e116ce46dc2958d5e71775596fd230 Mon Sep 17 00:00:00 2001 From: Max Golovanov Date: Wed, 8 Jan 2020 02:20:56 -0800 Subject: [PATCH 02/15] Add netcore30 wrapper example on top of shared lib --- lib/include/public/mat.h | 8 +- wrappers/netcore/EventSender.csproj | 12 +++ wrappers/netcore/Program.cs | 126 ++++++++++++++++++++++++++++ wrappers/netcore/appsettings.json | 18 ++++ wrappers/netcore/run.sh | 11 +++ wrappers/netcore/sdk-config.json | 46 ++++++++++ 6 files changed, 220 insertions(+), 1 deletion(-) create mode 100644 wrappers/netcore/EventSender.csproj create mode 100644 wrappers/netcore/Program.cs create mode 100644 wrappers/netcore/appsettings.json create mode 100755 wrappers/netcore/run.sh create mode 100644 wrappers/netcore/sdk-config.json diff --git a/lib/include/public/mat.h b/lib/include/public/mat.h index 0a9d2f02e..482568c23 100644 --- a/lib/include/public/mat.h +++ b/lib/include/public/mat.h @@ -14,6 +14,12 @@ #include #endif +#ifdef __clang__ +#define PACKED_STRUCT __attribute__((packed)) +#else +#define PACKED_STRUCT +#endif + #if (_MSC_VER == 1500) || (_MSC_VER == 1600) /* Visual Studio 2010 */ typedef __int64 int64_t; @@ -114,7 +120,7 @@ extern "C" { typedef int32_t evt_status_t; typedef struct evt_event evt_event; - typedef struct + typedef struct PACKED_STRUCT { evt_call_t call; /* In */ evt_handle_t handle; /* In / Out */ diff --git a/wrappers/netcore/EventSender.csproj b/wrappers/netcore/EventSender.csproj new file mode 100644 index 000000000..059cc350c --- /dev/null +++ b/wrappers/netcore/EventSender.csproj @@ -0,0 +1,12 @@ + + + Exe + netcoreapp3.1 + + + + + + + + diff --git a/wrappers/netcore/Program.cs b/wrappers/netcore/Program.cs new file mode 100644 index 000000000..4450893d8 --- /dev/null +++ b/wrappers/netcore/Program.cs @@ -0,0 +1,126 @@ +using System; +using System.Runtime.InteropServices; +using System.Text; +using System.IO; +using System.Json; + +namespace EventSender +{ + public enum EventCallType + { + EVT_OP_LOAD = 0x00000001, + EVT_OP_UNLOAD = 0x00000002, + EVT_OP_OPEN = 0x00000003, + EVT_OP_CLOSE = 0x00000004, + EVT_OP_CONFIG = 0x00000005, + EVT_OP_LOG = 0x00000006, + EVT_OP_PAUSE = 0x00000007, + EVT_OP_RESUME = 0x00000008, + EVT_OP_UPLOAD = 0x00000009, + EVT_OP_FLUSH = 0x0000000A, + EVT_OP_VERSION = 0x0000000B, + EVT_OP_OPEN_WITH_PARAMS = 0x0000000C, + EVT_OP_MAX = EVT_OP_OPEN_WITH_PARAMS + 1 + } + + // [StructLayout(LayoutKind.Sequential)] + [StructLayout(LayoutKind.Explicit, Size=32, CharSet=CharSet.Ansi)] + public struct EventCallContext + { + [FieldOffset(0)]public UInt32 call; + [FieldOffset(4)]public ulong handle; + [FieldOffset(12)]public IntPtr data; + [FieldOffset(16)]public UInt32 result; + [FieldOffset(20)]public UInt32 size; + } + + internal static class NativeMethods + { + [DllImport("libmat", EntryPoint = "evt_api_call_default" /* , CallingConvention = CallingConvention.Cdecl */)] + public static extern UInt32 evt_api_call([In, Out] ref EventCallContext context); + // public static extern UInt32 evt_api_call(ref EventCallContext context); + // Alternatives: + // [MarshalAs(UnmanagedType.LPStruct)] + } + + class Program + { + public const string SDK_VERSION = "3.3.0-netcore"; + + static string ReadConfiguration(string filename) + { + string result = ""; + using (StreamReader sr = new StreamReader(filename)) + { + result = sr.ReadToEnd(); + } + return result; + } + + static ulong evt_open(string cfg) + { + /* + byte[] data = Encoding.ASCII.GetBytes(cfg); + var nativeDataPtr = Marshal.AllocHGlobal(data.Length+1); + Marshal.Copy(data, 0, nativeDataPtr, data.Length); + Marshal.WriteByte(nativeDataPtr+data.Length, 0); + */ + var nativeDataPtr = Marshal.StringToHGlobalAnsi(cfg); + EventCallContext context = new EventCallContext + { + call = (Byte)EventCallType.EVT_OP_OPEN, + data = nativeDataPtr + }; + NativeMethods.evt_api_call(ref context); + // Marshal.Release(nativeDataPtr); + return context.handle; + } + + static ulong evt_close(ulong inHandle) + { + EventCallContext context = new EventCallContext + { + call = (Byte)EventCallType.EVT_OP_CLOSE, + handle = inHandle + }; + return NativeMethods.evt_api_call(ref context); + } + + static string evt_version() + { + byte[] data = Encoding.ASCII.GetBytes(SDK_VERSION); + var nativeDataPtr = Marshal.AllocHGlobal(data.Length+1); + Marshal.Copy(data, 0, nativeDataPtr, data.Length); + Marshal.WriteByte(nativeDataPtr+data.Length, 0); + EventCallContext context = new EventCallContext + { + call = (Byte)EventCallType.EVT_OP_VERSION, + data = nativeDataPtr + }; + NativeMethods.evt_api_call(ref context); + return Marshal.PtrToStringAnsi(context.data); // x.y.z + // PtrToStringAnsi(context.data); + } + + static void Main(string[] args) + { + // Environment.SetEnvironmentVariable("DYLD_FALLBACK_LIBRARY_PATH", "/usr/local/lib"); + + Console.WriteLine("Reading configuration..."); + string cfg = ReadConfiguration("sdk-config.json"); + // Parse to verify it is valid and print it out + JsonObject jsonDoc = (JsonObject)JsonObject.Parse(cfg); + Console.WriteLine("configuration:\n{0}", jsonDoc.ToString()); + + Console.WriteLine("SDK version: {0}", evt_version()); + + Console.WriteLine("evt_open..."); + var handle = evt_open(cfg); + Console.WriteLine("handle={0}", handle); + + Console.WriteLine("evt_close..."); + var result = evt_close(handle); + Console.WriteLine("result={0}", result); + } + } +} diff --git a/wrappers/netcore/appsettings.json b/wrappers/netcore/appsettings.json new file mode 100644 index 000000000..ecd461122 --- /dev/null +++ b/wrappers/netcore/appsettings.json @@ -0,0 +1,18 @@ +{ + "Logging": { + "IncludeScopes": true, + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + }, + "Console": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } + } +} + diff --git a/wrappers/netcore/run.sh b/wrappers/netcore/run.sh new file mode 100755 index 000000000..dd2764567 --- /dev/null +++ b/wrappers/netcore/run.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# Mac OS X: +# brew cask install dotnet-sdk + +#export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH +#export DYLD_FALLBACK_LIBRARY_PATH=/usr/local/lib:$DYLD_FALLBACK_LIBRARY_PATH + +dotnet run -c Debug -v diag + + + diff --git a/wrappers/netcore/sdk-config.json b/wrappers/netcore/sdk-config.json new file mode 100644 index 000000000..98cbeed13 --- /dev/null +++ b/wrappers/netcore/sdk-config.json @@ -0,0 +1,46 @@ +{ + "config": {"host": "*"}, + "name": "C-API-Client-0", + "version": "1.0.0", + "cacheFileFullNotificationPercentage": 75, + "cacheFilePath": "/tmp/storage.db", + "cacheFileSizeLimitInBytes": 3145728, + "cacheMemoryFullNotificationPercentage": 75, + "cacheMemorySizeLimitInBytes": 524288, + "compat": { + "dotType": true + }, + "enableLifecycleSession": false, + "eventCollectorUri": "https://self.events.data.microsoft.com/OneCollector/1.0/", + "hostMode": true, + "http": { + "compress": true + }, + "maxDBFlushQueues": 3, + "maxPendingHTTPRequests": 4, + "maxTeardownUploadTimeInSec": 1, + "minimumTraceLevel": 1, + "multiTenantEnabled": true, + "primaryToken": "6d084bbf6a9644ef83f40a77c9e34580-c2d379e0-4408-4325-9b4d-2a7d78131e14-7322", + "sample": { + "rate": 0 + }, + "sdkmode": 0, + "skipSqliteInitAndShutdown": null, + "stats": { + "interval": 5, + "split": false, + "tokenInt": "8130ef8ff472405d89d6f420038927ea-0c0d561e-cca5-4c81-90ed-0aa9ad786a03-7166", + "tokenProd": "4bb4d6f7cafc4e9292f972dca2dcde42-bd019ee8-e59c-4b0f-a02c-84e72157a3ef-7485" + }, + "tpm": { + "backoffConfig": "E,3000,300000,2,1", + "clockSkewEnabled": true, + "maxBlobSize": 2097152, + "maxRetryCount": 5 + }, + "traceLevelMask": 268435455, + "utc": { + "providerGroupId": "780dddc8-18a1-5781-895a-a690464fa89c" + } +} \ No newline at end of file From 4abaff917b30fb79c151c7e96b5806067d021759 Mon Sep 17 00:00:00 2001 From: Max Golovanov Date: Wed, 8 Jan 2020 02:34:37 -0800 Subject: [PATCH 03/15] Clean-up the wrapper a little bit --- wrappers/netcore/Program.cs | 51 ++++++++++++++++--------------------- wrappers/netcore/run.sh | 3 ++- 2 files changed, 24 insertions(+), 30 deletions(-) diff --git a/wrappers/netcore/Program.cs b/wrappers/netcore/Program.cs index 4450893d8..385029e74 100644 --- a/wrappers/netcore/Program.cs +++ b/wrappers/netcore/Program.cs @@ -23,24 +23,23 @@ public enum EventCallType EVT_OP_MAX = EVT_OP_OPEN_WITH_PARAMS + 1 } - // [StructLayout(LayoutKind.Sequential)] - [StructLayout(LayoutKind.Explicit, Size=32, CharSet=CharSet.Ansi)] + [StructLayout(LayoutKind.Explicit, Size = 24, CharSet = CharSet.Ansi)] public struct EventCallContext { - [FieldOffset(0)]public UInt32 call; - [FieldOffset(4)]public ulong handle; - [FieldOffset(12)]public IntPtr data; - [FieldOffset(16)]public UInt32 result; - [FieldOffset(20)]public UInt32 size; + [FieldOffset(0)] public UInt32 call; + [FieldOffset(4)] public ulong handle; + [FieldOffset(12)] public IntPtr data; + [FieldOffset(16)] public UInt32 result; + [FieldOffset(20)] public UInt32 size; } internal static class NativeMethods { - [DllImport("libmat", EntryPoint = "evt_api_call_default" /* , CallingConvention = CallingConvention.Cdecl */)] + // Conditional compilation: pass different library name depending on target OS? + const string SDK_LIBRARY_NAME = "libmat"; + + [DllImport(SDK_LIBRARY_NAME, EntryPoint = "evt_api_call_default")] public static extern UInt32 evt_api_call([In, Out] ref EventCallContext context); - // public static extern UInt32 evt_api_call(ref EventCallContext context); - // Alternatives: - // [MarshalAs(UnmanagedType.LPStruct)] } class Program @@ -59,20 +58,12 @@ static string ReadConfiguration(string filename) static ulong evt_open(string cfg) { - /* - byte[] data = Encoding.ASCII.GetBytes(cfg); - var nativeDataPtr = Marshal.AllocHGlobal(data.Length+1); - Marshal.Copy(data, 0, nativeDataPtr, data.Length); - Marshal.WriteByte(nativeDataPtr+data.Length, 0); - */ - var nativeDataPtr = Marshal.StringToHGlobalAnsi(cfg); EventCallContext context = new EventCallContext { call = (Byte)EventCallType.EVT_OP_OPEN, - data = nativeDataPtr + data = Marshal.StringToHGlobalAnsi(cfg) }; NativeMethods.evt_api_call(ref context); - // Marshal.Release(nativeDataPtr); return context.handle; } @@ -89,35 +80,37 @@ static ulong evt_close(ulong inHandle) static string evt_version() { byte[] data = Encoding.ASCII.GetBytes(SDK_VERSION); - var nativeDataPtr = Marshal.AllocHGlobal(data.Length+1); + var nativeDataPtr = Marshal.AllocHGlobal(data.Length + 1); Marshal.Copy(data, 0, nativeDataPtr, data.Length); - Marshal.WriteByte(nativeDataPtr+data.Length, 0); + Marshal.WriteByte(nativeDataPtr + data.Length, 0); EventCallContext context = new EventCallContext { call = (Byte)EventCallType.EVT_OP_VERSION, data = nativeDataPtr }; NativeMethods.evt_api_call(ref context); - return Marshal.PtrToStringAnsi(context.data); // x.y.z - // PtrToStringAnsi(context.data); + return Marshal.PtrToStringAnsi(context.data); } static void Main(string[] args) { - // Environment.SetEnvironmentVariable("DYLD_FALLBACK_LIBRARY_PATH", "/usr/local/lib"); - - Console.WriteLine("Reading configuration..."); + Console.WriteLine("Reading configuration..."); string cfg = ReadConfiguration("sdk-config.json"); - // Parse to verify it is valid and print it out + + // Parse to verify it is valid and print it out.. + // Parser should throw if config is invalid. JsonObject jsonDoc = (JsonObject)JsonObject.Parse(cfg); - Console.WriteLine("configuration:\n{0}", jsonDoc.ToString()); + Console.WriteLine("SDK configuration:\n{0}", jsonDoc.ToString()); + // Obtain SDK version from native library Console.WriteLine("SDK version: {0}", evt_version()); + // Initialize Console.WriteLine("evt_open..."); var handle = evt_open(cfg); Console.WriteLine("handle={0}", handle); + // FlushAndTeardown Console.WriteLine("evt_close..."); var result = evt_close(handle); Console.WriteLine("result={0}", result); diff --git a/wrappers/netcore/run.sh b/wrappers/netcore/run.sh index dd2764567..d975d50fc 100755 --- a/wrappers/netcore/run.sh +++ b/wrappers/netcore/run.sh @@ -5,7 +5,8 @@ #export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH #export DYLD_FALLBACK_LIBRARY_PATH=/usr/local/lib:$DYLD_FALLBACK_LIBRARY_PATH -dotnet run -c Debug -v diag +#dotnet run -c Debug -v diag +dotnet run -c Debug From c73b4ef06d810218d7c2b30f59b28dd8627d413a Mon Sep 17 00:00:00 2001 From: Max Golovanov Date: Sat, 18 Jan 2020 02:28:55 -0800 Subject: [PATCH 04/15] Add more APIs to netcore implementation --- lib/include/public/mat.h | 2 +- wrappers/netcore/EventNativeAPI.cs | 346 ++++++++++++++++++++++++++++ wrappers/netcore/EventSender.csproj | 1 + wrappers/netcore/Program.cs | 91 ++------ 4 files changed, 363 insertions(+), 77 deletions(-) create mode 100644 wrappers/netcore/EventNativeAPI.cs diff --git a/lib/include/public/mat.h b/lib/include/public/mat.h index 482568c23..3adb184e0 100644 --- a/lib/include/public/mat.h +++ b/lib/include/public/mat.h @@ -6,7 +6,7 @@ * For version handshake check there is no mandatory requirement to update the $PATCH level. * Ref. https://semver.org/ for Semantic Versioning documentation. */ -#define TELEMETRY_EVENTS_VERSION "3.1.0" +#define TELEMETRY_EVENTS_VERSION "3.3.0" #include "ctmacros.hpp" diff --git a/wrappers/netcore/EventNativeAPI.cs b/wrappers/netcore/EventNativeAPI.cs new file mode 100644 index 000000000..44b6a7650 --- /dev/null +++ b/wrappers/netcore/EventNativeAPI.cs @@ -0,0 +1,346 @@ +#pragma warning disable IDE1006 // ignore naming rule violations: we preserve original C API naming for clarity here +#pragma warning disable IDE0044 // ignore readonly suggestion for field passed over P/Invoke + +using System; +using System.Runtime.InteropServices; +using System.Text; +using System.IO; +using System.Json; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Collections; + +namespace Microsoft +{ + namespace Telemetry + { + namespace Core + { + public class Constants + { + public const string LIBRARY_NAME_POSIX = "libmat"; + public const string LIBRARY_NAME_WIN32 = "ClientTelemetry.dll"; + public const string VERSION = "3.3.0-netcore"; + } + + public enum EventCallType + { + EVT_OP_LOAD = 0x00000001, + EVT_OP_UNLOAD = 0x00000002, + EVT_OP_OPEN = 0x00000003, + EVT_OP_CLOSE = 0x00000004, + EVT_OP_CONFIG = 0x00000005, + EVT_OP_LOG = 0x00000006, + EVT_OP_PAUSE = 0x00000007, + EVT_OP_RESUME = 0x00000008, + EVT_OP_UPLOAD = 0x00000009, + EVT_OP_FLUSH = 0x0000000A, + EVT_OP_VERSION = 0x0000000B, + EVT_OP_OPEN_WITH_PARAMS = 0x0000000C, + EVT_OP_MAX = EVT_OP_OPEN_WITH_PARAMS + 1 + } + + public enum EventPropertyType + { + /* Basic types */ + TYPE_STRING = 0, + TYPE_INT64 = 1, + TYPE_DOUBLE = 2, + TYPE_TIME = 3, + TYPE_BOOLEAN = 4, + TYPE_GUID = 5, + /* Arrays of basic types */ + TYPE_STRING_ARRAY = 6, + TYPE_INT64_ARRAY = 7, + TYPE_DOUBLE_ARRAY = 8, + TYPE_TIME_ARRAY = 9, + TYPE_BOOL_ARRAY = 10, + TYPE_GUID_ARRAY = 11, + /* NULL-type */ + TYPE_NULL = 12 + } + + public struct PiiValue + { + public string value; + public UInt32 piKind; + } + + public class EventProperties : Dictionary + { + public EventProperties() + { + } + }; + + [StructLayout(LayoutKind.Explicit, Size = 16, CharSet = CharSet.Ansi)] + public unsafe struct EventGUIDType + { + /** + * + * Specifies the first eight hexadecimal digits of the GUID. + * + */ + [FieldOffset(0)] UInt32 Data1; + + /* + * Specifies the first group of four hexadecimal digits. + * + */ + [FieldOffset(4)] UInt16 Data2; + + /** + * + * Specifies the second group of four hexadecimal digits. + * + */ + [FieldOffset(6)] UInt16 Data3; + + /** + * An array of eight bytes. + * The first two bytes contain the third group of four hexadecimal digits. + * The remaining six bytes contain the final 12 hexadecimal digits. + * + */ + [FieldOffset(8)] + fixed byte Data4[8]; + } + + [StructLayout(LayoutKind.Explicit, Size = 24, CharSet = CharSet.Ansi)] + public struct EventContextType + { + [FieldOffset(0)] public UInt32 call; + [FieldOffset(4)] public ulong handle; + [FieldOffset(12)] public IntPtr data; + [FieldOffset(16)] public UInt32 result; + [FieldOffset(20)] public UInt32 size; + } + + public enum EventOpenParamType + { + OPEN_PARAM_TYPE_HTTP_HANDLER_SEND = 0, + OPEN_PARAM_TYPE_HTTP_HANDLER_CANCEL = 1, + OPEN_PARAM_TYPE_TASK_DISPATCHER_QUEUE = 2, + OPEN_PARAM_TYPE_TASK_DISPATCHER_CANCEL = 3, + OPEN_PARAM_TYPE_TASK_DISPATCHER_JOIN = 4 + } + + [StructLayout(LayoutKind.Explicit, Size = 12, CharSet = CharSet.Ansi)] + public struct EventOpenParam + { + [FieldOffset(0)] public UInt32 type; + [FieldOffset(4)] public IntPtr data; + }; + + /** + * + * Wraps logger configuration string and all input parameters to 'evt_open_with_params' + * + */ + // TODO: this structure size depends on architecture - 32-bit or 64-bit.. + // Since all Mac OS X and Linux are mostly 64-bit by now, as well as + // most Windows - we should assume that the struct layout is optimized + // for 64-bit. From a practical standpoint - somebody building .NET Core + // app would likely consider running it on a platform that is modern + // enough. One way to solve this issue for 32-bit machines is to add a + // custom SDK build flag that enforces certain struct layout. i.e. + // positioning the two pointers below as 64-bit integers instead of 32-bit. + [StructLayout(LayoutKind.Explicit, Size = 20, CharSet = CharSet.Ansi)] + public struct EventOpenWithParamsDataType + { + [FieldOffset(0)] public IntPtr config; + [FieldOffset(8)] public IntPtr parameters; /* pointer to array of EventOpenParam */ + [FieldOffset(8)] public UInt32 paramsCount; + } + + [StructLayout(LayoutKind.Explicit, Size = 8, CharSet = CharSet.Ansi)] + public struct EventPropertyValue + { + /* Basic types */ + [FieldOffset(0)] UInt64 as_uint64; + [FieldOffset(0)] IntPtr as_string; + [FieldOffset(0)] Int64 as_int64; + [FieldOffset(0)] double as_double; + [FieldOffset(0)] bool as_bool; + [FieldOffset(0)] IntPtr as_guid; + [FieldOffset(0)] UInt64 as_time; +#if FALSE + /* We don't support passing arrays yet. Code below needs to be ported from C++ to C# */ + /* Array types are nullptr-terminated array of pointers */ + char** as_arr_string; + int64_t** as_arr_int64; + bool** as_arr_bool; + double** as_arr_double; + evt_guid_t** as_arr_guid; + uint64_t** as_arr_time; +#endif + }; + + [StructLayout(LayoutKind.Explicit, Size = 16, CharSet = CharSet.Ansi)] + public struct EventProperty + { + [FieldOffset(0)] IntPtr name; + [FieldOffset(4)] EventPropertyType type; + [FieldOffset(8)] EventPropertyValue value; + [FieldOffset(12)] UInt32 piiKind; + } + + /** + * + * Identifies HTTP request method type + * + */ + public enum HttpRequestType + { + HTTP_REQUEST_TYPE_GET = 0, + HTTP_REQUEST_TYPE_POST = 1, + } + + public static class EventNativeAPI + { + // Conditional compilation: pass different library name depending on target OS + + [DllImport(Constants.LIBRARY_NAME_POSIX, EntryPoint = "evt_api_call_default")] + internal static extern UInt32 evt_api_call([In, Out] ref EventContextType context); + + /** + * + * Create or open existing SDK instance. + * + * SDK configuration. + * SDK instance handle. + */ + public static ulong evt_open(string cfg) + { + EventContextType context = new EventContextType + { + call = (Byte)EventCallType.EVT_OP_OPEN, + data = Marshal.StringToHGlobalAnsi(cfg) + }; + evt_api_call(ref context); + return context.handle; + } + + /** + * + * Destroy or close SDK instance by handle + * + * SDK instance handle. + * Status code. + */ + public static ulong evt_close(ulong inHandle) + { + EventContextType context = new EventContextType + { + call = (Byte)EventCallType.EVT_OP_CLOSE, + handle = inHandle + }; + return evt_api_call(ref context); + } + + public static ulong evt_log(ulong inHandle, ref EventProperties properties) + { + EventContextType context = new EventContextType + { + call = (Byte)EventCallType.EVT_OP_LOG, + handle = inHandle + }; + return evt_api_call(ref context); + } + + /** + * + * Pauses transmission. In that mode events stay in ram or saved to disk, not sent. + * + * SDK handle. + * Status code. + */ + public static ulong evt_pause(ulong inHandle) + { + EventContextType context = new EventContextType + { + call = (Byte)EventCallType.EVT_OP_PAUSE, + handle = inHandle + }; + return evt_api_call(ref context); + } + + /** + * + * Resumes transmission. Pending telemetry events should be attempted to be sent. + * + * SDK handle. + * Status code. + */ + public static ulong evt_resume(ulong inHandle) + { + EventContextType context = new EventContextType + { + call = (Byte)EventCallType.EVT_OP_RESUME, + handle = inHandle + }; + return evt_api_call(ref context); + } + + /** + * Provide a hint to telemetry system to attempt force-upload of events + * without waiting for the next batch timer interval. This API does not + * guarantee the upload. + * + * SDK handle. + * Status code. + */ + public static ulong evt_upload(ulong inHandle) + { + EventContextType context = new EventContextType + { + call = (Byte)EventCallType.EVT_OP_RESUME, + handle = inHandle + }; + return evt_api_call(ref context); + } + + /** + * Save pending telemetry events to offline storage on disk. + * + * SDK handle. + * Status code. + */ + public static ulong evt_flush(ulong inHandle) + { + EventContextType context = new EventContextType + { + call = (Byte)EventCallType.EVT_OP_FLUSH, + handle = inHandle + }; + return evt_api_call(ref context); + } + + /** + * Pass down SDK header version to SDK library. Needed for late binding version checking. + * This method provides means of a handshake between library header and a library impl. + * It is up to app dev to verify the value returned, making a decision whether some SDK + * features are implemented/supported by particular SDK version or not. + * + * SDK header semver. + * SDK library semver + */ + public static string evt_version() + { + byte[] data = Encoding.ASCII.GetBytes(Constants.VERSION); + var nativeDataPtr = Marshal.AllocHGlobal(data.Length + 1); + Marshal.Copy(data, 0, nativeDataPtr, data.Length); + Marshal.WriteByte(nativeDataPtr + data.Length, 0); + EventContextType context = new EventContextType + { + call = (Byte)EventCallType.EVT_OP_VERSION, + data = nativeDataPtr + }; + evt_api_call(ref context); + return Marshal.PtrToStringAnsi(context.data); + } + } + + } + } +} \ No newline at end of file diff --git a/wrappers/netcore/EventSender.csproj b/wrappers/netcore/EventSender.csproj index 059cc350c..3a42309a4 100644 --- a/wrappers/netcore/EventSender.csproj +++ b/wrappers/netcore/EventSender.csproj @@ -2,6 +2,7 @@ Exe netcoreapp3.1 + true diff --git a/wrappers/netcore/Program.cs b/wrappers/netcore/Program.cs index 385029e74..bea811e4e 100644 --- a/wrappers/netcore/Program.cs +++ b/wrappers/netcore/Program.cs @@ -4,48 +4,13 @@ using System.IO; using System.Json; +using Microsoft.Telemetry.Core; + namespace EventSender { - public enum EventCallType - { - EVT_OP_LOAD = 0x00000001, - EVT_OP_UNLOAD = 0x00000002, - EVT_OP_OPEN = 0x00000003, - EVT_OP_CLOSE = 0x00000004, - EVT_OP_CONFIG = 0x00000005, - EVT_OP_LOG = 0x00000006, - EVT_OP_PAUSE = 0x00000007, - EVT_OP_RESUME = 0x00000008, - EVT_OP_UPLOAD = 0x00000009, - EVT_OP_FLUSH = 0x0000000A, - EVT_OP_VERSION = 0x0000000B, - EVT_OP_OPEN_WITH_PARAMS = 0x0000000C, - EVT_OP_MAX = EVT_OP_OPEN_WITH_PARAMS + 1 - } - - [StructLayout(LayoutKind.Explicit, Size = 24, CharSet = CharSet.Ansi)] - public struct EventCallContext - { - [FieldOffset(0)] public UInt32 call; - [FieldOffset(4)] public ulong handle; - [FieldOffset(12)] public IntPtr data; - [FieldOffset(16)] public UInt32 result; - [FieldOffset(20)] public UInt32 size; - } - - internal static class NativeMethods - { - // Conditional compilation: pass different library name depending on target OS? - const string SDK_LIBRARY_NAME = "libmat"; - - [DllImport(SDK_LIBRARY_NAME, EntryPoint = "evt_api_call_default")] - public static extern UInt32 evt_api_call([In, Out] ref EventCallContext context); - } class Program { - public const string SDK_VERSION = "3.3.0-netcore"; - static string ReadConfiguration(string filename) { string result = ""; @@ -56,42 +21,6 @@ static string ReadConfiguration(string filename) return result; } - static ulong evt_open(string cfg) - { - EventCallContext context = new EventCallContext - { - call = (Byte)EventCallType.EVT_OP_OPEN, - data = Marshal.StringToHGlobalAnsi(cfg) - }; - NativeMethods.evt_api_call(ref context); - return context.handle; - } - - static ulong evt_close(ulong inHandle) - { - EventCallContext context = new EventCallContext - { - call = (Byte)EventCallType.EVT_OP_CLOSE, - handle = inHandle - }; - return NativeMethods.evt_api_call(ref context); - } - - static string evt_version() - { - byte[] data = Encoding.ASCII.GetBytes(SDK_VERSION); - var nativeDataPtr = Marshal.AllocHGlobal(data.Length + 1); - Marshal.Copy(data, 0, nativeDataPtr, data.Length); - Marshal.WriteByte(nativeDataPtr + data.Length, 0); - EventCallContext context = new EventCallContext - { - call = (Byte)EventCallType.EVT_OP_VERSION, - data = nativeDataPtr - }; - NativeMethods.evt_api_call(ref context); - return Marshal.PtrToStringAnsi(context.data); - } - static void Main(string[] args) { Console.WriteLine("Reading configuration..."); @@ -103,16 +32,26 @@ static void Main(string[] args) Console.WriteLine("SDK configuration:\n{0}", jsonDoc.ToString()); // Obtain SDK version from native library - Console.WriteLine("SDK version: {0}", evt_version()); + Console.WriteLine("SDK version: {0}", EventNativeAPI.evt_version()); // Initialize Console.WriteLine("evt_open..."); - var handle = evt_open(cfg); + var handle = EventNativeAPI.evt_open(cfg); Console.WriteLine("handle={0}", handle); +#if FALSE + var props = new EventProperties() { + { "strKey", "value1" }, + { "intKey", 12345 }, + { "dblKey", 0.12345 }, + { "guidKey", new Guid("73e21739-9d4e-497d-9c66-8e399a532ec9") } + }; + EventNativeAPI.evt_log(handle, ref props); +#endif + // FlushAndTeardown Console.WriteLine("evt_close..."); - var result = evt_close(handle); + var result = EventNativeAPI.evt_close(handle); Console.WriteLine("result={0}", result); } } From 30b7fe0d5cceaf68921f8fd0a4f6df5fe7344b46 Mon Sep 17 00:00:00 2001 From: Max Golovanov Date: Sat, 18 Jan 2020 18:32:07 -0800 Subject: [PATCH 05/15] Working implementation of C# .NET Core 3.1 wrapper on top of C API (tested on Mac) --- lib/include/public/mat.h | 363 ++++++++++++++++++----------- wrappers/netcore/EventNativeAPI.cs | 305 +++++++++++++++++++----- wrappers/netcore/Program.cs | 46 +++- wrappers/netcore/sdk-config.json | 8 +- 4 files changed, 524 insertions(+), 198 deletions(-) diff --git a/lib/include/public/mat.h b/lib/include/public/mat.h index 3adb184e0..7e284f49c 100644 --- a/lib/include/public/mat.h +++ b/lib/include/public/mat.h @@ -6,7 +6,7 @@ * For version handshake check there is no mandatory requirement to update the $PATCH level. * Ref. https://semver.org/ for Semantic Versioning documentation. */ -#define TELEMETRY_EVENTS_VERSION "3.3.0" +#define TELEMETRY_EVENTS_VERSION "3.3.0" #include "ctmacros.hpp" @@ -22,15 +22,15 @@ #if (_MSC_VER == 1500) || (_MSC_VER == 1600) /* Visual Studio 2010 */ -typedef __int64 int64_t; -typedef unsigned __int64 uint64_t; -typedef __int32 int32_t; -typedef unsigned __int32 uint32_t; -typedef __int16 int16_t; -typedef unsigned __int16 uint16_t; -typedef __int8 int8_t; -typedef unsigned __int8 uint8_t; -typedef int bool; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef __int16 int16_t; +typedef unsigned __int16 uint16_t; +typedef __int8 int8_t; +typedef unsigned __int8 uint8_t; +typedef int bool; #define inline #else /* Other compilers with C11 support */ @@ -39,16 +39,17 @@ typedef int bool; #endif #ifndef EVT_ARRAY_SIZE -#define EVT_ARRAY_SIZE(a) \ - ((sizeof(a) / sizeof(*(a))) / \ - (unsigned)(!(sizeof(a) % sizeof(*(a))))) +#define EVT_ARRAY_SIZE(a) \ + ((sizeof(a) / sizeof(*(a))) / \ + (unsigned)(!(sizeof(a) % sizeof(*(a))))) #endif #ifdef __cplusplus -extern "C" { +extern "C" +{ #endif - typedef enum + typedef enum /* 32-bit */ { EVT_OP_LOAD = 0x00000001, EVT_OP_UNLOAD = 0x00000002, @@ -62,10 +63,11 @@ extern "C" { EVT_OP_FLUSH = 0x0000000A, EVT_OP_VERSION = 0x0000000B, EVT_OP_OPEN_WITH_PARAMS = 0x0000000C, - EVT_OP_MAX = EVT_OP_OPEN_WITH_PARAMS + 1 + EVT_OP_MAX = EVT_OP_OPEN_WITH_PARAMS + 1, + EVT_OP_MAXINT = 0xFFFFFFFF } evt_call_t; - typedef enum + typedef enum /* 32-bit */ { /* Basic types */ TYPE_STRING, @@ -82,7 +84,8 @@ extern "C" { TYPE_BOOL_ARRAY, TYPE_GUID_ARRAY, /* NULL-type */ - TYPE_NULL + TYPE_NULL, + TYPE_MAXINT = 0xFFFFFFFF } evt_prop_t; typedef struct @@ -113,20 +116,20 @@ extern "C" { * The remaining six bytes contain the final 12 hexadecimal digits. * */ - uint8_t Data4[8]; + uint8_t Data4[8]; } evt_guid_t; - typedef int64_t evt_handle_t; - typedef int32_t evt_status_t; - typedef struct evt_event evt_event; + typedef int64_t evt_handle_t; + typedef int32_t evt_status_t; + typedef struct evt_event evt_event; typedef struct PACKED_STRUCT { - evt_call_t call; /* In */ - evt_handle_t handle; /* In / Out */ - void* data; /* In / Out */ - evt_status_t result; /* Out */ - uint32_t size; /* In / Out */ + evt_call_t call; /* In */ + evt_handle_t handle; /* In / Out */ + void* data; /* In / Out */ + evt_status_t result; /* Out */ + uint32_t size; /* In / Out */ } evt_context_t; /** @@ -151,8 +154,8 @@ extern "C" { */ typedef struct { - evt_open_param_type_t type; - void* data; + evt_open_param_type_t type; + void* data; } evt_open_param_t; /** @@ -162,38 +165,37 @@ extern "C" { */ typedef struct { - const char* config; + const char* config; const evt_open_param_t* params; - int32_t paramsCount; + int32_t paramsCount; } evt_open_with_params_data_t; - typedef union - { + typedef union { /* Basic types */ - uint64_t as_uint64; - const char* as_string; - int64_t as_int64; - double as_double; - bool as_bool; - evt_guid_t* as_guid; - uint64_t as_time; + uint64_t as_uint64; + const char* as_string; + int64_t as_int64; + double as_double; + bool as_bool; + evt_guid_t* as_guid; + uint64_t as_time; /* Array types are nullptr-terminated array of pointers */ - char** as_arr_string; - int64_t** as_arr_int64; - bool** as_arr_bool; - double** as_arr_double; - evt_guid_t** as_arr_guid; - uint64_t** as_arr_time; + char** as_arr_string; + int64_t** as_arr_int64; + bool** as_arr_bool; + double** as_arr_double; + evt_guid_t** as_arr_guid; + uint64_t** as_arr_time; } evt_prop_v; - typedef struct + typedef struct PACKED_STRUCT { - const char* name; - evt_prop_t type; - evt_prop_v value; - uint32_t piiKind; + const char* name; + evt_prop_t type; + evt_prop_v value; + uint32_t piiKind; } evt_prop; - + /** * * Identifies HTTP request method type @@ -225,8 +227,8 @@ extern "C" { */ typedef struct { - const char* name; - const char* value; + const char* name; + const char* value; } http_header_t; /** @@ -236,13 +238,13 @@ extern "C" { */ typedef struct { - const char* id; - http_request_type_t type; - const char* url; - const uint8_t* body; - int32_t bodySize; - const http_header_t* headers; - int32_t headersCount; + const char* id; + http_request_type_t type; + const char* url; + const uint8_t* body; + int32_t bodySize; + const http_header_t* headers; + int32_t headersCount; } http_request_t; /** @@ -252,17 +254,17 @@ extern "C" { */ typedef struct { - int32_t statusCode; - const uint8_t* body; - int32_t bodySize; - const http_header_t* headers; - int32_t headersCount; + int32_t statusCode; + const uint8_t* body; + int32_t bodySize; + const http_header_t* headers; + int32_t headersCount; } http_response_t; /* HTTP callback function signatures */ - typedef void (EVTSDK_LIBABI_CDECL *http_complete_fn_t)(const char* /*requestId*/, http_result_t, http_response_t*); - typedef void (EVTSDK_LIBABI_CDECL *http_send_fn_t)(http_request_t*, http_complete_fn_t); - typedef void (EVTSDK_LIBABI_CDECL *http_cancel_fn_t)(const char* /*requestId*/); + typedef void(EVTSDK_LIBABI_CDECL* http_complete_fn_t)(const char* /*requestId*/, http_result_t, http_response_t*); + typedef void(EVTSDK_LIBABI_CDECL* http_send_fn_t)(http_request_t*, http_complete_fn_t); + typedef void(EVTSDK_LIBABI_CDECL* http_cancel_fn_t)(const char* /*requestId*/); /** * @@ -271,21 +273,27 @@ extern "C" { */ typedef struct { - const char* id; - int64_t delayMs; - const char* typeName; + const char* id; + int64_t delayMs; + const char* typeName; } evt_task_t; /* Async worker thread callback function signatures */ - typedef void (EVTSDK_LIBABI_CDECL *task_callback_fn_t)(const char* /*taskId*/); + typedef void(EVTSDK_LIBABI_CDECL* task_callback_fn_t)(const char* /*taskId*/); typedef void(EVTSDK_LIBABI_CDECL* task_dispatcher_queue_fn_t)(evt_task_t*, task_callback_fn_t); - typedef bool (EVTSDK_LIBABI_CDECL *task_dispatcher_cancel_fn_t)(const char* /*taskId*/); - typedef void (EVTSDK_LIBABI_CDECL *task_dispatcher_join_fn_t)(); + typedef bool(EVTSDK_LIBABI_CDECL* task_dispatcher_cancel_fn_t)(const char* /*taskId*/); + typedef void(EVTSDK_LIBABI_CDECL* task_dispatcher_join_fn_t)(); #if (_MSC_VER == 1500) || (_MSC_VER == 1600) || (defined(__cplusplus) && !defined(__GNUG__)) /* Code to support C89 compiler, including VS2010 */ -#define TELEMETRY_EVENT(...) { __VA_ARGS__ , { NULL, TYPE_NULL } } -/* With C89-style initializers, structure members must be initialized in the order declared. +#define TELEMETRY_EVENT(...) \ + { \ + __VA_ARGS__, \ + { \ + NULL, TYPE_NULL \ + } \ + } + /* With C89-style initializers, structure members must be initialized in the order declared. ...and (!) - only the first member of a union can be initialized. Which means that we have to do the hack of C-style casting from value to char* ... */ @@ -297,8 +305,14 @@ extern "C" { pv.as_double = val; return pv; }; -#define _DBL(key, val) { key, TYPE_DOUBLE, _DBL2({ NULL }, val) } -#define PII_DBL(key, val, kind) { key, TYPE_DOUBLE, _DBL2({ NULL }, val), kind } +#define _DBL(key, val) \ + { \ + key, TYPE_DOUBLE, _DBL2({NULL}, val) \ + } +#define PII_DBL(key, val, kind) \ + { \ + key, TYPE_DOUBLE, _DBL2({NULL}, val), kind \ + } /* static inline evt_prop_v _TIME2(evt_prop_v pv, uint64_t val) @@ -313,48 +327,135 @@ extern "C" { #pragma message "C89 compiler does not support passing DOUBLE and TIME values via C API" #endif -#define _STR(key, val) { key, TYPE_STRING, { (uint64_t)((char *)val) } } -#define _INT(key, val) { key, TYPE_INT64, { (uint64_t)val } } -#define _BOOL(key, val) { key, TYPE_BOOLEAN, { (uint64_t)val } } -#define _GUID(key, val) { key, TYPE_GUID, { (uint64_t)((char *)val) } } -#define _TIME(key, val) { key, TYPE_TIME, { (uint64_t)val } } +#define _STR(key, val) \ + { \ + key, TYPE_STRING, \ + { \ + (uint64_t)((char*)val) \ + } \ + } +#define _INT(key, val) \ + { \ + key, TYPE_INT64, \ + { \ + (uint64_t) val \ + } \ + } +#define _BOOL(key, val) \ + { \ + key, TYPE_BOOLEAN, \ + { \ + (uint64_t) val \ + } \ + } +#define _GUID(key, val) \ + { \ + key, TYPE_GUID, \ + { \ + (uint64_t)((char*)val) \ + } \ + } +#define _TIME(key, val) \ + { \ + key, TYPE_TIME, \ + { \ + (uint64_t) val \ + } \ + } -#define PII_STR(key, val, kind) { key, TYPE_STRING, { (uint64_t)((char *)val) }, kind } -#define PII_INT(key, val, kind) { key, TYPE_INT64, { (uint64_t)val }, kind } -#define PII_BOOL(key, val, kind) { key, TYPE_BOOLEAN, { (uint64_t)val }, kind } -#define PII_GUID(key, val, kind) { key, TYPE_GUID, { (uint64_t)((char *)val) }, kind } -#define PII_TIME(key, val, kind) { key, TYPE_TIME, { (uint64_t)val }, kind } +#define PII_STR(key, val, kind) \ + { \ + key, TYPE_STRING, {(uint64_t)((char*)val)}, kind \ + } +#define PII_INT(key, val, kind) \ + { \ + key, TYPE_INT64, {(uint64_t)val}, kind \ + } +#define PII_BOOL(key, val, kind) \ + { \ + key, TYPE_BOOLEAN, {(uint64_t)val}, kind \ + } +#define PII_GUID(key, val, kind) \ + { \ + key, TYPE_GUID, {(uint64_t)((char*)val)}, kind \ + } +#define PII_TIME(key, val, kind) \ + { \ + key, TYPE_TIME, {(uint64_t)val}, kind \ + } #else - /* Code to support any modern C99 compiler */ -#define TELEMETRY_EVENT(...) { __VA_ARGS__ , { .name = NULL, .type = TYPE_NULL, .value = { .as_int64 = 0 }, .piiKind = 0 } } - -#define _STR(key, val) { .name = key, .type = TYPE_STRING, .value = { .as_string = val }, .piiKind = 0 } -#define _INT(key, val) { .name = key, .type = TYPE_INT64, .value = { .as_int64 = val }, .piiKind = 0 } -#define _DBL(key, val) { .name = key, .type = TYPE_DOUBLE, .value = { .as_double = val }, .piiKind = 0 } -#define _BOOL(key, val) { .name = key, .type = TYPE_BOOLEAN, .value = { .as_bool = val }, .piiKind = 0 } -#define _GUID(key, val) { .name = key, .type = TYPE_GUID, .value = { .as_string = val }, .piiKind = 0 } -#define _TIME(key, val) { .name = key, .type = TYPE_TIME, .value = { .as_time = val }, .piiKind = 0 } - -#define PII_STR(key, val, kind) { .name = key, .type = TYPE_STRING, .value = { .as_string = val }, .piiKind = kind } -#define PII_INT(key, val, kind) { .name = key, .type = TYPE_INT64, .value = { .as_int64 = val }, .piiKind = kind } -#define PII_DBL(key, val, kind) { .name = key, .type = TYPE_DOUBLE, .value = { .as_double = val }, .piiKind = kind } -#define PII_BOOL(key, val, kind) { .name = key, .type = TYPE_BOOLEAN, .value = { .as_bool = val }, .piiKind = kind } -#define PII_GUID(key, val, kind) { .name = key, .type = TYPE_GUID, .value = { .as_string = val }, .piiKind = kind } -#define PII_TIME(key, val, kind) { .name = key, .type = TYPE_TIME, .value = { .as_time = val }, .piiKind = kind } +/* Code to support any modern C99 compiler */ +#define TELEMETRY_EVENT(...) \ + { \ + __VA_ARGS__, \ + { \ + .name = NULL, .type = TYPE_NULL, .value = {.as_int64 = 0}, .piiKind = 0 \ + } \ + } + +#define _STR(key, val) \ + { \ + .name = key, .type = TYPE_STRING, .value = {.as_string = val}, .piiKind = 0 \ + } +#define _INT(key, val) \ + { \ + .name = key, .type = TYPE_INT64, .value = {.as_int64 = val}, .piiKind = 0 \ + } +#define _DBL(key, val) \ + { \ + .name = key, .type = TYPE_DOUBLE, .value = {.as_double = val}, .piiKind = 0 \ + } +#define _BOOL(key, val) \ + { \ + .name = key, .type = TYPE_BOOLEAN, .value = {.as_bool = val}, .piiKind = 0 \ + } +#define _GUID(key, val) \ + { \ + .name = key, .type = TYPE_GUID, .value = {.as_string = val}, .piiKind = 0 \ + } +#define _TIME(key, val) \ + { \ + .name = key, .type = TYPE_TIME, .value = {.as_time = val}, .piiKind = 0 \ + } + +#define PII_STR(key, val, kind) \ + { \ + .name = key, .type = TYPE_STRING, .value = {.as_string = val}, .piiKind = kind \ + } +#define PII_INT(key, val, kind) \ + { \ + .name = key, .type = TYPE_INT64, .value = {.as_int64 = val}, .piiKind = kind \ + } +#define PII_DBL(key, val, kind) \ + { \ + .name = key, .type = TYPE_DOUBLE, .value = {.as_double = val}, .piiKind = kind \ + } +#define PII_BOOL(key, val, kind) \ + { \ + .name = key, .type = TYPE_BOOLEAN, .value = {.as_bool = val}, .piiKind = kind \ + } +#define PII_GUID(key, val, kind) \ + { \ + .name = key, .type = TYPE_GUID, .value = {.as_string = val}, .piiKind = kind \ + } +#define PII_TIME(key, val, kind) \ + { \ + .name = key, .type = TYPE_TIME, .value = {.as_time = val}, .piiKind = kind \ + } #endif - typedef evt_status_t(EVTSDK_LIBABI_CDECL *evt_app_call_t)(evt_context_t *); + typedef evt_status_t(EVTSDK_LIBABI_CDECL* evt_app_call_t)(evt_context_t*); - EVTSDK_LIBABI evt_status_t EVTSDK_LIBABI_CDECL evt_api_call_default(evt_context_t *ctx); + EVTSDK_LIBABI evt_status_t EVTSDK_LIBABI_CDECL evt_api_call_default(evt_context_t* ctx); #ifdef _MSC_VER /* User of the library may delay-load the invocation of __impl_evt_api_call to assign their own implementation */ __declspec(selectany) evt_app_call_t evt_api_call = evt_api_call_default; #else - /* Implementation of evt_api_call can be provided by the executable module that includes this header */ - __attribute__((weak)) evt_app_call_t evt_api_call = evt_api_call_default; +/* Implementation of evt_api_call can be provided by the executable module that includes this header */ +__attribute__((weak)) evt_app_call_t evt_api_call = evt_api_call_default; #endif /** @@ -377,17 +478,17 @@ extern "C" { // Unable to load alternate implementation return -1; #else - /* TODO: + /* TODO: * - provide implementation for Linux and Mac * - consider accepting a library path rather than a library handle for dlopen */ - evt_context_t ctx; - ctx.call = EVT_OP_LOAD; - ctx.handle = handle; - return evt_api_call(&ctx); + evt_context_t ctx; + ctx.call = EVT_OP_LOAD; + ctx.handle = handle; + return evt_api_call(&ctx); #endif } - + /** * * Unloads SDK instance loaded with evt_load @@ -404,7 +505,7 @@ extern "C" { ctx.handle = handle; return evt_api_call(&ctx); } - + /** * * Create or open existing SDK instance. @@ -416,7 +517,7 @@ extern "C" { { evt_context_t ctx; ctx.call = EVT_OP_OPEN; - ctx.data = (void *)config; + ctx.data = (void*)config; evt_api_call(&ctx); return ctx.handle; } @@ -442,7 +543,7 @@ extern "C" { evt_context_t ctx; ctx.call = EVT_OP_OPEN_WITH_PARAMS; - ctx.data = (void *)(&data); + ctx.data = (void*)(&data); evt_api_call(&ctx); return ctx.handle; } @@ -461,7 +562,7 @@ extern "C" { ctx.handle = handle; return evt_api_call(&ctx); } - + /** * * Configure SDK instance using configuration provided. @@ -475,10 +576,10 @@ extern "C" { evt_context_t ctx; ctx.call = EVT_OP_CONFIG; ctx.handle = handle; - ctx.data = (void *)config; + ctx.data = (void*)config; return evt_api_call(&ctx); } - + /** * * Logs a telemetry event (security-enhanced _s function) @@ -493,7 +594,7 @@ extern "C" { evt_context_t ctx; ctx.call = EVT_OP_LOG; ctx.handle = handle; - ctx.data = (void *)evt; + ctx.data = (void*)evt; ctx.size = size; return evt_api_call(&ctx); } @@ -513,7 +614,7 @@ extern "C" { evt_context_t ctx; ctx.call = EVT_OP_LOG; ctx.handle = handle; - ctx.data = (void *)evt; + ctx.data = (void*)evt; ctx.size = 0; return evt_api_call(&ctx); } @@ -528,7 +629,7 @@ extern "C" { #define evt_log(handle, evt) evt_log_s(handle, EVT_ARRAY_SIZE(evt), evt) #endif #endif - + /** * * Pauses transmission. In that mode events stay in ram or saved to disk, not sent. @@ -543,7 +644,7 @@ extern "C" { ctx.handle = handle; return evt_api_call(&ctx); } - + /** * * Resumes transmission. Pending telemetry events should be attempted to be sent. @@ -558,7 +659,7 @@ extern "C" { ctx.handle = handle; return evt_api_call(&ctx); } - + /** * Provide a hint to telemetry system to attempt force-upload of events * without waiting for the next batch timer interval. This API does not @@ -574,7 +675,7 @@ extern "C" { ctx.handle = handle; return evt_api_call(&ctx); } - + /** * Save pending telemetry events to offline storage on disk. * @@ -588,7 +689,7 @@ extern "C" { ctx.handle = handle; return evt_api_call(&ctx); } - + /** * Pass down SDK header version to SDK library. Needed for late binding version checking. * This method provides means of a handshake between library header and a library impl. @@ -598,14 +699,14 @@ extern "C" { * SDK header semver. * SDK library semver */ - static inline const char * evt_version() + static inline const char* evt_version() { - static const char * libSemver = TELEMETRY_EVENTS_VERSION; + static const char* libSemver = TELEMETRY_EVENTS_VERSION; evt_context_t ctx; ctx.call = EVT_OP_VERSION; ctx.data = (void*)libSemver; evt_api_call(&ctx); - return (const char *)(ctx.data); + return (const char*)(ctx.data); } /* New API calls to be added using evt_api_call(&ctx) for backwards-forward / ABI compat */ diff --git a/wrappers/netcore/EventNativeAPI.cs b/wrappers/netcore/EventNativeAPI.cs index 44b6a7650..c18a594c8 100644 --- a/wrappers/netcore/EventNativeAPI.cs +++ b/wrappers/netcore/EventNativeAPI.cs @@ -1,5 +1,6 @@ #pragma warning disable IDE1006 // ignore naming rule violations: we preserve original C API naming for clarity here #pragma warning disable IDE0044 // ignore readonly suggestion for field passed over P/Invoke +#undef TRACE using System; using System.Runtime.InteropServices; @@ -23,7 +24,7 @@ public class Constants public const string VERSION = "3.3.0-netcore"; } - public enum EventCallType + public enum EventCallType : UInt32 { EVT_OP_LOAD = 0x00000001, EVT_OP_UNLOAD = 0x00000002, @@ -40,7 +41,7 @@ public enum EventCallType EVT_OP_MAX = EVT_OP_OPEN_WITH_PARAMS + 1 } - public enum EventPropertyType + public enum EventPropertyType : UInt32 { /* Basic types */ TYPE_STRING = 0, @@ -60,19 +61,39 @@ public enum EventPropertyType TYPE_NULL = 12 } - public struct PiiValue + public struct PiiKind { - public string value; - public UInt32 piKind; + /// No PII kind. + public const int None = 0; + /// An LDAP distinguished name. + public const int DistinguishedName = 1; + /// Generic data. + public const int GenericData = 2; + /// An IPV4 Internet address. + public const int IPv4Address = 3; + /// An IPV6 Internet address. + public const int IPv6Address = 4; + /// An e-mail subject. + public const int MailSubject = 5; + /// A telephone number. + public const int PhoneNumber = 6; + /// A query string. + public const int QueryString = 7; + /// A SIP address + public const int SipAddress = 8; + /// An e-mail address. + public const int SmtpAddress = 9; + /// An identity. + public const int Identity = 10; + /// A uniform resource indicator. + public const int Uri = 11; + /// A fully-qualified domain name. + public const int Fqdn = 12; + /// A legacy IPV4 Internet address. + public const int IPv4AddressLegacy = 13; + public const int CustomerData = 32; } - public class EventProperties : Dictionary - { - public EventProperties() - { - } - }; - [StructLayout(LayoutKind.Explicit, Size = 16, CharSet = CharSet.Ansi)] public unsafe struct EventGUIDType { @@ -106,14 +127,14 @@ public unsafe struct EventGUIDType fixed byte Data4[8]; } - [StructLayout(LayoutKind.Explicit, Size = 24, CharSet = CharSet.Ansi)] - public struct EventContextType + [StructLayout(LayoutKind.Explicit, Size = 28, CharSet = CharSet.Ansi)] + public unsafe struct EventContextType { [FieldOffset(0)] public UInt32 call; [FieldOffset(4)] public ulong handle; [FieldOffset(12)] public IntPtr data; - [FieldOffset(16)] public UInt32 result; - [FieldOffset(20)] public UInt32 size; + [FieldOffset(20)] public UInt32 result; + [FieldOffset(24)] public UInt32 size; } public enum EventOpenParamType @@ -126,12 +147,35 @@ public enum EventOpenParamType } [StructLayout(LayoutKind.Explicit, Size = 12, CharSet = CharSet.Ansi)] - public struct EventOpenParam + public unsafe struct EventOpenParam { [FieldOffset(0)] public UInt32 type; [FieldOffset(4)] public IntPtr data; }; + [StructLayout(LayoutKind.Explicit, Size = 8, CharSet = CharSet.Ansi)] + public unsafe struct EventPropertyValue + { + /* Basic types */ + [FieldOffset(0)] public UInt64 as_uint64; + [FieldOffset(0)] public IntPtr as_string; + [FieldOffset(0)] public Int64 as_int64; + [FieldOffset(0)] public double as_double; + [FieldOffset(0)] public bool as_bool; + [FieldOffset(0)] public IntPtr as_guid; + [FieldOffset(0)] public UInt64 as_time; +#if FALSE + /* We don't support passing arrays yet. Code below needs to be ported from C++ to C# */ + /* Array types are nullptr-terminated array of pointers */ + char** as_arr_string; + int64_t** as_arr_int64; + bool** as_arr_bool; + double** as_arr_double; + evt_guid_t** as_arr_guid; + uint64_t** as_arr_time; +#endif + }; + /** * * Wraps logger configuration string and all input parameters to 'evt_open_with_params' @@ -146,43 +190,20 @@ public struct EventOpenParam // custom SDK build flag that enforces certain struct layout. i.e. // positioning the two pointers below as 64-bit integers instead of 32-bit. [StructLayout(LayoutKind.Explicit, Size = 20, CharSet = CharSet.Ansi)] - public struct EventOpenWithParamsDataType + public unsafe struct EventOpenWithParamsDataType { [FieldOffset(0)] public IntPtr config; [FieldOffset(8)] public IntPtr parameters; /* pointer to array of EventOpenParam */ [FieldOffset(8)] public UInt32 paramsCount; } - [StructLayout(LayoutKind.Explicit, Size = 8, CharSet = CharSet.Ansi)] - public struct EventPropertyValue - { - /* Basic types */ - [FieldOffset(0)] UInt64 as_uint64; - [FieldOffset(0)] IntPtr as_string; - [FieldOffset(0)] Int64 as_int64; - [FieldOffset(0)] double as_double; - [FieldOffset(0)] bool as_bool; - [FieldOffset(0)] IntPtr as_guid; - [FieldOffset(0)] UInt64 as_time; -#if FALSE - /* We don't support passing arrays yet. Code below needs to be ported from C++ to C# */ - /* Array types are nullptr-terminated array of pointers */ - char** as_arr_string; - int64_t** as_arr_int64; - bool** as_arr_bool; - double** as_arr_double; - evt_guid_t** as_arr_guid; - uint64_t** as_arr_time; -#endif - }; - - [StructLayout(LayoutKind.Explicit, Size = 16, CharSet = CharSet.Ansi)] - public struct EventProperty + [StructLayout(LayoutKind.Explicit, Size = 24, CharSet = CharSet.Ansi)] + public unsafe struct EventPropertyKeyValue { - [FieldOffset(0)] IntPtr name; - [FieldOffset(4)] EventPropertyType type; - [FieldOffset(8)] EventPropertyValue value; - [FieldOffset(12)] UInt32 piiKind; + [FieldOffset(0)] public IntPtr name; + [FieldOffset(8)] public EventPropertyType type; + [FieldOffset(12)] public EventPropertyValue value; + [FieldOffset(20)] public UInt32 piiKind; } /** @@ -196,6 +217,174 @@ public enum HttpRequestType HTTP_REQUEST_TYPE_POST = 1, } + public class EventProperties : Dictionary + { + + public IntPtr nativeBuffer = IntPtr.Zero; + public int nativeSize = 0; + public int szEvtPropKV = Marshal.SizeOf(typeof(EventPropertyKeyValue)); + + public EventProperties() + { + } + internal unsafe void AllocNative() + { + nativeSize = (Count+1) * szEvtPropKV; + nativeBuffer = Marshal.AllocHGlobal(nativeSize);// sizeof(EventPropertyKeyValue)); + int i = 0; + EventPropertyKeyValue* propPtr = (EventPropertyKeyValue*)(IntPtr.Zero); + foreach (KeyValuePair item in this) + { + propPtr = (EventPropertyKeyValue*)(nativeBuffer) + i; + (*propPtr).name = Marshal.StringToHGlobalAnsi(item.Key); + (*propPtr).piiKind = 0; // TODO: add Pii Kind support + (*propPtr).type = item.Value.type; +#if (TRACE) + Console.Write("0x{0:X} ", (long)propPtr); +#endif + switch (item.Value.type) + { + case EventPropertyType.TYPE_BOOLEAN: + (*propPtr).value.as_bool = item.Value.value.as_bool; +#if (TRACE) + Console.WriteLine("boolean {0}={1}", item.Key, (*propPtr).value.as_bool); +#endif + break; + case EventPropertyType.TYPE_DOUBLE: + (*propPtr).value.as_double = item.Value.value.as_double; +#if (TRACE) + Console.WriteLine("double {0}={1}", item.Key, (*propPtr).value.as_double); +#endif + break; + case EventPropertyType.TYPE_GUID: + // TODO: not implemented +#if (TRACE) + Console.WriteLine("guid {0}={1}", item.Key, (*propPtr).value.as_guid); +#endif + break; + case EventPropertyType.TYPE_INT64: + (*propPtr).value.as_int64 = item.Value.value.as_int64; +#if (TRACE) + Console.WriteLine("int64 {0}={1}", item.Key, (*propPtr).value.as_int64); +#endif + break; + case EventPropertyType.TYPE_STRING: + { + string s = (string)(item.Value.objValue); + (*propPtr).value.as_string = Marshal.StringToHGlobalAnsi(s); + (*propPtr).piiKind = item.Value.piiKind; +#if (TRACE) + Console.WriteLine("string {0}={1} (piiKind={2})", item.Key, s, item.Value.piiKind); +#endif + break; + } + default: + break; + } + i++; + } + /* NULL terminator property at the end of property list */ + propPtr = (EventPropertyKeyValue*)(nativeBuffer) + i; + (*propPtr).name = IntPtr.Zero; + (*propPtr).type = EventPropertyType.TYPE_NULL; + } + + internal unsafe void FreeNative() + { + if (nativeBuffer==IntPtr.Zero) + { + throw new Exception("Memory not allocated!"); + } + // TODO: assert count==Count + int count = nativeSize / szEvtPropKV; + for (int i = 0; i < count; i++) + { + EventPropertyKeyValue* propPtr = (EventPropertyKeyValue*)(nativeBuffer) + i; + EventPropertyType type = (EventPropertyType)((*propPtr).type); + switch (type) + { + case EventPropertyType.TYPE_GUID: + // TODO: not implemented + break; + case EventPropertyType.TYPE_STRING: + { + IntPtr strPtr = (*propPtr).value.as_string; + if (strPtr != IntPtr.Zero) + { + Marshal.FreeHGlobal(strPtr); + } + break; + } + default: + break; + } + if ((*propPtr).name != IntPtr.Zero) + { + Marshal.FreeHGlobal((*propPtr).name); + } + } + Marshal.FreeHGlobal(nativeBuffer); + nativeBuffer = IntPtr.Zero; + nativeSize = 0; + } + } + + public class EventProperty + { + public EventPropertyType type; + public EventPropertyValue value; + public object objValue = null; + public UInt32 piiKind = 0; + + public EventProperty(string strValue) + { + type = EventPropertyType.TYPE_STRING; + objValue = strValue; + } + + public EventProperty(string strValue, UInt32 piiKind) + { + type = EventPropertyType.TYPE_STRING; + objValue = strValue; + this.piiKind = piiKind; + } + + public EventProperty(Guid guidValue) + { + type = EventPropertyType.TYPE_GUID; + objValue = guidValue; + } + + public EventProperty(Int64 intValue) + { + type = EventPropertyType.TYPE_INT64; + value.as_int64 = intValue; + } + + public EventProperty(int intValue) + { + type = EventPropertyType.TYPE_INT64; + value.as_int64 = intValue; + } + + public EventProperty(double doubleValue) + { + type = EventPropertyType.TYPE_DOUBLE; + value.as_double = doubleValue; + } + + public EventProperty(bool boolValue) + { + type = EventPropertyType.TYPE_BOOLEAN; + value.as_bool = boolValue; + } + + public static implicit operator EventProperty(string v) => new EventProperty(v); + public static implicit operator EventProperty(int v) => new EventProperty(v); + public static implicit operator EventProperty(double v) => new EventProperty(v); + + }; + public static class EventNativeAPI { // Conditional compilation: pass different library name depending on target OS @@ -218,6 +407,7 @@ public static ulong evt_open(string cfg) data = Marshal.StringToHGlobalAnsi(cfg) }; evt_api_call(ref context); + Marshal.FreeHGlobal(context.data); return context.handle; } @@ -240,12 +430,21 @@ public static ulong evt_close(ulong inHandle) public static ulong evt_log(ulong inHandle, ref EventProperties properties) { - EventContextType context = new EventContextType + ulong result = 0; + unsafe { - call = (Byte)EventCallType.EVT_OP_LOG, - handle = inHandle + properties.AllocNative(); + EventContextType context = new EventContextType + { + call = (Byte)EventCallType.EVT_OP_LOG, + handle = inHandle, + data = properties.nativeBuffer, + size = 0 /* (uint)(properties.Count) */ + }; + result = evt_api_call(ref context); + properties.FreeNative(); }; - return evt_api_call(ref context); + return result; } /** @@ -337,7 +536,9 @@ public static string evt_version() data = nativeDataPtr }; evt_api_call(ref context); - return Marshal.PtrToStringAnsi(context.data); + string result = Marshal.PtrToStringAnsi(context.data); + Marshal.FreeHGlobal(nativeDataPtr); + return result; } } diff --git a/wrappers/netcore/Program.cs b/wrappers/netcore/Program.cs index bea811e4e..856f0dd2b 100644 --- a/wrappers/netcore/Program.cs +++ b/wrappers/netcore/Program.cs @@ -3,6 +3,7 @@ using System.Text; using System.IO; using System.Json; +using System.Diagnostics; using Microsoft.Telemetry.Core; @@ -35,22 +36,45 @@ static void Main(string[] args) Console.WriteLine("SDK version: {0}", EventNativeAPI.evt_version()); // Initialize - Console.WriteLine("evt_open..."); + Console.WriteLine(">>> evt_open..."); var handle = EventNativeAPI.evt_open(cfg); Console.WriteLine("handle={0}", handle); -#if FALSE - var props = new EventProperties() { - { "strKey", "value1" }, - { "intKey", 12345 }, - { "dblKey", 0.12345 }, - { "guidKey", new Guid("73e21739-9d4e-497d-9c66-8e399a532ec9") } - }; - EventNativeAPI.evt_log(handle, ref props); -#endif + // Initialize + Console.WriteLine(">>> evt_pause..."); + EventNativeAPI.evt_pause(handle); + + Console.WriteLine(">>> evt_log..."); + + long total0 = GC.GetTotalMemory(true); + long frag0 = GC.GetGCMemoryInfo().FragmentedBytes; + Stopwatch sw = new Stopwatch(); + sw.Start(); + + const int MAX_ITERATIONS = 100000; + for (int i = 0; i < MAX_ITERATIONS; i++) + { + var props = new EventProperties() { + { "strKey", "value1" }, + { "intKey", 12345 }, + { "dblKey", 0.12345 } +// , { "guidKey", new Guid("73e21739-9d4e-497d-9c66-8e399a532ec9") } + }; + EventNativeAPI.evt_log(handle, ref props); + } + sw.Stop(); + long total1 = GC.GetTotalMemory(true); + long frag1 = GC.GetGCMemoryInfo().FragmentedBytes; + // Print some benchmarking results for offline storage + serialization + TimeSpan ts = sw.Elapsed; + Console.WriteLine("Elapsed = {0}", ts); + Console.WriteLine("Event rate = {0} eps", (MAX_ITERATIONS/ts.TotalSeconds) ); + Console.WriteLine("Latency = {0} ms", (ts.TotalMilliseconds/MAX_ITERATIONS) ); + Console.WriteLine("Mem used = {0} bytes", total1-total0); + Console.WriteLine("Fragmented = {0} bytes", frag1-frag0); // FlushAndTeardown - Console.WriteLine("evt_close..."); + Console.WriteLine(">>> evt_close..."); var result = EventNativeAPI.evt_close(handle); Console.WriteLine("result={0}", result); } diff --git a/wrappers/netcore/sdk-config.json b/wrappers/netcore/sdk-config.json index 98cbeed13..0b396daac 100644 --- a/wrappers/netcore/sdk-config.json +++ b/wrappers/netcore/sdk-config.json @@ -4,9 +4,9 @@ "version": "1.0.0", "cacheFileFullNotificationPercentage": 75, "cacheFilePath": "/tmp/storage.db", - "cacheFileSizeLimitInBytes": 3145728, + "cacheFileSizeLimitInBytes": 13145728, "cacheMemoryFullNotificationPercentage": 75, - "cacheMemorySizeLimitInBytes": 524288, + "cacheMemorySizeLimitInBytes": 32768000, "compat": { "dotType": true }, @@ -19,7 +19,7 @@ "maxDBFlushQueues": 3, "maxPendingHTTPRequests": 4, "maxTeardownUploadTimeInSec": 1, - "minimumTraceLevel": 1, + "minimumTraceLevel": 5, "multiTenantEnabled": true, "primaryToken": "6d084bbf6a9644ef83f40a77c9e34580-c2d379e0-4408-4325-9b4d-2a7d78131e14-7322", "sample": { @@ -39,7 +39,7 @@ "maxBlobSize": 2097152, "maxRetryCount": 5 }, - "traceLevelMask": 268435455, + "traceLevelMask": 0, "utc": { "providerGroupId": "780dddc8-18a1-5781-895a-a690464fa89c" } From 5a3059cfacffeb32a8dcaa08901f1cd1100879a8 Mon Sep 17 00:00:00 2001 From: Max Golovanov Date: Sat, 18 Jan 2020 19:04:56 -0800 Subject: [PATCH 06/15] Add some support for GUID type (not the final impl) --- wrappers/netcore/EventNativeAPI.cs | 9 ++++++--- wrappers/netcore/Program.cs | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/wrappers/netcore/EventNativeAPI.cs b/wrappers/netcore/EventNativeAPI.cs index c18a594c8..3c0b5c6d1 100644 --- a/wrappers/netcore/EventNativeAPI.cs +++ b/wrappers/netcore/EventNativeAPI.cs @@ -351,8 +351,11 @@ public EventProperty(string strValue, UInt32 piiKind) public EventProperty(Guid guidValue) { - type = EventPropertyType.TYPE_GUID; - objValue = guidValue; + // TODO: compact GUID on wire is not implemented! + // Currently we flatten it to string. + // type = EventPropertyType.TYPE_GUID; + type = EventPropertyType.TYPE_STRING; + objValue = guidValue.ToString(); } public EventProperty(Int64 intValue) @@ -382,7 +385,7 @@ public EventProperty(bool boolValue) public static implicit operator EventProperty(string v) => new EventProperty(v); public static implicit operator EventProperty(int v) => new EventProperty(v); public static implicit operator EventProperty(double v) => new EventProperty(v); - + public static implicit operator EventProperty(Guid v) => new EventProperty(v); }; public static class EventNativeAPI diff --git a/wrappers/netcore/Program.cs b/wrappers/netcore/Program.cs index 856f0dd2b..bb3102d2d 100644 --- a/wrappers/netcore/Program.cs +++ b/wrappers/netcore/Program.cs @@ -57,8 +57,8 @@ static void Main(string[] args) var props = new EventProperties() { { "strKey", "value1" }, { "intKey", 12345 }, - { "dblKey", 0.12345 } -// , { "guidKey", new Guid("73e21739-9d4e-497d-9c66-8e399a532ec9") } + { "dblKey", 0.12345 } , + { "guidKey", new Guid("73e21739-9d4e-497d-9c66-8e399a532ec9") } }; EventNativeAPI.evt_log(handle, ref props); } From 3e0c9a6f05d5cc3783e31772bab051a2961add9e Mon Sep 17 00:00:00 2001 From: Max Golovanov Date: Thu, 24 Sep 2020 23:04:52 -0700 Subject: [PATCH 07/15] Verify that the .NET Core wrapper sample works on Windows --- wrappers/netcore/EventNativeAPI.cs | 7 +++---- wrappers/netcore/EventSender.csproj | 3 +++ wrappers/netcore/EventSender.xml | 3 +++ wrappers/netcore/Program.cs | 2 +- wrappers/netcore/README.md | 19 +++++++++++++++++++ wrappers/netcore/run.cmd | 2 ++ 6 files changed, 31 insertions(+), 5 deletions(-) create mode 100644 wrappers/netcore/EventSender.xml create mode 100644 wrappers/netcore/README.md create mode 100644 wrappers/netcore/run.cmd diff --git a/wrappers/netcore/EventNativeAPI.cs b/wrappers/netcore/EventNativeAPI.cs index 3c0b5c6d1..8fde9915c 100644 --- a/wrappers/netcore/EventNativeAPI.cs +++ b/wrappers/netcore/EventNativeAPI.cs @@ -19,9 +19,8 @@ namespace Core { public class Constants { - public const string LIBRARY_NAME_POSIX = "libmat"; - public const string LIBRARY_NAME_WIN32 = "ClientTelemetry.dll"; - public const string VERSION = "3.3.0-netcore"; + public const string LIBRARY_NAME = "ClientTelemetry"; + public const string VERSION = "3.4.0-netcore"; } public enum EventCallType : UInt32 @@ -392,7 +391,7 @@ public static class EventNativeAPI { // Conditional compilation: pass different library name depending on target OS - [DllImport(Constants.LIBRARY_NAME_POSIX, EntryPoint = "evt_api_call_default")] + [DllImport(Constants.LIBRARY_NAME, EntryPoint = "evt_api_call_default")] internal static extern UInt32 evt_api_call([In, Out] ref EventContextType context); /** diff --git a/wrappers/netcore/EventSender.csproj b/wrappers/netcore/EventSender.csproj index 3a42309a4..666be2695 100644 --- a/wrappers/netcore/EventSender.csproj +++ b/wrappers/netcore/EventSender.csproj @@ -6,6 +6,9 @@ + + PreserveNewest + diff --git a/wrappers/netcore/EventSender.xml b/wrappers/netcore/EventSender.xml new file mode 100644 index 000000000..706ff14b5 --- /dev/null +++ b/wrappers/netcore/EventSender.xml @@ -0,0 +1,3 @@ + + + diff --git a/wrappers/netcore/Program.cs b/wrappers/netcore/Program.cs index bb3102d2d..2d7aecf1e 100644 --- a/wrappers/netcore/Program.cs +++ b/wrappers/netcore/Program.cs @@ -71,7 +71,7 @@ static void Main(string[] args) Console.WriteLine("Event rate = {0} eps", (MAX_ITERATIONS/ts.TotalSeconds) ); Console.WriteLine("Latency = {0} ms", (ts.TotalMilliseconds/MAX_ITERATIONS) ); Console.WriteLine("Mem used = {0} bytes", total1-total0); - Console.WriteLine("Fragmented = {0} bytes", frag1-frag0); + // Console.WriteLine("Fragmented = {0} bytes", frag1-frag0); // FlushAndTeardown Console.WriteLine(">>> evt_close..."); diff --git a/wrappers/netcore/README.md b/wrappers/netcore/README.md new file mode 100644 index 000000000..a74a77c07 --- /dev/null +++ b/wrappers/netcore/README.md @@ -0,0 +1,19 @@ +# .NET Core wrapper example for 1DS C/C++ SDK + +Note that this wrapper is incomplete simple reference implementation that illustrates how to use 1DS C API from cross-platform .NET Core application. + +## POSIX instructions (Linux, Mac) + +1. Install latest .NET Core for your platform. + +2. Make sure you compile and install shared library build of SDK (`build.sh -l shared`). + +3. `run.sh` to compile and run the sample wrapper. + +## Windows instructions + +1. Install latest .NET Core for your platform. + +2. Open `Solutions\MSTelemetry.sln` and compile `win32-dll` project, producing `ClientTelemetry.dll` + +3. `run.cmd` to compile and run the sample wrapper. diff --git a/wrappers/netcore/run.cmd b/wrappers/netcore/run.cmd new file mode 100644 index 000000000..d31eb7c7e --- /dev/null +++ b/wrappers/netcore/run.cmd @@ -0,0 +1,2 @@ +set "PATH=%CD%\..\..\Solutions\out\Release\x64\win32-dll;%PATH%" +dotnet run -c Release From ef6ccbd6522ade999f94a8bfc07fdc38e1cda045 Mon Sep 17 00:00:00 2001 From: Max Golovanov Date: Sun, 12 Feb 2023 14:10:48 -0800 Subject: [PATCH 08/15] Move packing behavior where it would fit better - ctmacros.hpp --- lib/include/public/ctmacros.hpp | 20 ++++++++++++++++++ lib/include/public/mat.h | 36 ++++++++++----------------------- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/lib/include/public/ctmacros.hpp b/lib/include/public/ctmacros.hpp index 42547e41d..b32b4b9b6 100644 --- a/lib/include/public/ctmacros.hpp +++ b/lib/include/public/ctmacros.hpp @@ -127,4 +127,24 @@ #define EVTSDK_LIBABI_CDECL MATSDK_LIBABI_CDECL #define EVTSDK_SPEC MATSDK_SPEC +/* Implement struct packing for stable FFI C API */ +#ifdef __clang__ +# define MATSDK_PACKED_STRUCT __attribute__((packed)) +# define MATSDK_PACK_PUSH +# define MATSDK_PACK_POP +#elif __GNUC__ +# define MATSDK_PACKED_STRUCT __attribute__((packed)) +# define MATSDK_PACK_PUSH +# define MATSDK_PACK_POP +#elif _MSC_VER +# define MATSDK_PACKED_STRUCT +# define MATSDK_PACK_PUSH __pragma(pack(push, 1)) +# define MATSDK_PACK_POP __pragma(pack(pop)) +#else +/* No packing behavior on unknown compilers */ +# define MATSDK_PACKED_STRUCT +# define MATSDK_PACK_PUSH +# define MATSDK_PACK_POP +#endif + #endif diff --git a/lib/include/public/mat.h b/lib/include/public/mat.h index 6fd65da85..fd148fe1d 100644 --- a/lib/include/public/mat.h +++ b/lib/include/public/mat.h @@ -17,20 +17,6 @@ #include #endif -#ifdef __clang__ -#define PACKED_STRUCT __attribute__((packed)) -#define PACK_PUSH -#define PACK_POP -#elif __GNUC__ -#define PACKED_STRUCT __attribute__((packed)) -#define PACK_PUSH -#define PACK_POP -#else -#define PACKED_STRUCT -#define PACK_PUSH __pragma(pack(push, 1)) -#define PACK_POP __pragma(pack(pop)) -#endif - #if (_MSC_VER == 1500) || (_MSC_VER == 1600) /* Visual Studio 2010 */ typedef __int64 int64_t; @@ -59,7 +45,7 @@ typedef int bool; extern "C" { #endif - typedef enum evt_call_t /* 32-bit */ + typedef enum evt_call_t { EVT_OP_LOAD = 0x00000001, EVT_OP_UNLOAD = 0x00000002, @@ -77,7 +63,7 @@ extern "C" { EVT_OP_MAXINT = 0xFFFFFFFF } evt_call_t; - typedef enum evt_prop_t /* 32-bit */ + typedef enum evt_prop_t { /* Basic types */ TYPE_STRING, @@ -98,9 +84,9 @@ extern "C" { TYPE_MAXINT = 0xFFFFFFFF } evt_prop_t; - typedef struct PACKED_STRUCT evt_guid_t + typedef struct MATSDK_PACKED_STRUCT evt_guid_t { -PACK_PUSH +MATSDK_PACK_PUSH /** * * Specifies the first eight hexadecimal digits of the GUID. @@ -128,22 +114,22 @@ PACK_PUSH * */ uint8_t Data4[8]; -PACK_POP +MATSDK_PACK_POP } evt_guid_t; typedef int64_t evt_handle_t; typedef int32_t evt_status_t; typedef struct evt_event evt_event; - typedef struct PACKED_STRUCT evt_context_t + typedef struct MATSDK_PACKED_STRUCT evt_context_t { -PACK_PUSH +MATSDK_PACK_PUSH evt_call_t call; /* In */ evt_handle_t handle; /* In / Out */ void* data; /* In / Out */ evt_status_t result; /* Out */ uint32_t size; /* In / Out */ -PACK_POP +MATSDK_PACK_POP } evt_context_t; /** @@ -203,14 +189,14 @@ PACK_POP uint64_t** as_arr_time; } evt_prop_v; - typedef struct PACKED_STRUCT evt_prop + typedef struct MATSDK_PACKED_STRUCT evt_prop { -PACK_PUSH +MATSDK_PACK_PUSH const char* name; evt_prop_t type; evt_prop_v value; uint32_t piiKind; -PACK_POP +MATSDK_PACK_POP } evt_prop; /** From 2faa5c195bb03604fc5bf7a2219dd86d9614fb27 Mon Sep 17 00:00:00 2001 From: Max Golovanov Date: Sun, 12 Feb 2023 15:05:50 -0800 Subject: [PATCH 09/15] There really is 8 of them, not 7 --- examples/cpp/SampleCppMini/main.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/cpp/SampleCppMini/main.cpp b/examples/cpp/SampleCppMini/main.cpp index 3cddf79a2..1d371ced6 100644 --- a/examples/cpp/SampleCppMini/main.cpp +++ b/examples/cpp/SampleCppMini/main.cpp @@ -31,7 +31,7 @@ void test_cpp_api(const char * token, int ticketType, const char *ticket) if (ticket != nullptr) { - const char *ticketNames[7] = + const char *ticketNames[8] = { "TicketType_MSA_Device", "TicketType_MSA_User", @@ -40,7 +40,7 @@ void test_cpp_api(const char * token, int ticketType, const char *ticket) "TicketType_AAD", "TicketType_AAD_User", "TicketType_AAD_JWT", - "TicketType_AAD_Device", + "TicketType_AAD_Device" }; printf("\nSet ticket %s=%s\n", ticketNames[ticketType], ticket); auto tc = LogManager::GetAuthTokensController(); From d2bbc6132877e5e5d9485bf10b4db3ebde4a4d01 Mon Sep 17 00:00:00 2001 From: Max Golovanov Date: Sun, 12 Feb 2023 19:28:08 -0800 Subject: [PATCH 10/15] Clean-up C# side --- wrappers/netcore/Program.cs | 108 +++++++++++++++++++++---------- wrappers/netcore/sdk-config.json | 98 +++++++++++++++------------- 2 files changed, 129 insertions(+), 77 deletions(-) diff --git a/wrappers/netcore/Program.cs b/wrappers/netcore/Program.cs index 2d7aecf1e..e1767003d 100644 --- a/wrappers/netcore/Program.cs +++ b/wrappers/netcore/Program.cs @@ -12,6 +12,12 @@ namespace EventSender class Program { + + /// + /// Read 1DS SDK configuration from JSON configuration file. + /// + /// + /// static string ReadConfiguration(string filename) { string result = ""; @@ -22,6 +28,41 @@ static string ReadConfiguration(string filename) return result; } + /// + /// Run action in a loop and measure common performance characteristics. + /// + /// + /// + static void StressTest(Action action, int maxIterations) + { + long total0 = GC.GetTotalMemory(true); + long frag0 = GC.GetGCMemoryInfo().FragmentedBytes; + Stopwatch sw = new Stopwatch(); + sw.Start(); + + for (int i = 0; i < maxIterations; i++) + { + action(i); + } + + sw.Stop(); + long total1 = GC.GetTotalMemory(true); + long frag1 = GC.GetGCMemoryInfo().FragmentedBytes; + // Print some benchmarking results for offline storage + serialization + TimeSpan ts = sw.Elapsed; + Console.WriteLine("Elapsed = {0}", ts); + Console.WriteLine("Event rate = {0} eps", + (ts.TotalMilliseconds != 0) ? + (maxIterations / ts.TotalSeconds) : 1000); + Console.WriteLine("Latency = {0} ms", (ts.TotalMilliseconds / maxIterations)); + Console.WriteLine("Mem used = {0} bytes", total1 - total0); + } + + /// + /// Small demo how to use 1DS .NET Core API routed via 1DS C API. + /// All features of C API are supported. + /// + /// static void Main(string[] args) { Console.WriteLine("Reading configuration..."); @@ -40,43 +81,44 @@ static void Main(string[] args) var handle = EventNativeAPI.evt_open(cfg); Console.WriteLine("handle={0}", handle); - // Initialize - Console.WriteLine(">>> evt_pause..."); - EventNativeAPI.evt_pause(handle); - + // Log something Console.WriteLine(">>> evt_log..."); + var props = new EventProperties() { + { "name", "SampleNetCore" }, + { "strKey", "value1" }, + { "intKey", 12345 }, + { "dblKey", 0.12345 } , + { "guidKey", new Guid("73e21739-9d4e-497d-9c66-8e399a532ec9") } + }; + EventNativeAPI.evt_log(handle, ref props); - long total0 = GC.GetTotalMemory(true); - long frag0 = GC.GetGCMemoryInfo().FragmentedBytes; - Stopwatch sw = new Stopwatch(); - sw.Start(); - - const int MAX_ITERATIONS = 100000; - for (int i = 0; i < MAX_ITERATIONS; i++) - { - var props = new EventProperties() { - { "strKey", "value1" }, - { "intKey", 12345 }, - { "dblKey", 0.12345 } , - { "guidKey", new Guid("73e21739-9d4e-497d-9c66-8e399a532ec9") } - }; - EventNativeAPI.evt_log(handle, ref props); - } - sw.Stop(); - long total1 = GC.GetTotalMemory(true); - long frag1 = GC.GetGCMemoryInfo().FragmentedBytes; - // Print some benchmarking results for offline storage + serialization - TimeSpan ts = sw.Elapsed; - Console.WriteLine("Elapsed = {0}", ts); - Console.WriteLine("Event rate = {0} eps", (MAX_ITERATIONS/ts.TotalSeconds) ); - Console.WriteLine("Latency = {0} ms", (ts.TotalMilliseconds/MAX_ITERATIONS) ); - Console.WriteLine("Mem used = {0} bytes", total1-total0); - // Console.WriteLine("Fragmented = {0} bytes", frag1-frag0); + // Now let's run a small stress test... + StressTest( + (param1) => + { + var props = new EventProperties() { + { "name", "SampleNetCore.PerfTest" }, + { "intKey", param1 }, + }; + EventNativeAPI.evt_log(handle, ref props); + } + , 10 // number of iterations + ); - // FlushAndTeardown + ulong result = 0; + + // Flush events to storage + result = EventNativeAPI.evt_flush(handle); + Console.WriteLine("result={0}", result); + + // Force upload of all pending events + result = EventNativeAPI.evt_upload(handle); + Console.WriteLine("result={0}", result); + + // FlushAndTeardown Console.WriteLine(">>> evt_close..."); - var result = EventNativeAPI.evt_close(handle); - Console.WriteLine("result={0}", result); + result = EventNativeAPI.evt_close(handle); + Console.WriteLine("result={0}", result); } } } diff --git a/wrappers/netcore/sdk-config.json b/wrappers/netcore/sdk-config.json index 0b396daac..dfc4796ed 100644 --- a/wrappers/netcore/sdk-config.json +++ b/wrappers/netcore/sdk-config.json @@ -1,46 +1,56 @@ { - "config": {"host": "*"}, - "name": "C-API-Client-0", - "version": "1.0.0", - "cacheFileFullNotificationPercentage": 75, - "cacheFilePath": "/tmp/storage.db", - "cacheFileSizeLimitInBytes": 13145728, - "cacheMemoryFullNotificationPercentage": 75, - "cacheMemorySizeLimitInBytes": 32768000, - "compat": { - "dotType": true - }, - "enableLifecycleSession": false, - "eventCollectorUri": "https://self.events.data.microsoft.com/OneCollector/1.0/", - "hostMode": true, - "http": { - "compress": true - }, - "maxDBFlushQueues": 3, - "maxPendingHTTPRequests": 4, - "maxTeardownUploadTimeInSec": 1, - "minimumTraceLevel": 5, - "multiTenantEnabled": true, - "primaryToken": "6d084bbf6a9644ef83f40a77c9e34580-c2d379e0-4408-4325-9b4d-2a7d78131e14-7322", - "sample": { - "rate": 0 - }, - "sdkmode": 0, - "skipSqliteInitAndShutdown": null, - "stats": { - "interval": 5, - "split": false, - "tokenInt": "8130ef8ff472405d89d6f420038927ea-0c0d561e-cca5-4c81-90ed-0aa9ad786a03-7166", - "tokenProd": "4bb4d6f7cafc4e9292f972dca2dcde42-bd019ee8-e59c-4b0f-a02c-84e72157a3ef-7485" - }, - "tpm": { - "backoffConfig": "E,3000,300000,2,1", - "clockSkewEnabled": true, - "maxBlobSize": 2097152, - "maxRetryCount": 5 - }, - "traceLevelMask": 0, - "utc": { - "providerGroupId": "780dddc8-18a1-5781-895a-a690464fa89c" - } + "config": { + "host": "*", + "scope": "*" + }, + "name": "C-API-Client-0", + "version": "1.0.0", + "cacheFileFullNotificationPercentage": 75, + "cacheFilePath": "offline-storage.db", + "cacheFileSizeLimitInBytes": 3145728, + "cacheFullNotificationIntervalTime": 5000, + "cacheMemoryFullNotificationPercentage": 75, + "cacheMemorySizeLimitInBytes": 524288, + "compat": { + "customTypePrefix": "custom", + "dotType": true + }, + "enableDbDropIfFull": false, + "enableLifecycleSession": false, + "enableNetworkDetector": true, + "enableTrace": true, + "eventCollectorUri": "https://mobile.events.data.microsoft.com/OneCollector/1.0/", + "hostMode": true, + "http": { + "compress": false, + "contentEncoding": "deflate", + "msRootCheck": false + }, + "maxDBFlushQueues": 3, + "maxPendingHTTPRequests": 4, + "maxTeardownUploadTimeInSec": 5, + "minimumTraceLevel": 1, + "multiTenantEnabled": true, + "primaryToken": "fba3c287ba474016b77e0ab612175255-8ef3561c-6103-4027-8ca0-2c79dcbd8901-6902", + "sample": { + "rate": 0 + }, + "sdkmode": 0, + "sessionResetEnabled": false, + "stats": { + "interval": 0, + "split": false, + "tokenInt": "8130ef8ff472405d89d6f420038927ea-0c0d561e-cca5-4c81-90ed-0aa9ad786a03-7166", + "tokenProd": "4bb4d6f7cafc4e9292f972dca2dcde42-bd019ee8-e59c-4b0f-a02c-84e72157a3ef-7485" + }, + "tpm": { + "backoffConfig": "E,3000,300000,2,1", + "clockSkewEnabled": true, + "maxBlobSize": 2097152, + "maxRetryCount": 5 + }, + "traceLevelMask": 4294967295, + "utc": { + "enabled": false + } } \ No newline at end of file From 3266cd90c61aec7a32ee21e45309710566464b27 Mon Sep 17 00:00:00 2001 From: Max Golovanov Date: Sun, 12 Feb 2023 19:29:01 -0800 Subject: [PATCH 11/15] Allow C API to decide what token to use: LogManager primaryToken or iKey specified on event --- lib/api/capi.cpp | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/api/capi.cpp b/lib/api/capi.cpp index 3e0d9bd53..13d9f5b5d 100644 --- a/lib/api/capi.cpp +++ b/lib/api/capi.cpp @@ -242,10 +242,24 @@ evt_status_t mat_log(evt_context_t *ctx) EventProperties props; props.unpack(evt, ctx->size); + // Determine ingestion token to use for this record. + std::string token; + + // Use LogManager configuration primary token if available. + if (config.HasConfig(CFG_STR_PRIMARY_TOKEN)) + { + token = static_cast(config[CFG_STR_PRIMARY_TOKEN]); + } + + // Allow to override iKey per event via property. + // C API client maintains one handle for different tenants. auto m = props.GetProperties(); - EventProperty &prop = m[COMMONFIELDS_IKEY]; - std::string token = prop.as_string; - props.erase(COMMONFIELDS_IKEY); + if (m.count(COMMONFIELDS_IKEY)) + { + EventProperty& prop = m[COMMONFIELDS_IKEY]; + token = prop.as_string; + props.erase(COMMONFIELDS_IKEY); + } // Privacy feature for OTEL C API client: // From 515e059f79d219832d229fe760450be1fd97f8a8 Mon Sep 17 00:00:00 2001 From: Max Golovanov Date: Sun, 12 Feb 2023 19:29:41 -0800 Subject: [PATCH 12/15] Since packing is a potentially ABI-breaking change, need to update the version to current v3.7 --- lib/include/public/mat.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/include/public/mat.h b/lib/include/public/mat.h index fd148fe1d..a633b1b89 100644 --- a/lib/include/public/mat.h +++ b/lib/include/public/mat.h @@ -9,7 +9,7 @@ * For version handshake check there is no mandatory requirement to update the $PATCH level. * Ref. https://semver.org/ for Semantic Versioning documentation. */ -#define TELEMETRY_EVENTS_VERSION "3.4.0" +#define TELEMETRY_EVENTS_VERSION "3.7.0" #include "ctmacros.hpp" From 7f525d9a10c1303c20c8f9b17c765b88d23c86c4 Mon Sep 17 00:00:00 2001 From: Max Golovanov Date: Sun, 12 Feb 2023 19:31:26 -0800 Subject: [PATCH 13/15] Minor bugfix --- wrappers/netcore/EventNativeAPI.cs | 20 ++++++++++++++------ wrappers/netcore/EventSender.csproj | 13 ++++++++++++- wrappers/netcore/EventSender.xml | 1 + 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/wrappers/netcore/EventNativeAPI.cs b/wrappers/netcore/EventNativeAPI.cs index 8fde9915c..e861c8f9e 100644 --- a/wrappers/netcore/EventNativeAPI.cs +++ b/wrappers/netcore/EventNativeAPI.cs @@ -20,7 +20,7 @@ namespace Core public class Constants { public const string LIBRARY_NAME = "ClientTelemetry"; - public const string VERSION = "3.4.0-netcore"; + public const string VERSION = "3.7.0-netcore"; } public enum EventCallType : UInt32 @@ -158,9 +158,9 @@ public unsafe struct EventPropertyValue /* Basic types */ [FieldOffset(0)] public UInt64 as_uint64; [FieldOffset(0)] public IntPtr as_string; - [FieldOffset(0)] public Int64 as_int64; + [FieldOffset(0)] public Int64 as_int64; [FieldOffset(0)] public double as_double; - [FieldOffset(0)] public bool as_bool; + [FieldOffset(0)] public bool as_bool; [FieldOffset(0)] public IntPtr as_guid; [FieldOffset(0)] public UInt64 as_time; #if FALSE @@ -228,7 +228,7 @@ public EventProperties() } internal unsafe void AllocNative() { - nativeSize = (Count+1) * szEvtPropKV; + nativeSize = (Count + 1) * szEvtPropKV; nativeBuffer = Marshal.AllocHGlobal(nativeSize);// sizeof(EventPropertyKeyValue)); int i = 0; EventPropertyKeyValue* propPtr = (EventPropertyKeyValue*)(IntPtr.Zero); @@ -290,7 +290,7 @@ internal unsafe void AllocNative() internal unsafe void FreeNative() { - if (nativeBuffer==IntPtr.Zero) + if (nativeBuffer == IntPtr.Zero) { throw new Exception("Memory not allocated!"); } @@ -430,6 +430,14 @@ public static ulong evt_close(ulong inHandle) return evt_api_call(ref context); } + /** + * REMEMBER! Privacy feature for OTEL C API client: + * + * C API customer that does not explicitly pass down JSON + * config["config]["scope"] = COMMONFIELDS_SCOPE_ALL; + * + * should not be able to capture the host's context vars. + */ public static ulong evt_log(ulong inHandle, ref EventProperties properties) { ulong result = 0; @@ -495,7 +503,7 @@ public static ulong evt_upload(ulong inHandle) { EventContextType context = new EventContextType { - call = (Byte)EventCallType.EVT_OP_RESUME, + call = (Byte)EventCallType.EVT_OP_UPLOAD, handle = inHandle }; return evt_api_call(ref context); diff --git a/wrappers/netcore/EventSender.csproj b/wrappers/netcore/EventSender.csproj index 666be2695..73dc23c23 100644 --- a/wrappers/netcore/EventSender.csproj +++ b/wrappers/netcore/EventSender.csproj @@ -1,4 +1,4 @@ - + Exe netcoreapp3.1 @@ -9,6 +9,17 @@ PreserveNewest + + + PreserveNewest + + + diff --git a/wrappers/netcore/EventSender.xml b/wrappers/netcore/EventSender.xml index 706ff14b5..b10fcd35f 100644 --- a/wrappers/netcore/EventSender.xml +++ b/wrappers/netcore/EventSender.xml @@ -1,3 +1,4 @@ + From 9de1d489567c6c6aec753230e6e42a3cff948fd1 Mon Sep 17 00:00:00 2001 From: Max Golovanov Date: Sun, 12 Feb 2023 19:31:50 -0800 Subject: [PATCH 14/15] Use debug library in the sample --- wrappers/netcore/run.cmd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wrappers/netcore/run.cmd b/wrappers/netcore/run.cmd index d31eb7c7e..3700b2611 100644 --- a/wrappers/netcore/run.cmd +++ b/wrappers/netcore/run.cmd @@ -1,2 +1,2 @@ -set "PATH=%CD%\..\..\Solutions\out\Release\x64\win32-dll;%PATH%" -dotnet run -c Release +set "PATH=%CD%\..\..\Solutions\out\Debug\x64\win32-dll;%PATH%" +dotnet run -c Debug From b9b8fc0bae4df0cacf12adc4336d2eead4240752 Mon Sep 17 00:00:00 2001 From: Max Golovanov Date: Mon, 13 Feb 2023 00:08:15 -0800 Subject: [PATCH 15/15] Add helper method and use Newtonsoft for now --- wrappers/netcore/EventNativeAPI.cs | 30 +++++++++- wrappers/netcore/Program.cs | 91 ++++++++++++++++-------------- 2 files changed, 78 insertions(+), 43 deletions(-) diff --git a/wrappers/netcore/EventNativeAPI.cs b/wrappers/netcore/EventNativeAPI.cs index e861c8f9e..26ad7a82a 100644 --- a/wrappers/netcore/EventNativeAPI.cs +++ b/wrappers/netcore/EventNativeAPI.cs @@ -6,7 +6,7 @@ using System.Runtime.InteropServices; using System.Text; using System.IO; -using System.Json; + using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Collections; @@ -385,6 +385,32 @@ public EventProperty(bool boolValue) public static implicit operator EventProperty(int v) => new EventProperty(v); public static implicit operator EventProperty(double v) => new EventProperty(v); public static implicit operator EventProperty(Guid v) => new EventProperty(v); + + public static EventProperty FromObject(object v) + { + if (v is Guid guid) + { + return new EventProperty(guid); + } + + switch (Type.GetTypeCode(v.GetType())) + { + case TypeCode.String: + return new EventProperty((string)v); + case TypeCode.Int16: + case TypeCode.Int32: + case TypeCode.Int64: + return new EventProperty((int)v); + case TypeCode.Double: + return new EventProperty((double)v); + case TypeCode.Boolean: + return new EventProperty((bool)v); + default: + break; + } + + return new EventProperty(v.ToString()); + } }; public static class EventNativeAPI @@ -554,4 +580,4 @@ public static string evt_version() } } -} \ No newline at end of file +} diff --git a/wrappers/netcore/Program.cs b/wrappers/netcore/Program.cs index e1767003d..99b0133c8 100644 --- a/wrappers/netcore/Program.cs +++ b/wrappers/netcore/Program.cs @@ -2,21 +2,26 @@ using System.Runtime.InteropServices; using System.Text; using System.IO; -using System.Json; using System.Diagnostics; - using Microsoft.Telemetry.Core; +#if HAVE_SYSTEM_JSON +// TODO: Unity 2021 doesn't have support for System.Json yet +using System.Json; +#else +using Newtonsoft.Json.Linq; +#endif + namespace EventSender { class Program { - /// - /// Read 1DS SDK configuration from JSON configuration file. - /// - /// + /// + /// Read 1DS SDK configuration from JSON configuration file. + /// + /// /// static string ReadConfiguration(string filename) { @@ -28,23 +33,23 @@ static string ReadConfiguration(string filename) return result; } - /// - /// Run action in a loop and measure common performance characteristics. - /// - /// + /// + /// Run action in a loop and measure common performance characteristics. + /// + /// /// - static void StressTest(Action action, int maxIterations) - { + static void StressTest(Action action, int maxIterations) + { long total0 = GC.GetTotalMemory(true); long frag0 = GC.GetGCMemoryInfo().FragmentedBytes; Stopwatch sw = new Stopwatch(); - sw.Start(); - - for (int i = 0; i < maxIterations; i++) - { - action(i); - } - + sw.Start(); + + for (int i = 0; i < maxIterations; i++) + { + action(i); + } + sw.Stop(); long total1 = GC.GetTotalMemory(true); long frag1 = GC.GetGCMemoryInfo().FragmentedBytes; @@ -55,13 +60,13 @@ static void StressTest(Action action, int maxIterations) (ts.TotalMilliseconds != 0) ? (maxIterations / ts.TotalSeconds) : 1000); Console.WriteLine("Latency = {0} ms", (ts.TotalMilliseconds / maxIterations)); - Console.WriteLine("Mem used = {0} bytes", total1 - total0); + Console.WriteLine("Mem used = {0} bytes", total1 - total0); } - /// - /// Small demo how to use 1DS .NET Core API routed via 1DS C API. - /// All features of C API are supported. - /// + /// + /// Small demo how to use 1DS .NET Core API routed via 1DS C API. + /// All features of C API are supported. + /// /// static void Main(string[] args) { @@ -70,7 +75,11 @@ static void Main(string[] args) // Parse to verify it is valid and print it out.. // Parser should throw if config is invalid. +#if HAVE_SYSTEM_JSON JsonObject jsonDoc = (JsonObject)JsonObject.Parse(cfg); +#else + JObject jsonDoc = JObject.Parse(cfg); +#endif Console.WriteLine("SDK configuration:\n{0}", jsonDoc.ToString()); // Obtain SDK version from native library @@ -94,31 +103,31 @@ static void Main(string[] args) // Now let's run a small stress test... StressTest( - (param1) => - { + (param1) => + { var props = new EventProperties() { { "name", "SampleNetCore.PerfTest" }, - { "intKey", param1 }, - }; + { "intKey", param1 }, + }; EventNativeAPI.evt_log(handle, ref props); } , 10 // number of iterations ); - ulong result = 0; - - // Flush events to storage - result = EventNativeAPI.evt_flush(handle); - Console.WriteLine("result={0}", result); - - // Force upload of all pending events - result = EventNativeAPI.evt_upload(handle); - Console.WriteLine("result={0}", result); - - // FlushAndTeardown + ulong result = 0; + + // Flush events to storage + result = EventNativeAPI.evt_flush(handle); + Console.WriteLine("result={0}", result); + + // Force upload of all pending events + result = EventNativeAPI.evt_upload(handle); + Console.WriteLine("result={0}", result); + + // FlushAndTeardown Console.WriteLine(">>> evt_close..."); - result = EventNativeAPI.evt_close(handle); - Console.WriteLine("result={0}", result); + result = EventNativeAPI.evt_close(handle); + Console.WriteLine("result={0}", result); } } }