Added DS-5 Streamline tracing support 38/305838/3
authorAdeel Kazmi <adeel.kazmi@samsung.com>
Thu, 8 Feb 2024 14:23:44 +0000 (14:23 +0000)
committerAdeel Kazmi <adeel.kazmi@samsung.com>
Fri, 9 Feb 2024 11:51:55 +0000 (11:51 +0000)
Change-Id: I2086a5db94ba7baedf12a73e3c44d98a1c5b25c6

16 files changed:
README.md
build/tizen/CMakeLists.txt
build/tizen/profiles/common-profile.cmake
build/tizen/profiles/ivi-profile.cmake
build/tizen/profiles/mobile-profile.cmake
build/tizen/profiles/tv-profile.cmake
build/tizen/profiles/ubuntu-profile.cmake
build/tizen/profiles/wearable-profile.cmake
dali/internal/trace/file.list
dali/internal/trace/streamline/streamline_annotate.c [new file with mode: 0644]
dali/internal/trace/streamline/streamline_annotate.h [new file with mode: 0644]
dali/internal/trace/streamline/streamline_annotate_logging.h [new file with mode: 0644]
dali/internal/trace/streamline/trace-factory-streamline.cpp [new file with mode: 0644]
dali/internal/trace/streamline/trace-manager-impl-streamline.cpp [new file with mode: 0644]
dali/internal/trace/streamline/trace-manager-impl-streamline.h [new file with mode: 0644]
packaging/dali-adaptor.spec

index 42afb8d..f198769 100644 (file)
--- a/README.md
+++ b/README.md
          * [Building and executing test cases](#building-and-executing-test-cases)
       * [2. GBS Builds](#2-gbs-builds)
          * [DEBUG Builds](#debug-builds)
+         * [DS-5 Streamline Trace Builds](#ds-5-streamline-trace-builds)
       * [3. Building for MS Windows](#3-building-for-ms-windows)
-         * Build with the Visual Studio project.
-         * Build with CMake.
+         * [Build with the Visual Studio project](#build-with-the-visual-studio-project)
+         * [Build with CMake](#build-with-cmake)
       * [4. Building for MacOS](#4-building-for-macos)
 
 # Build Instructions
@@ -57,6 +58,13 @@ See the README.md in dali-adaptor/automated-tests.
          $ gbs build -A [TARGET_ARCH] --define "%enable_debug 1"
 
 
+### DS-5 Streamline Trace Builds
+
+By default, when using a GBS build, trace is enabled but Ttrace is used.
+If you want to use DS-5 Streamline instead then do the following:
+
+         $ gbs build -A [TARGET_ARCH] --define "%enable_streamline 1"
+
 ## 3. Building for MS Windows
 
 Third party dependencies are built using vcpkg. Instructions on how to install vcpkg can be found in the
index 75d2f6c..3ba49c3 100644 (file)
@@ -15,10 +15,11 @@ IF(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
   ENDIF()
 ENDIF()
 
-OPTION(ENABLE_PKG_CONFIGURE  "Use pkgconfig" ON)
-OPTION(ENABLE_LINK_TEST      "Enable the link test" ON)
-OPTION(ENABLE_ATSPI          "Enable AT-SPI accessibility" ON)
-OPTION(ENABLE_TRACE          "Enable Trace" OFF)
+OPTION(ENABLE_PKG_CONFIGURE     "Use pkgconfig" ON)
+OPTION(ENABLE_LINK_TEST         "Enable the link test" ON)
+OPTION(ENABLE_ATSPI             "Enable AT-SPI accessibility" ON)
+OPTION(ENABLE_TRACE             "Enable Trace" OFF)
+OPTION(ENABLE_TRACE_STREAMLINE  "Enable Trace (Streamline)" OFF)
 
 # Include additional macros
 INCLUDE( common.cmake )
@@ -120,8 +121,14 @@ IF( ENABLE_VCONF )
   ADD_DEFINITIONS("-DVCONF_ENABLED")
 ENDIF()
 
-IF( ENABLE_TRACE )
+IF( ENABLE_TRACE OR ENABLE_TRACE_STREAMLINE )
   ADD_DEFINITIONS("-DTRACE_ENABLED")
+  SET(ENABLE_TRACE_STRING ON)
+  IF( ENABLE_TRACE_STREAMLINE )
+    SET(ENABLE_TRACE_STRING "ON (Streamline)")
+  ENDIF()
+ELSE()
+  SET(ENABLE_TRACE_STRING OFF)
 ENDIF()
 
 # Set up compiler flags and warnings
@@ -439,7 +446,7 @@ MESSAGE( STATUS "Using Tizen APP FW libraries:     ${ENABLE_APPFW}")
 MESSAGE( STATUS "Use pkg configure:                ${ENABLE_PKG_CONFIGURE}" )
 MESSAGE( STATUS "Enable link test:                 ${ENABLE_LINK_TEST}" )
 MESSAGE( STATUS "Enable AT-SPI:                    ${ENABLE_ATSPI}" )
-MESSAGE( STATUS "Enable Trace:                     ${ENABLE_TRACE}" )
+MESSAGE( STATUS "Enable Trace:                     ${ENABLE_TRACE_STRING}" )
 MESSAGE( STATUS "Enable VConf:                     ${ENABLE_VCONF}" )
 MESSAGE( STATUS "Tizen Platform Config supported   ${TIZEN_PLATFORM_CONFIG_SUPPORTED_LOGMSG}")
 MESSAGE( STATUS "Compile flags:                    ${CMAKE_CXX_FLAGS}")
index 0fdd61a..ac8b406 100644 (file)
@@ -79,7 +79,11 @@ IF( ENABLE_NETWORK_LOGGING )
          )
 ENDIF()
 
-IF( ENABLE_TRACE )
+IF( ENABLE_TRACE_STREAMLINE )
+    SET( SOURCES ${SOURCES}
+         ${adaptor_trace_tizen_streamline_src_files}
+         )
+ELSEIF( ENABLE_TRACE )
     SET( SOURCES ${SOURCES}
          ${adaptor_trace_tizen_src_files}
          )
index af32bce..f37f9e2 100644 (file)
@@ -81,7 +81,11 @@ IF( ENABLE_NETWORK_LOGGING )
          )
 ENDIF()
 
-IF( ENABLE_TRACE )
+IF( ENABLE_TRACE_STREAMLINE )
+    SET( SOURCES ${SOURCES}
+         ${adaptor_trace_tizen_streamline_src_files}
+         )
+ELSEIF( ENABLE_TRACE )
     SET( SOURCES ${SOURCES}
          ${adaptor_trace_tizen_src_files}
          )
index 0f00790..9200c02 100644 (file)
@@ -79,10 +79,14 @@ IF( ENABLE_NETWORK_LOGGING )
     )
 ENDIF()
 
-IF( ENABLE_TRACE )
+IF( ENABLE_TRACE_STREAMLINE )
     SET( SOURCES ${SOURCES}
-        ${adaptor_trace_tizen_src_files}
-      )
+         ${adaptor_trace_tizen_streamline_src_files}
+         )
+ELSEIF( ENABLE_TRACE )
+    SET( SOURCES ${SOURCES}
+         ${adaptor_trace_tizen_src_files}
+         )
 ENDIF()
 
 IF( COMPONENT_APPLICATION_SUPPORT )
index d3cfac0..70fb01d 100644 (file)
@@ -79,7 +79,11 @@ IF( ENABLE_NETWORK_LOGGING )
          )
 ENDIF()
 
-IF( ENABLE_TRACE )
+IF( ENABLE_TRACE_STREAMLINE )
+    SET( SOURCES ${SOURCES}
+         ${adaptor_trace_tizen_streamline_src_files}
+         )
+ELSEIF( ENABLE_TRACE )
     SET( SOURCES ${SOURCES}
          ${adaptor_trace_tizen_src_files}
          )
index 9c30c55..1f0723c 100644 (file)
@@ -74,10 +74,14 @@ IF( ENABLE_NETWORK_LOGGING )
     )
 ENDIF()
 
-IF( ENABLE_TRACE )
+IF( ENABLE_TRACE_STREAMLINE )
     SET( SOURCES ${SOURCES}
-        ${adaptor_trace_generic_src_files}
-      )
+         ${adaptor_trace_tizen_streamline_src_files}
+    )
+ELSEIF( ENABLE_TRACE )
+    SET( SOURCES ${SOURCES}
+         ${adaptor_trace_generic_src_files}
+    )
 ENDIF()
 
 # Set the header directories
index b7cae78..70db0ba 100644 (file)
@@ -77,14 +77,18 @@ ENDIF()
 
 IF( ENABLE_NETWORK_LOGGING )
     SET( SOURCES ${SOURCES}
-          ${adaptor_performance_logging_src_files}
-    )
+         ${adaptor_performance_logging_src_files}
+         )
 ENDIF()
 
-IF( ENABLE_TRACE )
+IF( ENABLE_TRACE_STREAMLINE )
     SET( SOURCES ${SOURCES}
-        ${adaptor_trace_tizen_src_files}
-      )
+         ${adaptor_trace_tizen_streamline_src_files}
+         )
+ELSEIF( ENABLE_TRACE )
+    SET( SOURCES ${SOURCES}
+         ${adaptor_trace_tizen_src_files}
+         )
 ENDIF()
 
 IF( COMPONENT_APPLICATION_SUPPORT )
index cccd8d8..0720271 100644 (file)
@@ -1,19 +1,25 @@
 
 # module: trace, backend: common
-SET( adaptor_trace_common_src_files 
-    ${adaptor_trace_dir}/common/trace-manager-impl.cpp 
+SET( adaptor_trace_common_src_files
+    ${adaptor_trace_dir}/common/trace-manager-impl.cpp
     ${adaptor_trace_dir}/common/trace-factory.cpp
 )
 
 # module: trace, backend: generic
-SET( adaptor_trace_generic_src_files 
-    ${adaptor_trace_dir}/generic/trace-manager-impl-generic.cpp 
+SET( adaptor_trace_generic_src_files
+    ${adaptor_trace_dir}/generic/trace-manager-impl-generic.cpp
     ${adaptor_trace_dir}/generic/trace-factory-generic.cpp
 )
 
 # module: trace, backend: tizen
-SET( adaptor_trace_tizen_src_files 
-    ${adaptor_trace_dir}/tizen/trace-manager-impl-tizen.cpp 
+SET( adaptor_trace_tizen_src_files
+    ${adaptor_trace_dir}/tizen/trace-manager-impl-tizen.cpp
     ${adaptor_trace_dir}/tizen/trace-factory-tizen.cpp
 )
 
+# module: trace, backend: tizen-streamline
+SET( adaptor_trace_tizen_streamline_src_files
+    ${adaptor_trace_dir}/streamline/streamline_annotate.c
+    ${adaptor_trace_dir}/streamline/trace-manager-impl-streamline.cpp
+    ${adaptor_trace_dir}/streamline/trace-factory-streamline.cpp
+)
diff --git a/dali/internal/trace/streamline/streamline_annotate.c b/dali/internal/trace/streamline/streamline_annotate.c
new file mode 100644 (file)
index 0000000..cbaf7a9
--- /dev/null
@@ -0,0 +1,1540 @@
+/**
+ * Copyright (C) 2014-2023 by Arm Limited. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include "streamline_annotate.h"
+
+#include "streamline_annotate_logging.h"
+
+#include <arpa/inet.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/socket.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <time.h>
+#include <unistd.h>
+
+#define THREAD_BUFFER_SIZE (1 << 16)
+#define THREAD_BUFFER_MASK (THREAD_BUFFER_SIZE - 1)
+
+#ifdef TCP_ANNOTATIONS
+#define STREAMLINE_ANNOTATE_PARENT 8082
+#define STREAMLINE_ANNOTATE 8083
+#else
+#define STREAMLINE_ANNOTATE_PARENT "\0streamline-annotate-parent"
+#define STREAMLINE_ANNOTATE "\0streamline-annotate"
+#endif
+
+static const char gator_annotate_handshake[] = "ANNOTATE 5\n";
+static const int  gator_minimum_version      = 24;
+
+static const uint8_t HEADER_UTF8             = 0x01;
+static const uint8_t HEADER_UTF8_COLOR       = 0x02;
+static const uint8_t HEADER_CHANNEL_NAME     = 0x03;
+static const uint8_t HEADER_GROUP_NAME       = 0x04;
+static const uint8_t HEADER_VISUAL           = 0x05;
+static const uint8_t HEADER_MARKER           = 0x06;
+static const uint8_t HEADER_MARKER_COLOR     = 0x07;
+static const uint8_t HEADER_COUNTER          = 0x08;
+static const uint8_t HEADER_COUNTER_VALUE    = 0x09;
+static const uint8_t HEADER_ACTIVITY_SWITCH  = 0x0a;
+static const uint8_t HEADER_CAM_TRACK        = 0x0b;
+static const uint8_t HEADER_CAM_JOB          = 0x0c;
+static const uint8_t HEADER_CAM_VIEW_NAME    = 0x0d;
+static const uint8_t HEADER_CAM_JOB_START    = 0x0e;
+static const uint8_t HEADER_CAM_JOB_SET_DEPS = 0x0f;
+static const uint8_t HEADER_CAM_JOB_STOP     = 0x10;
+
+static const uint32_t SIZE_COLOR        = 4;
+static const uint32_t MAXSIZE_PACK_INT  = 5;
+static const uint32_t MAXSIZE_PACK_LONG = 10;
+
+static const uint64_t NS_PER_S = 1000000000;
+
+struct gator_thread
+{
+  struct gator_thread* next;
+  const char*          oob_data;
+  /* oob_data must be written before oob_length */
+  size_t oob_length;
+  /* Posted when data is sent */
+  sem_t    sem;
+  int      fd;
+  int      tid;
+  uint32_t write_pos;
+  uint32_t read_pos;
+  bool     exited;
+  char     buf[THREAD_BUFFER_SIZE];
+};
+
+struct gator_counter
+{
+  struct gator_counter*                  next;
+  const char*                            title;
+  const char*                            name;
+  const char*                            units;
+  const char*                            description;
+  const char**                           activities;
+  uint32_t*                              activity_colors;
+  size_t                                 activity_count;
+  int                                    per_cpu;
+  int                                    average_selection;
+  int                                    average_cores;
+  int                                    percentage;
+  enum gator_annotate_counter_class      counter_class;
+  enum gator_annotate_display            display;
+  enum gator_annotate_series_composition series_composition;
+  enum gator_annotate_rendering_type     rendering_type;
+  uint32_t                               id;
+  uint32_t                               modifier;
+  uint32_t                               cores;
+  uint32_t                               color;
+};
+
+struct gator_cam_track
+{
+  struct gator_cam_track* next;
+  const char*             name;
+  uint32_t                view_uid;
+  uint32_t                track_uid;
+  uint32_t                parent_track;
+};
+
+struct gator_cam_name
+{
+  struct gator_cam_name* next;
+  const char*            name;
+  uint32_t               view_uid;
+};
+
+struct gator_state
+{
+  struct gator_thread*    threads;
+  struct gator_counter*   counters;
+  struct gator_cam_track* cam_tracks;
+  struct gator_cam_name*  cam_names;
+  /* Post to request asynchronous send of data */
+  sem_t sender_sem;
+  /* Post to request synchronous send of data */
+  sem_t sync_sem;
+  /* Posted when synchronous send of data has completed */
+  sem_t         sync_waiter_sem;
+  pthread_key_t key;
+  int           parent_fd;
+  bool          initialized;
+  bool          capturing;
+  bool          forked;
+  bool          resend_state;
+};
+
+static struct gator_state gator_state;
+/* Intentionally exported */
+uint8_t gator_dont_mangle_keys;
+
+static int gator_socket_cloexec(int domain, int type, int protocol)
+{
+#ifdef SOCK_CLOEXEC
+  return socket(domain, type | SOCK_CLOEXEC, protocol);
+#else
+  const int sock = socket(domain, type, protocol);
+  if(sock < 0)
+    return -1;
+
+  const int fdf = fcntl(sock, F_GETFD);
+  if((fdf == -1) || (fcntl(sock, F_SETFD, fdf | FD_CLOEXEC) != 0))
+  {
+    close(sock);
+    return -1;
+  }
+  return sock;
+#endif
+}
+
+static void gator_destructor(void* value)
+{
+  struct gator_thread* const thread = (struct gator_thread*)value;
+  if(thread != NULL)
+  {
+    thread->exited = true;
+  }
+}
+
+void gator_annotate_fork_child(void)
+{
+  /* Single threaded at this point */
+  struct gator_thread* thread;
+  pthread_setspecific(gator_state.key, NULL);
+  for(thread = gator_state.threads; thread != NULL; thread = thread->next)
+  {
+    thread->exited   = true;
+    thread->read_pos = thread->write_pos;
+  }
+
+  gator_state.forked = true;
+}
+
+void gator_annotate_flush(void)
+{
+  if(gator_state.capturing)
+  {
+    /* Request synchronous send of data */
+    sem_post(&gator_state.sync_sem);
+    /* Wake up sender */
+    sem_post(&gator_state.sender_sem);
+    /* Wait for completion */
+    sem_wait(&gator_state.sync_waiter_sem);
+  }
+}
+
+static void gator_set_ts(struct timespec* const ts, const uint64_t time)
+{
+  ts->tv_sec  = time / NS_PER_S;
+  ts->tv_nsec = time % NS_PER_S;
+}
+
+static uint64_t gator_time(const clockid_t clk_id)
+{
+  struct timespec ts;
+  if(clock_gettime(clk_id, &ts) != 0)
+  {
+    return ~0;
+  }
+
+  return NS_PER_S * ts.tv_sec + ts.tv_nsec;
+}
+
+uint64_t gator_get_time(void)
+{
+#ifndef CLOCK_MONOTONIC_RAW
+  /* Android doesn't have this defined but it was added in Linux 2.6.28 */
+#define CLOCK_MONOTONIC_RAW 4
+#endif
+  return gator_time(CLOCK_MONOTONIC_RAW);
+}
+
+static int get_correct_socket_fd(bool for_parent)
+{
+#ifdef TCP_ANNOTATIONS
+  const int fd = gator_socket_cloexec(AF_INET, SOCK_STREAM, 0);
+  if(fd < 0)
+    return -1;
+
+  struct sockaddr_in addr;
+  addr.sin_family      = AF_INET;
+  addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+  if(for_parent)
+  {
+    addr.sin_port = htons(STREAMLINE_ANNOTATE_PARENT);
+  }
+  else
+  {
+    addr.sin_port = htons(STREAMLINE_ANNOTATE);
+  }
+  if(connect(fd, (const struct sockaddr*)&addr, sizeof(addr)) != 0)
+  {
+    close(fd);
+    return -1;
+  }
+#else
+  const int fd = gator_socket_cloexec(PF_UNIX, SOCK_STREAM, 0);
+  if(fd < 0)
+  {
+    return -1;
+  }
+
+  struct sockaddr_un addr = {0};
+  addr.sun_family         = AF_UNIX;
+
+  char*  socket_name = STREAMLINE_ANNOTATE;
+  size_t name_len    = sizeof(STREAMLINE_ANNOTATE);
+  if(for_parent)
+  {
+    socket_name = STREAMLINE_ANNOTATE_PARENT;
+    name_len    = sizeof(STREAMLINE_ANNOTATE_PARENT);
+  }
+
+  memcpy(addr.sun_path, socket_name, name_len);
+  if(connect(fd, (const struct sockaddr*)&addr, offsetof(struct sockaddr_un, sun_path) + name_len - 1) != 0)
+  {
+    close(fd);
+    return -1;
+  }
+#endif
+
+  return fd;
+}
+
+static bool gator_parent_connect(void)
+{
+  const int fd = get_correct_socket_fd(/* for_parent= */ true);
+  if(fd < 0)
+  {
+    return false;
+  }
+
+  gator_state.parent_fd = fd;
+  return true;
+}
+
+static uint32_t gator_buf_pos(const uint32_t pos)
+{
+  return pos & THREAD_BUFFER_MASK;
+}
+
+static uint32_t gator_buf_write_byte(char* const buf, uint32_t* const write_pos_ptr, char b)
+{
+  const uint32_t write_pos = *write_pos_ptr;
+  buf[write_pos]           = b;
+  *write_pos_ptr           = gator_buf_pos(write_pos + 1);
+  return 1;
+}
+
+static uint32_t gator_buf_write_uint32(char* const buf, uint32_t* const write_pos_ptr, uint32_t i)
+{
+  const uint32_t write_pos          = *write_pos_ptr;
+  buf[gator_buf_pos(write_pos + 0)] = i & 0xff;
+  buf[gator_buf_pos(write_pos + 1)] = (i >> 8) & 0xff;
+  buf[gator_buf_pos(write_pos + 2)] = (i >> 16) & 0xff;
+  buf[gator_buf_pos(write_pos + 3)] = (i >> 24) & 0xff;
+  *write_pos_ptr                    = gator_buf_pos(write_pos + sizeof(uint32_t));
+  return sizeof(uint32_t);
+}
+
+static uint32_t gator_buf_write_color(char* const buf, uint32_t* const write_pos_ptr, const uint32_t color)
+{
+  const uint32_t write_pos          = *write_pos_ptr;
+  buf[gator_buf_pos(write_pos + 0)] = (color >> 8) & 0xff;
+  buf[gator_buf_pos(write_pos + 1)] = (color >> 16) & 0xff;
+  buf[gator_buf_pos(write_pos + 2)] = (color >> 24) & 0xff;
+  buf[gator_buf_pos(write_pos + 3)] = (color >> 0) & 0xff;
+  *write_pos_ptr                    = gator_buf_pos(write_pos + 4);
+  return 4;
+}
+
+static uint32_t gator_buf_write_int(char* const buf, uint32_t* const write_pos_ptr, int32_t x)
+{
+  const uint32_t write_pos    = *write_pos_ptr;
+  int            packed_bytes = 0;
+  int            more         = true;
+
+  while(more)
+  {
+    /* low order 7 bits of x */
+    char b = x & 0x7f;
+
+    x >>= 7;
+
+    if((x == 0 && (b & 0x40) == 0) || (x == -1 && (b & 0x40) != 0))
+    {
+      more = false;
+    }
+    else
+    {
+      b |= 0x80;
+    }
+
+    buf[gator_buf_pos(write_pos + packed_bytes)] = b;
+    packed_bytes++;
+  }
+
+  *write_pos_ptr = gator_buf_pos(write_pos + packed_bytes);
+  return packed_bytes;
+}
+
+static uint32_t gator_buf_write_long(char* const buf, uint32_t* const write_pos_ptr, int64_t x)
+{
+  const uint32_t write_pos    = *write_pos_ptr;
+  int            packed_bytes = 0;
+  int            more         = true;
+
+  while(more)
+  {
+    /* low order 7 bits of x */
+    char b = x & 0x7f;
+
+    x >>= 7;
+
+    if((x == 0 && (b & 0x40) == 0) || (x == -1 && (b & 0x40) != 0))
+    {
+      more = false;
+    }
+    else
+    {
+      b |= 0x80;
+    }
+
+    buf[gator_buf_pos(write_pos + packed_bytes)] = b;
+    packed_bytes++;
+  }
+
+  *write_pos_ptr = gator_buf_pos(write_pos + packed_bytes);
+  return packed_bytes;
+}
+
+static uint32_t gator_buf_write_time(char* const buf, uint32_t* const write_pos_ptr)
+{
+  return gator_buf_write_long(buf, write_pos_ptr, gator_get_time());
+}
+
+static uint32_t gator_buf_write_bytes(char* const       buf,
+                                      uint32_t* const   write_pos_ptr,
+                                      const char* const data,
+                                      const uint32_t    count)
+{
+  if(count > 0)
+  {
+    const uint32_t write_pos = *write_pos_ptr;
+    if(write_pos + count <= THREAD_BUFFER_SIZE)
+    {
+      memcpy(buf + write_pos, data, count);
+    }
+    else
+    {
+      const uint32_t first  = THREAD_BUFFER_SIZE - write_pos;
+      const uint32_t second = count - first;
+      memcpy(buf + write_pos, data, first);
+      memcpy(buf, data + first, second);
+    }
+    *write_pos_ptr = gator_buf_pos(write_pos + count);
+  }
+  return count;
+}
+
+static int gator_connect(const int tid)
+{
+  const int fd = get_correct_socket_fd(/* for_parent= */ false);
+  if(fd < 0)
+  {
+    return -1;
+  }
+
+  /* Send tid as gatord cannot autodiscover it and the per process unique id */
+  uint32_t write_pos = 0;
+  char     buf[sizeof(gator_annotate_handshake) + 2 * sizeof(uint32_t) + 1];
+  gator_buf_write_bytes(buf, &write_pos, gator_annotate_handshake, sizeof(gator_annotate_handshake) - 1);
+  gator_buf_write_uint32(buf, &write_pos, tid);
+  gator_buf_write_uint32(buf, &write_pos, getpid());
+  gator_buf_write_byte(buf, &write_pos, gator_dont_mangle_keys);
+  const ssize_t bytes = send(fd, buf, write_pos, MSG_NOSIGNAL);
+  if(bytes != (ssize_t)write_pos)
+  {
+    close(fd);
+    return -1;
+  }
+
+  return fd;
+}
+
+static void gator_start_capturing(void)
+{
+  if(__sync_bool_compare_and_swap(&gator_state.capturing, false, true))
+  {
+    gator_state.resend_state = true;
+  }
+}
+
+static void gator_stop_capturing(void)
+{
+  if(__sync_bool_compare_and_swap(&gator_state.capturing, true, false))
+  {
+    struct gator_thread* thread;
+    for(thread = gator_state.threads; thread != NULL; thread = thread->next)
+    {
+      thread->read_pos   = thread->write_pos;
+      thread->oob_length = 0;
+      sem_post(&thread->sem);
+      if(thread->fd > 0)
+      {
+        close(thread->fd);
+        thread->fd = -1;
+      }
+    }
+  }
+}
+
+static bool gator_send(struct gator_thread* const thread, const uint32_t write_pos)
+{
+  size_t  write;
+  ssize_t bytes;
+
+  if(write_pos > thread->read_pos)
+  {
+    write = write_pos - thread->read_pos;
+    bytes = send(thread->fd, thread->buf + thread->read_pos, write, MSG_NOSIGNAL);
+    if(bytes == 0)
+    {
+      //not an error reattempt.
+      return true;
+    }
+    if(bytes < 0)
+    {
+      return false;
+    }
+    thread->read_pos = gator_buf_pos(thread->read_pos + bytes);
+  }
+  else
+  {
+    write = THREAD_BUFFER_SIZE - thread->read_pos;
+    bytes = send(thread->fd, thread->buf + thread->read_pos, write, MSG_NOSIGNAL);
+    if(bytes == 0)
+    {
+      //not an error reattempt.
+      return true;
+    }
+    if(bytes < 0)
+    {
+      return false;
+    }
+    thread->read_pos = gator_buf_pos(thread->read_pos + bytes);
+
+    if(write == (size_t)bytes)
+    {
+      /* Don't write more on a short write to be fair to other threads */
+      write = write_pos;
+      bytes = send(thread->fd, thread->buf, write, MSG_NOSIGNAL);
+      if(bytes == 0)
+      {
+        //not an error reattempt.
+        return true;
+      }
+      if(bytes < 0)
+      {
+        return false;
+      }
+      thread->read_pos = gator_buf_pos(thread->read_pos + bytes);
+    }
+  }
+
+  if(write == (size_t)bytes && thread->oob_length > 0)
+  {
+    __sync_synchronize();
+    /* Don't write more on a short write to be fair to other threads */
+    bytes = send(thread->fd, thread->oob_data, thread->oob_length, MSG_NOSIGNAL);
+    if(bytes == 0)
+    {
+      //not an error reattempt.
+      return true;
+    }
+    if(bytes < 0)
+    {
+      return false;
+    }
+    thread->oob_data += bytes;
+    thread->oob_length -= bytes;
+  }
+  return true;
+}
+
+static void* gator_func(void* arg)
+{
+  bool     print = true;
+  uint64_t last  = 0;
+  prctl(PR_SET_NAME, (unsigned long)&"gator-annotate", 0, 0, 0);
+
+  if(arg != NULL)
+  {
+    /* Forked */
+    if(gator_state.parent_fd >= 0)
+    {
+      close(gator_state.parent_fd);
+      gator_state.parent_fd = -1;
+    }
+  }
+
+  for(;;)
+  {
+    if(gator_state.parent_fd < 0)
+    {
+      if(gator_parent_connect())
+      {
+        /* Optimistically begin capturing data */
+        gator_start_capturing();
+      }
+      else
+      {
+        gator_stop_capturing();
+        if(print)
+        {
+          LOG(LOG_ERROR,
+              "Warning : Not connected to gatord, "                                                          //
+              "the application will run normally but Streamline will not collect annotations. "              //
+              "To collect annotations, please verify you are running gatord 5.%i or later and that SELinux " //
+              "is disabled.\n",                                                                              //
+              gator_minimum_version);
+          print = false;
+        }
+        sleep(1);
+        continue;
+      }
+    }
+
+    if(!gator_state.capturing)
+    {
+      char          temp;
+      const ssize_t bytes = read(gator_state.parent_fd, &temp, sizeof(temp));
+      if(bytes <= 0)
+      {
+        close(gator_state.parent_fd);
+        gator_state.parent_fd = -1;
+        continue;
+      }
+      gator_start_capturing();
+      gator_state.resend_state = true;
+    }
+
+    if(gator_state.capturing)
+    {
+      /* Iterate every 100ms */
+      const uint64_t freq   = NS_PER_S / 10;
+      const uint64_t now    = gator_time(CLOCK_REALTIME);
+      const uint64_t wakeup = last + freq;
+      if(wakeup < now)
+      {
+        /* Already timed out, save the current time and iterate again */
+        last = now;
+      }
+      else
+      {
+        struct timespec ts;
+        gator_set_ts(&ts, wakeup);
+        sem_timedwait(&gator_state.sender_sem, &ts);
+        last = wakeup;
+        while(sem_trywait(&gator_state.sender_sem) == 0)
+        {
+          /* Ignore multiple posts */
+        }
+      }
+    }
+
+    int sync_count = 0;
+    while(sem_trywait(&gator_state.sync_sem) == 0)
+    {
+      ++sync_count;
+    }
+
+    struct gator_thread** prev   = &gator_state.threads;
+    struct gator_thread*  thread = *prev;
+    while(thread != NULL)
+    {
+      if(gator_state.capturing)
+      {
+        if(thread->fd < 0 && (thread->fd = gator_connect(thread->tid)) < 0)
+        {
+          gator_stop_capturing();
+        }
+        else
+        {
+          const uint32_t write_pos = thread->write_pos;
+          if(write_pos != thread->read_pos || thread->oob_length > 0)
+          {
+            if(!gator_send(thread, write_pos))
+            {
+              LOG(LOG_ERROR,
+                  "Failed to send bytes, "                                                    //
+                  "gator_thread = (exited:%s, fd:%d, oob_length:%zu, read_pos:%d,  tid:%d), " //
+                  "write_pos = %u",                                                           //
+                  thread->exited ? "true" : "false",
+                  thread->fd,
+                  thread->oob_length,
+                  thread->read_pos,
+                  thread->tid,
+                  write_pos);
+              gator_stop_capturing();
+            }
+            else
+            {
+              sem_post(&thread->sem);
+            }
+          }
+        }
+      }
+
+      struct gator_thread* const next = thread->next;
+      if(!thread->exited || !__sync_bool_compare_and_swap(prev, thread, next))
+      {
+        /* If the cas fails, the linked list has changed, get it next time */
+        prev   = &thread->next;
+        thread = *prev;
+      }
+      else
+      {
+        if(thread->fd > 0)
+        {
+          close(thread->fd);
+        }
+        sem_destroy(&thread->sem);
+        free(thread);
+        thread = next;
+      }
+    }
+
+    for(; sync_count > 0; --sync_count)
+    {
+      sem_post(&gator_state.sync_waiter_sem);
+    }
+  }
+  return NULL;
+}
+
+void gator_annotate_setup(void)
+{
+  /* Support calling gator_annotate_setup more than once, but not at the same time on different cores */
+  if(__sync_bool_compare_and_swap(&gator_state.initialized, false, true))
+  {
+    gator_state.parent_fd = -1;
+    /* Optimistically begin capturing data */
+    gator_state.capturing = true;
+
+    int err = sem_init(&gator_state.sender_sem, 0, 0);
+    if(err != 0)
+    {
+      LOG(LOG_ERROR, "sem_init failed, with error %s", strerror(err));
+      return;
+    }
+
+    err = sem_init(&gator_state.sync_sem, 0, 0);
+    if(err != 0)
+    {
+      LOG(LOG_ERROR, "sem_init failed, with error %s", strerror(err));
+      return;
+    }
+
+    err = sem_init(&gator_state.sync_waiter_sem, 0, 0);
+    if(err != 0)
+    {
+      LOG(LOG_ERROR, "sem_init failed, with error %s", strerror(err));
+      return;
+    }
+
+    err = pthread_key_create(&gator_state.key, gator_destructor);
+    if(err != 0)
+    {
+      LOG(LOG_ERROR, "pthread_key_create failed, with error %s", strerror(err));
+      return;
+    }
+
+#if !defined(__ANDROID_API__) || __ANDROID_API__ >= 21
+    err = pthread_atfork(NULL, NULL, gator_annotate_fork_child);
+    if(err != 0)
+    {
+      LOG(LOG_ERROR, "pthread_atfork failed, with error %s", strerror(err));
+      return;
+    }
+#endif
+
+    if(atexit(gator_annotate_flush) != 0)
+    {
+      LOG(LOG_ERROR, "atexit failed");
+      return;
+    }
+
+    pthread_t thread;
+    err = pthread_create(&thread, NULL, gator_func, NULL);
+    if(err != 0)
+    {
+      LOG(LOG_ERROR, "pthread_create failed, with error %s", strerror(err));
+      return;
+    }
+  }
+}
+
+static void gator_annotate_write_counter(struct gator_thread* thread, const struct gator_counter* counter);
+static void gator_annotate_write_cam_track(struct gator_thread* thread, const struct gator_cam_track* cam_track);
+static void gator_annotate_write_cam_name(struct gator_thread* thread, const struct gator_cam_name* cam_name);
+
+static struct gator_thread* gator_get_thread(void)
+{
+  if(__sync_bool_compare_and_swap(&gator_state.forked, true, false))
+  {
+    pthread_t thread;
+    int       err = pthread_create(&thread, NULL, gator_func, (void*)1);
+    if(err != 0)
+    {
+      LOG(LOG_ERROR, "pthread_create failed, with error %s", strerror(err));
+      return NULL;
+    }
+  }
+
+  if(!gator_state.capturing)
+  {
+    return NULL;
+  }
+
+  struct gator_thread* thread = (struct gator_thread*)pthread_getspecific(gator_state.key);
+  int                  err;
+  if(thread != NULL)
+  {
+    goto success;
+  }
+
+  thread = (struct gator_thread*)malloc(sizeof(*thread));
+  if(thread == NULL)
+  {
+    LOG(LOG_ERROR, "malloc failed, with error %s", strerror(errno));
+    return NULL;
+  }
+
+  thread->oob_data   = NULL;
+  thread->oob_length = 0;
+  thread->fd         = -1;
+  thread->tid        = syscall(__NR_gettid);
+  thread->write_pos  = 0;
+  thread->read_pos   = 0;
+  thread->exited     = false;
+
+  err = sem_init(&thread->sem, 0, 0);
+  if(err != 0)
+  {
+    LOG(LOG_ERROR, "sem_init failed, with error %s", strerror(err));
+    goto fail_free_thread;
+  }
+
+  err = pthread_setspecific(gator_state.key, thread);
+  if(err != 0)
+  {
+    LOG(LOG_ERROR, "pthread_setspecific failed, with error %s", strerror(err));
+    goto fail_sem_destroy;
+  }
+
+  do
+  {
+    thread->next = gator_state.threads;
+  } while(!__sync_bool_compare_and_swap(&gator_state.threads, thread->next, thread));
+
+success:
+  if(__sync_bool_compare_and_swap(&gator_state.resend_state, true, false))
+  {
+    struct gator_counter* counter;
+    for(counter = gator_state.counters; counter != NULL; counter = counter->next)
+    {
+      gator_annotate_write_counter(thread, counter);
+    }
+    struct gator_cam_track* cam_track;
+    for(cam_track = gator_state.cam_tracks; cam_track != NULL; cam_track = cam_track->next)
+    {
+      gator_annotate_write_cam_track(thread, cam_track);
+    }
+    struct gator_cam_name* cam_name;
+    for(cam_name = gator_state.cam_names; cam_name != NULL; cam_name = cam_name->next)
+    {
+      gator_annotate_write_cam_name(thread, cam_name);
+    }
+  }
+
+  return thread;
+
+fail_sem_destroy:
+  sem_destroy(&thread->sem);
+fail_free_thread:
+  free(thread);
+  return NULL;
+}
+
+static uint32_t gator_buf_free(const struct gator_thread* const thread)
+{
+  return (thread->read_pos - thread->write_pos - 1) & THREAD_BUFFER_MASK;
+}
+
+static uint32_t gator_buf_used(const struct gator_thread* const thread)
+{
+  return (thread->write_pos - thread->read_pos) & THREAD_BUFFER_MASK;
+}
+
+#define gator_buf_wait_bytes(thread, bytes)         \
+  const uint32_t __bytes = (bytes);                 \
+  if(__bytes > THREAD_BUFFER_SIZE / 2)              \
+  {                                                 \
+    /* Large annotations won't fit in the buffer */ \
+    LOG(LOG_ERROR, "message is too large");         \
+    return;                                         \
+  }                                                 \
+  __gator_buf_wait_bytes((thread), __bytes);
+
+static void __gator_buf_wait_bytes(struct gator_thread* const thread, const uint32_t bytes)
+{
+  while(gator_buf_free(thread) < bytes)
+  {
+    sem_post(&gator_state.sender_sem);
+    sem_wait(&thread->sem);
+  }
+}
+
+static void gator_msg_begin(const char                 marker,
+                            struct gator_thread* const thread,
+                            uint32_t* const            write_pos_ptr,
+                            uint32_t* const            size_pos_ptr,
+                            uint32_t* const            length_ptr)
+{
+  *write_pos_ptr = thread->write_pos;
+  gator_buf_write_byte(thread->buf, write_pos_ptr, marker);
+  *size_pos_ptr  = *write_pos_ptr;
+  *write_pos_ptr = gator_buf_pos(*write_pos_ptr + sizeof(uint32_t));
+  *length_ptr    = 0;
+}
+
+static void gator_msg_end(struct gator_thread* const thread,
+                          const uint32_t             write_pos,
+                          uint32_t                   size_pos,
+                          const uint32_t             length)
+{
+  gator_buf_write_uint32(thread->buf, &size_pos, length);
+  thread->write_pos = write_pos;
+
+  /* Wakeup the sender thread if 3/4 full */
+  if(gator_buf_used(thread) >= 3 * THREAD_BUFFER_SIZE / 4)
+  {
+    sem_post(&gator_state.sender_sem);
+  }
+}
+
+void gator_annotate_str(const uint32_t channel, const char* const str)
+{
+  struct gator_thread* const thread = gator_get_thread();
+  if(thread == NULL)
+  {
+    return;
+  }
+
+  const int str_size = (str == NULL) ? 0 : strlen(str);
+  gator_buf_wait_bytes(thread, 1 + sizeof(uint32_t) + MAXSIZE_PACK_LONG + MAXSIZE_PACK_INT + str_size);
+
+  uint32_t write_pos;
+  uint32_t size_pos;
+  uint32_t length;
+  gator_msg_begin(HEADER_UTF8, thread, &write_pos, &size_pos, &length);
+
+  length += gator_buf_write_time(thread->buf, &write_pos);
+  length += gator_buf_write_int(thread->buf, &write_pos, channel);
+  length += gator_buf_write_bytes(thread->buf, &write_pos, str, str_size);
+
+  gator_msg_end(thread, write_pos, size_pos, length);
+}
+
+void gator_annotate_color(const uint32_t channel, const uint32_t color, const char* const str)
+{
+  struct gator_thread* const thread = gator_get_thread();
+  if(thread == NULL)
+  {
+    return;
+  }
+
+  const int str_size = (str == NULL) ? 0 : strlen(str);
+  gator_buf_wait_bytes(thread, 1 + sizeof(uint32_t) + MAXSIZE_PACK_LONG + MAXSIZE_PACK_INT + SIZE_COLOR + str_size);
+
+  uint32_t write_pos;
+  uint32_t size_pos;
+  uint32_t length;
+  gator_msg_begin(HEADER_UTF8_COLOR, thread, &write_pos, &size_pos, &length);
+
+  length += gator_buf_write_time(thread->buf, &write_pos);
+  length += gator_buf_write_int(thread->buf, &write_pos, channel);
+  length += gator_buf_write_color(thread->buf, &write_pos, color);
+  length += gator_buf_write_bytes(thread->buf, &write_pos, str, str_size);
+
+  gator_msg_end(thread, write_pos, size_pos, length);
+}
+
+void gator_annotate_name_channel(const uint32_t channel, const uint32_t group, const char* const str)
+{
+  struct gator_thread* const thread = gator_get_thread();
+  if(thread == NULL)
+  {
+    return;
+  }
+
+  const int str_size = (str == NULL) ? 0 : strlen(str);
+  gator_buf_wait_bytes(thread, 1 + sizeof(uint32_t) + MAXSIZE_PACK_LONG + 2 * MAXSIZE_PACK_INT + str_size);
+
+  uint32_t write_pos;
+  uint32_t size_pos;
+  uint32_t length;
+  gator_msg_begin(HEADER_CHANNEL_NAME, thread, &write_pos, &size_pos, &length);
+
+  length += gator_buf_write_time(thread->buf, &write_pos);
+  length += gator_buf_write_int(thread->buf, &write_pos, channel);
+  length += gator_buf_write_int(thread->buf, &write_pos, group);
+  length += gator_buf_write_bytes(thread->buf, &write_pos, str, str_size);
+
+  gator_msg_end(thread, write_pos, size_pos, length);
+}
+
+void gator_annotate_name_group(const uint32_t group, const char* const str)
+{
+  struct gator_thread* const thread = gator_get_thread();
+  if(thread == NULL)
+  {
+    return;
+  }
+
+  const int str_size = (str == NULL) ? 0 : strlen(str);
+  gator_buf_wait_bytes(thread, 1 + sizeof(uint32_t) + MAXSIZE_PACK_LONG + MAXSIZE_PACK_INT + str_size);
+
+  uint32_t write_pos;
+  uint32_t size_pos;
+  uint32_t length;
+  gator_msg_begin(HEADER_GROUP_NAME, thread, &write_pos, &size_pos, &length);
+
+  length += gator_buf_write_time(thread->buf, &write_pos);
+  length += gator_buf_write_int(thread->buf, &write_pos, group);
+  length += gator_buf_write_bytes(thread->buf, &write_pos, str, str_size);
+
+  gator_msg_end(thread, write_pos, size_pos, length);
+}
+
+void gator_annotate_visual(const void* const data, const uint32_t data_length, const char* const str)
+{
+  struct gator_thread* const thread = gator_get_thread();
+  if(thread == NULL)
+  {
+    return;
+  }
+
+  const int str_size = (str == NULL) ? 0 : strlen(str);
+  /* Don't include data_length because it doesn't end up in the buffer */
+  gator_buf_wait_bytes(thread, 1 + sizeof(uint32_t) + MAXSIZE_PACK_LONG + str_size + 1);
+
+  uint32_t write_pos;
+  uint32_t size_pos;
+  uint32_t length;
+  gator_msg_begin(HEADER_VISUAL, thread, &write_pos, &size_pos, &length);
+
+  length += gator_buf_write_time(thread->buf, &write_pos);
+  length += gator_buf_write_bytes(thread->buf, &write_pos, str, str_size);
+  length += gator_buf_write_byte(thread->buf, &write_pos, '\0');
+  /* Calculate the length, but don't write the image */
+  length += data_length;
+  /* Write the length and commit the first part of the message */
+  gator_buf_write_uint32(thread->buf, &size_pos, length);
+  thread->write_pos = write_pos;
+
+  thread->oob_data = (const char*)data;
+  __sync_synchronize();
+  thread->oob_length = data_length;
+  while(thread->oob_length > 0)
+  {
+    sem_post(&gator_state.sender_sem);
+    sem_wait(&thread->sem);
+  }
+}
+
+void gator_annotate_marker(const char* const str)
+{
+  struct gator_thread* const thread = gator_get_thread();
+  if(thread == NULL)
+  {
+    return;
+  }
+  const int str_size = (str == NULL) ? 0 : strlen(str);
+  gator_buf_wait_bytes(thread, 1 + sizeof(uint32_t) + MAXSIZE_PACK_LONG + str_size);
+
+  uint32_t write_pos;
+  uint32_t size_pos;
+  uint32_t length;
+  gator_msg_begin(HEADER_MARKER, thread, &write_pos, &size_pos, &length);
+
+  length += gator_buf_write_time(thread->buf, &write_pos);
+  length += gator_buf_write_bytes(thread->buf, &write_pos, str, str_size);
+
+  gator_msg_end(thread, write_pos, size_pos, length);
+}
+
+void gator_annotate_marker_color(const uint32_t color, const char* const str)
+{
+  struct gator_thread* const thread = gator_get_thread();
+  if(thread == NULL)
+  {
+    return;
+  }
+  const int str_size = (str == NULL) ? 0 : strlen(str);
+  gator_buf_wait_bytes(thread, 1 + sizeof(uint32_t) + MAXSIZE_PACK_LONG + SIZE_COLOR + str_size);
+
+  uint32_t write_pos;
+  uint32_t size_pos;
+  uint32_t length;
+  gator_msg_begin(HEADER_MARKER_COLOR, thread, &write_pos, &size_pos, &length);
+
+  length += gator_buf_write_time(thread->buf, &write_pos);
+  length += gator_buf_write_color(thread->buf, &write_pos, color);
+  length += gator_buf_write_bytes(thread->buf, &write_pos, str, str_size);
+
+  gator_msg_end(thread, write_pos, size_pos, length);
+}
+
+static void gator_annotate_write_counter(struct gator_thread* const thread, const struct gator_counter* const counter)
+{
+  const int title_size       = (counter->title == NULL) ? 0 : strlen(counter->title);
+  const int name_size        = (counter->name == NULL) ? 0 : strlen(counter->name);
+  const int units_size       = (counter->units == NULL) ? 0 : strlen(counter->units);
+  const int description_size = (counter->description == NULL) ? 0 : strlen(counter->description);
+  int       activity_size    = 0;
+  size_t    i;
+  for(i = 0; i < counter->activity_count; ++i)
+  {
+    activity_size += (counter->activities[i] == NULL) ? 0 : strlen(counter->activities[i]);
+    activity_size += SIZE_COLOR;
+  }
+  gator_buf_wait_bytes(thread,
+                       1 + sizeof(uint32_t) + 12 * MAXSIZE_PACK_INT + SIZE_COLOR + activity_size + title_size + 1 +
+                         name_size + 1 + units_size + 1 + description_size);
+
+  uint32_t write_pos;
+  uint32_t size_pos;
+  uint32_t length;
+  gator_msg_begin(HEADER_COUNTER, thread, &write_pos, &size_pos, &length);
+
+  length += gator_buf_write_int(thread->buf, &write_pos, counter->id);
+  length += gator_buf_write_int(thread->buf, &write_pos, counter->per_cpu);
+  length += gator_buf_write_int(thread->buf, &write_pos, counter->counter_class);
+  length += gator_buf_write_int(thread->buf, &write_pos, counter->display);
+  length += gator_buf_write_int(thread->buf, &write_pos, counter->modifier);
+  length += gator_buf_write_int(thread->buf, &write_pos, counter->series_composition);
+  length += gator_buf_write_int(thread->buf, &write_pos, counter->rendering_type);
+  length += gator_buf_write_int(thread->buf, &write_pos, counter->average_selection);
+  length += gator_buf_write_int(thread->buf, &write_pos, counter->average_cores);
+  length += gator_buf_write_int(thread->buf, &write_pos, counter->percentage);
+  length += gator_buf_write_int(thread->buf, &write_pos, counter->activity_count);
+  length += gator_buf_write_int(thread->buf, &write_pos, counter->cores);
+  length += gator_buf_write_color(thread->buf, &write_pos, counter->color);
+  for(i = 0; i < counter->activity_count; ++i)
+  {
+    length += gator_buf_write_bytes(thread->buf,
+                                    &write_pos,
+                                    counter->activities[i],
+                                    (counter->activities[i] == NULL) ? 0 : strlen(counter->activities[i]));
+    length += gator_buf_write_byte(thread->buf, &write_pos, '\0');
+    length += gator_buf_write_color(thread->buf, &write_pos, counter->activity_colors[i]);
+  }
+  length += gator_buf_write_bytes(thread->buf, &write_pos, counter->title, title_size);
+  length += gator_buf_write_byte(thread->buf, &write_pos, '\0');
+  length += gator_buf_write_bytes(thread->buf, &write_pos, counter->name, name_size);
+  length += gator_buf_write_byte(thread->buf, &write_pos, '\0');
+  length += gator_buf_write_bytes(thread->buf, &write_pos, counter->units, units_size);
+  length += gator_buf_write_byte(thread->buf, &write_pos, '\0');
+  length += gator_buf_write_bytes(thread->buf, &write_pos, counter->description, description_size);
+
+  gator_msg_end(thread, write_pos, size_pos, length);
+}
+
+void gator_annotate_counter(const uint32_t                               id,
+                            const char* const                            title,
+                            const char* const                            name,
+                            const int                                    per_cpu,
+                            const enum gator_annotate_counter_class      counter_class,
+                            const enum gator_annotate_display            display,
+                            const char* const                            units,
+                            const uint32_t                               modifier,
+                            const enum gator_annotate_series_composition series_composition,
+                            const enum gator_annotate_rendering_type     rendering_type,
+                            const int                                    average_selection,
+                            const int                                    average_cores,
+                            const int                                    percentage,
+                            const size_t                                 activity_count,
+                            const char* const* const                     activities,
+                            const uint32_t* const                        activity_colors,
+                            const uint32_t                               cores,
+                            const uint32_t                               color,
+                            const char* const                            description)
+{
+  struct gator_counter* const counter = (struct gator_counter*)malloc(sizeof(*counter));
+  if(counter == NULL)
+  {
+    LOG(LOG_ERROR, "malloc failed, with error %s", strerror(errno));
+    return;
+  }
+
+  /* Save off this counter so it can be resent if needed */
+  counter->id                 = id;
+  counter->title              = (title == NULL) ? NULL : strdup(title);
+  counter->name               = (name == NULL) ? NULL : strdup(name);
+  counter->per_cpu            = per_cpu;
+  counter->counter_class      = counter_class;
+  counter->display            = display;
+  counter->units              = (units == NULL) ? NULL : strdup(units);
+  counter->modifier           = modifier;
+  counter->series_composition = series_composition;
+  counter->rendering_type     = rendering_type;
+  counter->average_selection  = average_selection;
+  counter->average_cores      = average_cores;
+  counter->percentage         = percentage;
+  counter->activity_count     = activity_count;
+  if(activity_count == 0)
+  {
+    counter->activities      = NULL;
+    counter->activity_colors = NULL;
+  }
+  else
+  {
+    counter->activities = (const char**)malloc(activity_count * sizeof(activities[0]));
+    if(counter->activities == NULL)
+    {
+      LOG(LOG_ERROR, "malloc failed, with error %s", strerror(errno));
+      goto free_counter;
+    }
+    counter->activity_colors = (uint32_t*)malloc(activity_count * sizeof(activity_colors[0]));
+    if(counter->activity_colors == NULL)
+    {
+      LOG(LOG_ERROR, "malloc failed, with error %s", strerror(errno));
+      goto free_activities;
+    }
+    size_t i;
+    for(i = 0; i < activity_count; ++i)
+    {
+      counter->activities[i]      = (activities[i] == NULL) ? NULL : strdup(activities[i]);
+      counter->activity_colors[i] = activity_colors[i];
+    }
+  }
+  counter->cores       = cores;
+  counter->color       = color;
+  counter->description = (description == NULL) ? NULL : strdup(description);
+
+  do
+  {
+    counter->next = gator_state.counters;
+  } while(!__sync_bool_compare_and_swap(&gator_state.counters, counter->next, counter));
+
+  {
+    struct gator_thread* const thread = gator_get_thread();
+    if(thread == NULL)
+    {
+      return;
+    }
+
+    gator_annotate_write_counter(thread, counter);
+  }
+
+  return;
+
+free_activities:
+  free(counter->activities);
+free_counter:
+  free(counter);
+}
+
+// NOLINTNEXTLINE(bugprone-easily-swappable-parameters, readability-identifier-length)
+void gator_annotate_counter_time_value(const uint32_t core, const uint32_t id, const uint64_t time, const int64_t value)
+{
+  struct gator_thread* const thread = gator_get_thread();
+  if(thread == NULL)
+  {
+    return;
+  }
+
+  gator_buf_wait_bytes(thread, 1 + sizeof(uint32_t) + 2 * MAXSIZE_PACK_LONG + 2 * MAXSIZE_PACK_INT);
+
+  uint32_t write_pos;
+  uint32_t size_pos;
+  uint32_t length;
+  gator_msg_begin(HEADER_COUNTER_VALUE, thread, &write_pos, &size_pos, &length);
+
+  // NOLINTNEXTLINE(bugprone-narrowing-conversions)
+  length += gator_buf_write_long(thread->buf, &write_pos, time);
+  length += gator_buf_write_int(thread->buf, &write_pos, core);
+  length += gator_buf_write_int(thread->buf, &write_pos, id);
+  length += gator_buf_write_long(thread->buf, &write_pos, value);
+
+  gator_msg_end(thread, write_pos, size_pos, length);
+}
+
+// NOLINTNEXTLINE(readability-identifier-length)
+void gator_annotate_counter_value(const uint32_t core, const uint32_t id, const int64_t value)
+{
+  const uint64_t current_time = gator_get_time();
+  gator_annotate_counter_time_value(core, id, current_time, value);
+}
+
+// NOLINTNEXTLINE(readability-identifier-length)
+void gator_annotate_activity_switch(const uint32_t core, const uint32_t id, const uint32_t activity, const uint32_t tid)
+{
+  struct gator_thread* const thread = gator_get_thread();
+  if(thread == NULL)
+  {
+    return;
+  }
+
+  gator_buf_wait_bytes(thread, 1 + sizeof(uint32_t) + MAXSIZE_PACK_LONG + 4 * MAXSIZE_PACK_INT);
+
+  uint32_t write_pos;
+  uint32_t size_pos;
+  uint32_t length;
+  gator_msg_begin(HEADER_ACTIVITY_SWITCH, thread, &write_pos, &size_pos, &length);
+
+  length += gator_buf_write_time(thread->buf, &write_pos);
+  length += gator_buf_write_int(thread->buf, &write_pos, core);
+  length += gator_buf_write_int(thread->buf, &write_pos, id);
+  length += gator_buf_write_int(thread->buf, &write_pos, activity);
+  length += gator_buf_write_int(thread->buf, &write_pos, tid);
+
+  gator_msg_end(thread, write_pos, size_pos, length);
+}
+
+static void gator_annotate_write_cam_track(struct gator_thread* const          thread,
+                                           const struct gator_cam_track* const cam_track)
+{
+  const int name_size = (cam_track->name == NULL) ? 0 : strlen(cam_track->name);
+  gator_buf_wait_bytes(thread, 1 + sizeof(uint32_t) + 3 * MAXSIZE_PACK_INT + name_size);
+
+  uint32_t write_pos;
+  uint32_t size_pos;
+  uint32_t length;
+  gator_msg_begin(HEADER_CAM_TRACK, thread, &write_pos, &size_pos, &length);
+
+  length += gator_buf_write_int(thread->buf, &write_pos, cam_track->view_uid);
+  length += gator_buf_write_int(thread->buf, &write_pos, cam_track->track_uid);
+  length += gator_buf_write_int(thread->buf, &write_pos, cam_track->parent_track);
+  length += gator_buf_write_bytes(thread->buf, &write_pos, cam_track->name, name_size);
+
+  gator_msg_end(thread, write_pos, size_pos, length);
+}
+
+void gator_cam_track(const uint32_t    view_uid,
+                     const uint32_t    track_uid,
+                     const uint32_t    parent_track,
+                     const char* const name)
+{
+  struct gator_cam_track* const cam_track = (struct gator_cam_track*)malloc(sizeof(*cam_track));
+  if(cam_track == NULL)
+  {
+    LOG(LOG_ERROR, "malloc failed, with error %s", strerror(errno));
+    return;
+  }
+
+  /* Save off this track so it can be resent if needed */
+  cam_track->view_uid     = view_uid;
+  cam_track->track_uid    = track_uid;
+  cam_track->parent_track = parent_track;
+  cam_track->name         = (name == NULL) ? NULL : strdup(name);
+
+  do
+  {
+    cam_track->next = gator_state.cam_tracks;
+  } while(!__sync_bool_compare_and_swap(&gator_state.cam_tracks, cam_track->next, cam_track));
+
+  struct gator_thread* const thread = gator_get_thread();
+  if(thread == NULL)
+  {
+    return;
+  }
+
+  gator_annotate_write_cam_track(thread, cam_track);
+}
+
+void gator_cam_job(const uint32_t        view_uid,
+                   const uint32_t        job_uid,
+                   const char* const     name,
+                   const uint32_t        track,
+                   const uint64_t        start_time,
+                   const uint64_t        duration,
+                   const uint32_t        color,
+                   const uint32_t        primary_dependency,
+                   const size_t          dependency_count,
+                   const uint32_t* const dependencies)
+{
+  struct gator_thread* const thread = gator_get_thread();
+  if(thread == NULL)
+  {
+    return;
+  }
+
+  const int name_size = (name == NULL) ? 0 : strlen(name);
+  gator_buf_wait_bytes(thread,
+                       1 + sizeof(uint32_t) + 5 * MAXSIZE_PACK_INT + 2 * MAXSIZE_PACK_LONG + SIZE_COLOR +
+                         dependency_count * MAXSIZE_PACK_INT + name_size);
+
+  uint32_t write_pos;
+  uint32_t size_pos;
+  uint32_t length;
+  gator_msg_begin(HEADER_CAM_JOB, thread, &write_pos, &size_pos, &length);
+
+  length += gator_buf_write_int(thread->buf, &write_pos, view_uid);
+  length += gator_buf_write_int(thread->buf, &write_pos, job_uid);
+  length += gator_buf_write_int(thread->buf, &write_pos, track);
+  length += gator_buf_write_long(thread->buf, &write_pos, start_time);
+  length += gator_buf_write_long(thread->buf, &write_pos, duration);
+  length += gator_buf_write_color(thread->buf, &write_pos, color);
+  // NOLINTNEXTLINE(bugprone-narrowing-conversions)
+  length += gator_buf_write_int(thread->buf, &write_pos, primary_dependency);
+  length += gator_buf_write_int(thread->buf, &write_pos, dependency_count);
+  size_t i;
+  for(i = 0; i < dependency_count; ++i)
+  {
+    length += gator_buf_write_int(thread->buf, &write_pos, dependencies[i]);
+  }
+  length += gator_buf_write_bytes(thread->buf, &write_pos, name, name_size);
+
+  gator_msg_end(thread, write_pos, size_pos, length);
+}
+
+void gator_cam_job_start(const uint32_t    view_uid,
+                         const uint32_t    job_uid,
+                         const char* const name,
+                         const uint32_t    track,
+                         const uint64_t    time,
+                         const uint32_t    color)
+{
+  struct gator_thread* const thread = gator_get_thread();
+  if(thread == NULL)
+  {
+    return;
+  }
+
+  const int name_size = (name == NULL) ? 0 : strlen(name);
+  gator_buf_wait_bytes(thread,
+                       1 + sizeof(uint32_t) + 3 * MAXSIZE_PACK_INT + MAXSIZE_PACK_LONG + SIZE_COLOR + name_size);
+
+  uint32_t write_pos;
+  uint32_t size_pos;
+  uint32_t length;
+  gator_msg_begin(HEADER_CAM_JOB_START, thread, &write_pos, &size_pos, &length);
+
+  length += gator_buf_write_int(thread->buf, &write_pos, view_uid);
+  length += gator_buf_write_int(thread->buf, &write_pos, job_uid);
+  length += gator_buf_write_int(thread->buf, &write_pos, track);
+  length += gator_buf_write_long(thread->buf, &write_pos, time);
+  length += gator_buf_write_color(thread->buf, &write_pos, color);
+  length += gator_buf_write_bytes(thread->buf, &write_pos, name, name_size);
+
+  gator_msg_end(thread, write_pos, size_pos, length);
+}
+
+void gator_cam_job_set_dependencies(const uint32_t        view_uid,
+                                    const uint32_t        job_uid,
+                                    const uint64_t        time,
+                                    const uint32_t        primary_dependency,
+                                    const size_t          dependency_count,
+                                    const uint32_t* const dependencies)
+{
+  struct gator_thread* const thread = gator_get_thread();
+  if(thread == NULL)
+  {
+    return;
+  }
+
+  gator_buf_wait_bytes(thread,
+                       1 + sizeof(uint32_t) + 4 * MAXSIZE_PACK_INT + MAXSIZE_PACK_LONG +
+                         dependency_count * MAXSIZE_PACK_INT);
+
+  uint32_t write_pos;
+  uint32_t size_pos;
+  uint32_t length;
+  gator_msg_begin(HEADER_CAM_JOB_SET_DEPS, thread, &write_pos, &size_pos, &length);
+
+  length += gator_buf_write_int(thread->buf, &write_pos, view_uid);
+  length += gator_buf_write_int(thread->buf, &write_pos, job_uid);
+  length += gator_buf_write_long(thread->buf, &write_pos, time);
+  length += gator_buf_write_int(thread->buf, &write_pos, primary_dependency);
+  length += gator_buf_write_int(thread->buf, &write_pos, dependency_count);
+  size_t i;
+  for(i = 0; i < dependency_count; ++i)
+  {
+    length += gator_buf_write_int(thread->buf, &write_pos, dependencies[i]);
+  }
+
+  gator_msg_end(thread, write_pos, size_pos, length);
+}
+
+void gator_cam_job_stop(const uint32_t view_uid, const uint32_t job_uid, const uint64_t time)
+{
+  struct gator_thread* const thread = gator_get_thread();
+  if(thread == NULL)
+  {
+    return;
+  }
+
+  gator_buf_wait_bytes(thread, 1 + sizeof(uint32_t) + 2 * MAXSIZE_PACK_INT + MAXSIZE_PACK_LONG);
+
+  uint32_t write_pos;
+  uint32_t size_pos;
+  uint32_t length;
+  gator_msg_begin(HEADER_CAM_JOB_STOP, thread, &write_pos, &size_pos, &length);
+
+  length += gator_buf_write_int(thread->buf, &write_pos, view_uid);
+  length += gator_buf_write_int(thread->buf, &write_pos, job_uid);
+  length += gator_buf_write_long(thread->buf, &write_pos, time);
+
+  gator_msg_end(thread, write_pos, size_pos, length);
+}
+
+static void gator_annotate_write_cam_name(struct gator_thread* const         thread,
+                                          const struct gator_cam_name* const cam_name)
+{
+  const int name_size = (cam_name->name == NULL) ? 0 : strlen(cam_name->name);
+  gator_buf_wait_bytes(thread, 1 + sizeof(uint32_t) + MAXSIZE_PACK_INT + name_size);
+
+  uint32_t write_pos;
+  uint32_t size_pos;
+  uint32_t length;
+  gator_msg_begin(HEADER_CAM_VIEW_NAME, thread, &write_pos, &size_pos, &length);
+
+  length += gator_buf_write_int(thread->buf, &write_pos, cam_name->view_uid);
+  length += gator_buf_write_bytes(thread->buf, &write_pos, cam_name->name, name_size);
+
+  gator_msg_end(thread, write_pos, size_pos, length);
+}
+
+void gator_cam_view_name(const uint32_t view_uid, const char* const name)
+{
+  struct gator_cam_name* const cam_name = (struct gator_cam_name*)malloc(sizeof(*cam_name));
+  if(cam_name == NULL)
+  {
+    return;
+  }
+
+  /* Save off this name so it can be resent if needed */
+  cam_name->next     = NULL;
+  cam_name->view_uid = view_uid;
+  cam_name->name     = (name == NULL) ? NULL : strdup(name);
+
+  do
+  {
+    cam_name->next = gator_state.cam_names;
+  } while(!__sync_bool_compare_and_swap(&gator_state.cam_names, cam_name->next, cam_name));
+
+  struct gator_thread* const thread = gator_get_thread();
+  if(thread == NULL)
+  {
+    return;
+  }
+
+  gator_annotate_write_cam_name(thread, cam_name);
+}
diff --git a/dali/internal/trace/streamline/streamline_annotate.h b/dali/internal/trace/streamline/streamline_annotate.h
new file mode 100644 (file)
index 0000000..2c8bd88
--- /dev/null
@@ -0,0 +1,347 @@
+/* Copyright (C) 2014-2023 by Arm Limited. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * 3. Neither the name of the copyright holder nor the names of its
+ *    contributors may be used to endorse or promote products derived from
+ *    this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
+ * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef STREAMLINE_ANNOTATE_H
+#define STREAMLINE_ANNOTATE_H
+
+/*
+ *  ANNOTATE_DEFINE                              Deprecated and no longer used
+ *
+ *  ANNOTATE_SETUP                               Execute at the start of the program before other ANNOTATE macros are called
+ *
+ *  For defining integer counters where the numeric value is in the range 0...1<<63-1:
+ *
+ *  ANNOTATE_DELTA_COUNTER                       Define a delta counter
+ *  ANNOTATE_ABSOLUTE_COUNTER                    Define an absolute counter
+ *  ANNOTATE_COUNTER_VALUE                       Emit a counter value
+ *
+ *  For defining fractional (float/double) counters:
+ *
+ *  NB: The float value is converted to an integer value in the above range using some fixed multiplier, so for example
+ *  to send a float value in the range 0.000...1.000, with 3-decimal places accuracy, use a modifier value of 1000.
+ *  The float values will be converted to integers between 0 ... 1000.
+ *
+ *  ANNOTATE_DELTA_COUNTER_SCALE                 Define a delta counter with scaling
+ *  ANNOTATE_ABSOLUTE_COUNTER_SCALE              Define an absolute counter with scaling
+ *  ANNOTATE_COUNTER_VALUE_SCALE                 Emit a counter value with scaling
+ *
+ *  For defining CAM views, which visualize multiple linked 'jobs' within a Gantt-like view:
+ *
+ *  CAM_VIEW_NAME                                Name the custom activity map view
+ *  CAM_TRACK                                    Create a new custom activity map track
+ *  CAM_JOB                                      Add a new job to a CAM track, use gator_get_time() to obtain the time in nanoseconds
+ *
+ *  For defining textual annotations:
+ *
+ *  NB: Channels and groups are defined per thread. This means that if the same
+ *  channel number is used on different threads they are in fact separate
+ *  channels. A channel can belong to only one group per thread. This means
+ *  channel 1 cannot be part of both group 1 and group 2 on the same thread.
+ *
+ *  ANNOTATE_NAME_GROUP(group, str)              Name a group
+ *  ANNOTATE_NAME_CHANNEL(channel, group, str)   Name a channel and link it to a group
+ *  ANNOTATE(str)                                String annotation
+ *  ANNOTATE_CHANNEL(channel, str)               String annotation on a channel
+ *  ANNOTATE_COLOR(color, str)                   String annotation with color
+ *  ANNOTATE_CHANNEL_COLOR(channel, color, str)  String annotation on a channel with color
+ *  ANNOTATE_END()                               Terminate an annotation
+ *  ANNOTATE_CHANNEL_END(channel)                Terminate an annotation on a channel
+ *
+ *  For sending image annotations:
+ *
+ *  ANNOTATE_VISUAL(data, length, str)           Image annotation with optional string
+ *
+ *  For sending bookmark annotations:
+ *
+ *  ANNOTATE_MARKER()                            Marker annotation
+ *  ANNOTATE_MARKER_STR(str)                     Marker annotation with a string
+ *  ANNOTATE_MARKER_COLOR(color)                 Marker annotation with a color
+ *  ANNOTATE_MARKER_COLOR_STR(color, str)        Marker annotation with a string and color
+ */
+
+/* ESC character, hex RGB (little endian) */
+#define ANNOTATE_RED 0x0000ff1b
+#define ANNOTATE_BLUE 0xff00001b
+#define ANNOTATE_GREEN 0x00ff001b
+#define ANNOTATE_PURPLE 0xff00ff1b
+#define ANNOTATE_YELLOW 0x00ffff1b
+#define ANNOTATE_CYAN 0xffff001b
+#define ANNOTATE_WHITE 0xffffff1b
+#define ANNOTATE_LTGRAY 0xbbbbbb1b
+#define ANNOTATE_DKGRAY 0x5555551b
+#define ANNOTATE_BLACK 0x0000001b
+
+#define ANNOTATE_COLOR_CYCLE 0x00000000
+#define ANNOTATE_COLOR_T1 0x00000001
+#define ANNOTATE_COLOR_T2 0x00000002
+#define ANNOTATE_COLOR_T3 0x00000003
+#define ANNOTATE_COLOR_T4 0x00000004
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+  enum gator_annotate_counter_class
+  {
+    ANNOTATE_DELTA = 1,
+    ANNOTATE_ABSOLUTE,
+    ANNOTATE_ACTIVITY,
+    ANNOTATE_INCIDENT
+  };
+
+  enum gator_annotate_display
+  {
+    ANNOTATE_AVERAGE = 1,
+    ANNOTATE_ACCUMULATE,
+    ANNOTATE_HERTZ,
+    ANNOTATE_MAXIMUM,
+    ANNOTATE_MINIMUM
+  };
+
+  enum gator_annotate_series_composition
+  {
+    ANNOTATE_STACKED = 1,
+    ANNOTATE_OVERLAY,
+    ANNOTATE_LOG10
+  };
+
+  enum gator_annotate_rendering_type
+  {
+    ANNOTATE_FILL = 1,
+    ANNOTATE_LINE,
+    ANNOTATE_BAR
+  };
+
+  void     gator_annotate_setup(void);
+  uint64_t gator_get_time(void);
+  void     gator_annotate_fork_child(void);
+  void     gator_annotate_flush(void);
+  void     gator_annotate_str(uint32_t channel, const char* str);
+  void     gator_annotate_color(uint32_t channel, uint32_t color, const char* str);
+  void     gator_annotate_name_channel(uint32_t channel, uint32_t group, const char* str);
+  void     gator_annotate_name_group(uint32_t group, const char* str);
+  void     gator_annotate_visual(const void* data, uint32_t length, const char* str);
+  void     gator_annotate_marker(const char* str);
+  void     gator_annotate_marker_color(uint32_t color, const char* str);
+  void     gator_annotate_counter(uint32_t                               id,
+                                  const char*                            title,
+                                  const char*                            name,
+                                  int                                    per_cpu,
+                                  enum gator_annotate_counter_class      counter_class,
+                                  enum gator_annotate_display            display,
+                                  const char*                            units,
+                                  uint32_t                               modifier,
+                                  enum gator_annotate_series_composition series_composition,
+                                  enum gator_annotate_rendering_type     rendering_type,
+                                  int                                    average_selection,
+                                  int                                    average_cores,
+                                  int                                    percentage,
+                                  size_t                                 activity_count,
+                                  const char* const*                     activities,
+                                  const uint32_t*                        activity_colors,
+                                  uint32_t                               cores,
+                                  uint32_t                               color,
+                                  const char*                            description);
+  void     gator_annotate_counter_value(uint32_t core, uint32_t id, int64_t value);
+  void     gator_annotate_counter_time_value(uint32_t core, uint32_t id, uint64_t time, int64_t value);
+  void     gator_annotate_activity_switch(uint32_t core, uint32_t id, uint32_t activity, uint32_t tid);
+  void     gator_cam_track(uint32_t view_uid, uint32_t track_uid, uint32_t parent_track, const char* name);
+  void     gator_cam_job(uint32_t        view_uid,
+                         uint32_t        job_uid,
+                         const char*     name,
+                         uint32_t        track,
+                         uint64_t        start_time,
+                         uint64_t        duration,
+                         uint32_t        color,
+                         uint32_t        primary_dependency,
+                         size_t          dependency_count,
+                         const uint32_t* dependencies);
+  void     gator_cam_job_start(uint32_t    view_uid,
+                               uint32_t    job_uid,
+                               const char* name,
+                               uint32_t    track,
+                               uint64_t    time,
+                               uint32_t    color);
+  void     gator_cam_job_set_dependencies(uint32_t        view_uid,
+                                          uint32_t        job_uid,
+                                          uint64_t        time,
+                                          uint32_t        primary_dependency,
+                                          size_t          dependency_count,
+                                          const uint32_t* dependencies);
+  void     gator_cam_job_stop(uint32_t view_uid, uint32_t job_uid, uint64_t time);
+  void     gator_cam_view_name(uint32_t view_uid, const char* name);
+
+#define ANNOTATE_DEFINE extern int gator_annotate_unused
+
+#define ANNOTATE_SETUP gator_annotate_setup()
+
+#define ANNOTATE(str) gator_annotate_str(0, str)
+#define ANNOTATE_CHANNEL(channel, str) gator_annotate_str(channel, str)
+#define ANNOTATE_COLOR(color, str) gator_annotate_color(0, color, str)
+#define ANNOTATE_CHANNEL_COLOR(channel, color, str) gator_annotate_color(channel, color, str)
+#define ANNOTATE_END() gator_annotate_str(0, NULL)
+#define ANNOTATE_CHANNEL_END(channel) gator_annotate_str(channel, NULL)
+#define ANNOTATE_NAME_CHANNEL(channel, group, str) gator_annotate_name_channel(channel, group, str)
+#define ANNOTATE_NAME_GROUP(group, str) gator_annotate_name_group(group, str)
+
+#define ANNOTATE_VISUAL(data, length, str) gator_annotate_visual(data, length, str)
+
+#define ANNOTATE_MARKER() gator_annotate_marker(NULL)
+#define ANNOTATE_MARKER_STR(str) gator_annotate_marker(str)
+#define ANNOTATE_MARKER_COLOR(color) gator_annotate_marker_color(color, NULL)
+#define ANNOTATE_MARKER_COLOR_STR(color, str) gator_annotate_marker_color(color, str)
+
+#define ANNOTATE_DELTA_COUNTER(id, title, name) \
+  gator_annotate_counter(id,                    \
+                         title,                 \
+                         name,                  \
+                         0,                     \
+                         ANNOTATE_DELTA,        \
+                         ANNOTATE_ACCUMULATE,   \
+                         NULL,                  \
+                         1,                     \
+                         ANNOTATE_STACKED,      \
+                         ANNOTATE_FILL,         \
+                         0,                     \
+                         0,                     \
+                         0,                     \
+                         0,                     \
+                         NULL,                  \
+                         NULL,                  \
+                         0,                     \
+                         ANNOTATE_COLOR_CYCLE,  \
+                         NULL)
+#define ANNOTATE_ABSOLUTE_COUNTER(id, title, name) \
+  gator_annotate_counter(id,                       \
+                         title,                    \
+                         name,                     \
+                         0,                        \
+                         ANNOTATE_ABSOLUTE,        \
+                         ANNOTATE_MAXIMUM,         \
+                         NULL,                     \
+                         1,                        \
+                         ANNOTATE_STACKED,         \
+                         ANNOTATE_FILL,            \
+                         0,                        \
+                         0,                        \
+                         0,                        \
+                         0,                        \
+                         NULL,                     \
+                         NULL,                     \
+                         0,                        \
+                         ANNOTATE_COLOR_CYCLE,     \
+                         NULL)
+#define ANNOTATE_COUNTER_VALUE(id, value) gator_annotate_counter_value(0, id, value)
+
+#define ANNOTATE_DELTA_COUNTER_SCALE(id, title, name, modifier) \
+  gator_annotate_counter(id,                                    \
+                         title,                                 \
+                         name,                                  \
+                         0,                                     \
+                         ANNOTATE_DELTA,                        \
+                         ANNOTATE_ACCUMULATE,                   \
+                         NULL,                                  \
+                         modifier,                              \
+                         ANNOTATE_STACKED,                      \
+                         ANNOTATE_FILL,                         \
+                         0,                                     \
+                         0,                                     \
+                         0,                                     \
+                         0,                                     \
+                         NULL,                                  \
+                         NULL,                                  \
+                         0,                                     \
+                         ANNOTATE_COLOR_CYCLE,                  \
+                         NULL)
+#define ANNOTATE_ABSOLUTE_COUNTER_SCALE(id, title, name, modifier) \
+  gator_annotate_counter(id,                                       \
+                         title,                                    \
+                         name,                                     \
+                         0,                                        \
+                         ANNOTATE_ABSOLUTE,                        \
+                         ANNOTATE_MAXIMUM,                         \
+                         NULL,                                     \
+                         modifier,                                 \
+                         ANNOTATE_STACKED,                         \
+                         ANNOTATE_FILL,                            \
+                         0,                                        \
+                         0,                                        \
+                         0,                                        \
+                         0,                                        \
+                         NULL,                                     \
+                         NULL,                                     \
+                         0,                                        \
+                         ANNOTATE_COLOR_CYCLE,                     \
+                         NULL)
+#define ANNOTATE_COUNTER_VALUE_SCALE(id, value, modifier)             \
+  do                                                                  \
+  {                                                                   \
+    uint64_t __scaledvalue = (uint64_t)((value) * (modifier) + 0.5f); \
+    gator_annotate_counter_value(0, id, __scaledvalue);               \
+  } while(0)
+
+#define ANNOTATE_COUNTER_TIME_VALUE_SCALE(id, time, value, modifier)  \
+  do                                                                  \
+  {                                                                   \
+    uint64_t __scaledvalue = (uint64_t)((value) * (modifier) + 0.5f); \
+    gator_annotate_counter_time_value(0, id, time, __scaledvalue);    \
+  } while(0)
+
+#define CAM_TRACK(view_uid, track_uid, parent_track, name) gator_cam_track(view_uid, track_uid, parent_track, name)
+#define CAM_JOB(view_uid, job_uid, name, track, start_time, duration, color) \
+  gator_cam_job(view_uid, job_uid, name, track, start_time, duration, color, -1, 0, 0)
+#define CAM_JOB_DEP(view_uid, job_uid, name, track, start_time, duration, color, dependency)          \
+  {                                                                                                   \
+    uint32_t __dependency = dependency;                                                               \
+    gator_cam_job(view_uid, job_uid, name, track, start_time, duration, color, -1, 1, &__dependency); \
+  }
+#define CAM_JOB_DEPS(view_uid, job_uid, name, track, start_time, duration, color, dependency_count, dependencies) \
+  gator_cam_job(view_uid, job_uid, name, track, start_time, duration, color, -1, dependency_count, dependencies)
+#define CAM_JOB_START(view_uid, job_uid, name, track, time, color) \
+  gator_cam_job_start(view_uid, job_uid, name, track, time, color)
+#define CAM_JOB_SET_DEP(view_uid, job_uid, time, dependency)                       \
+  {                                                                                \
+    uint32_t __dependency = dependency;                                            \
+    gator_cam_job_set_dependencies(view_uid, job_uid, time, -1, 1, &__dependency); \
+  }
+#define CAM_JOB_SET_DEPS(view_uid, job_uid, time, dependency_count, dependencies) \
+  gator_cam_job_set_dependencies(view_uid, job_uid, time, -1, dependency_count, dependencies)
+#define CAM_JOB_STOP(view_uid, job_uid, time) gator_cam_job_stop(view_uid, job_uid, time)
+#define CAM_VIEW_NAME(view_uid, name) gator_cam_view_name(view_uid, name)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* STREAMLINE_ANNOTATE_H */
diff --git a/dali/internal/trace/streamline/streamline_annotate_logging.h b/dali/internal/trace/streamline/streamline_annotate_logging.h
new file mode 100644 (file)
index 0000000..86b7017
--- /dev/null
@@ -0,0 +1,55 @@
+/* Copyright (C) 2021 by Arm Limited. All rights reserved. */
+
+#ifndef STREAMLINE_ANNOTATE_LOGGING_H
+#define STREAMLINE_ANNOTATE_LOGGING_H
+
+//Mapped to android log levels - android_LogPriority
+enum log_levels
+{
+  LOG_UNKNOWN = 0,
+  LOG_DEFAULT,
+  LOG_VERBOSE,
+  LOG_DEBUG,
+  LOG_INFO,
+  LOG_WARN,
+  LOG_ERROR,
+  LOG_FATAL,
+  LOG_SILENT
+};
+
+/* ANDROID IMPLEMENTATION */
+#if defined(ANDROID) || defined(__ANDROID__)
+#include <android/log.h>
+
+#define LOG_TAG "AnnotationLog"
+
+#define LOGGING(LOG_LEVEL, fmt, ...) \
+  __android_log_print(LOG_LEVEL, LOG_TAG, "%s/%s:%d " fmt, __FILE__, __FUNCTION__, __LINE__, ##__VA_ARGS__);
+
+/* LINUX IMPLEMENTATION */
+#elif defined(linux) || defined(__linux) || defined(__linux__)
+// clang-format off
+char *log_levels[] = { "UNKNOWN",
+                       "DEFAULT",
+                       "VERBOSE",
+                       "DEBUG",
+                       "INFO",
+                       "WARN",
+                       "ERROR",
+                       "FATAL",
+                       "SILENT"};
+// clang-format on
+#define LOGGING(LOG_LEVEL, fmt, ...) \
+  printf("%s/%s:%d [%s] " fmt " \n", __FILE__, __func__, __LINE__, log_levels[LOG_LEVEL], ##__VA_ARGS__);
+
+#endif
+//Use to do logging, if not needed un-define this variable
+#define ENABLE_LOG
+
+#ifdef ENABLE_LOG
+#define LOG(LOG_LEVEL, fmt, ...) LOGGING(LOG_LEVEL, fmt, ##__VA_ARGS__)
+#else
+#define LOG(LOG_LEVEL, fmt, ...) // nothing
+#endif
+
+#endif /* STREAMLINE_ANNOTATE_LOGGING_H */
diff --git a/dali/internal/trace/streamline/trace-factory-streamline.cpp b/dali/internal/trace/streamline/trace-factory-streamline.cpp
new file mode 100644 (file)
index 0000000..1aa4b7a
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include <dali/internal/trace/common/trace-factory.h>
+#include <dali/internal/trace/streamline/trace-manager-impl-streamline.h>
+
+namespace Dali
+{
+namespace Internal
+{
+namespace Adaptor
+{
+namespace TraceManagerFactory
+{
+// TraceManager Factory to be implemented by the platform
+TraceManagerUPtr CreateTraceFactory(PerformanceInterface* performanceInterface)
+{
+  return TraceManagerUPtr(new Dali::Internal::Adaptor::TraceManagerStreamline(performanceInterface));
+}
+
+} // namespace TraceManagerFactory
+
+} // namespace Adaptor
+
+} // namespace Internal
+
+} // namespace Dali
diff --git a/dali/internal/trace/streamline/trace-manager-impl-streamline.cpp b/dali/internal/trace/streamline/trace-manager-impl-streamline.cpp
new file mode 100644 (file)
index 0000000..63536d5
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// CLASS HEADER
+#include <dali/internal/trace/streamline/trace-manager-impl-streamline.h>
+
+// EXTERNAL INCLUDES
+#include <dali/integration-api/debug.h>
+#include <dali/internal/system/common/performance-interface.h>
+
+// INTERNAL INCLUDES
+#include <dali/devel-api/adaptor-framework/environment-variable.h>
+#include <dali/internal/trace/streamline/streamline_annotate.h>
+
+ANNOTATE_DEFINE;
+
+namespace Dali::Internal::Adaptor
+{
+namespace
+{
+const char* DALI_TRACE_ENABLE_PRINT_LOG_ENV = "DALI_TRACE_ENABLE_PRINT_LOG";
+const char* EMPTY_TAG                       = "(null)";
+bool        gTraceManagerEnablePrintLog     = false;
+} // namespace
+
+TraceManagerStreamline* TraceManagerStreamline::traceManagerStreamline = nullptr;
+
+TraceManagerStreamline::TraceManagerStreamline(PerformanceInterface* performanceInterface)
+: TraceManager(performanceInterface)
+{
+  ANNOTATE_SETUP;
+  const char* enablePrintLog = Dali::EnvironmentVariable::GetEnvironmentVariable(DALI_TRACE_ENABLE_PRINT_LOG_ENV);
+  if(enablePrintLog && std::atoi(enablePrintLog) != 0)
+  {
+    gTraceManagerEnablePrintLog = true;
+  }
+
+  TraceManagerStreamline::traceManagerStreamline = this;
+}
+
+Dali::Integration::Trace::LogContextFunction TraceManagerStreamline::GetLogContextFunction()
+{
+  return LogContext;
+}
+
+void TraceManagerStreamline::LogContext(bool start, const char* tag, const char* message)
+{
+  if(traceManagerStreamline)
+  {
+    if(start)
+    {
+      ANNOTATE(tag);
+    }
+    else
+    {
+      ANNOTATE_END();
+    }
+  }
+
+  if(gTraceManagerEnablePrintLog)
+  {
+    if(start)
+    {
+      DALI_LOG_DEBUG_INFO("BEGIN: %s%s%s\n", tag ? tag : EMPTY_TAG, message ? " " : "", message ? message : "");
+    }
+    else
+    {
+      DALI_LOG_DEBUG_INFO("END: %s%s%s\n", tag ? tag : EMPTY_TAG, message ? " " : "", message ? message : "");
+    }
+  }
+}
+
+} // namespace Dali::Internal::Adaptor
diff --git a/dali/internal/trace/streamline/trace-manager-impl-streamline.h b/dali/internal/trace/streamline/trace-manager-impl-streamline.h
new file mode 100644 (file)
index 0000000..307ab14
--- /dev/null
@@ -0,0 +1,61 @@
+#pragma once
+
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// EXTERNAL INCLUDES
+
+// INTERNAL INCLUDES
+#include <dali/internal/trace/common/trace-manager-impl.h>
+
+namespace Dali::Internal::Adaptor
+{
+class PerformanceInterface;
+
+class TraceManagerStreamline : public Dali::Internal::Adaptor::TraceManager
+{
+public:
+  /**
+   * Static member to hold TraceManagerStreamline instance. This allows
+   * to access PerformanceInterface for network logging.
+   */
+  static TraceManagerStreamline* traceManagerStreamline;
+
+  /**
+   * Explicit Constructor
+   */
+  explicit TraceManagerStreamline(PerformanceInterface* performanceInterface);
+
+protected:
+  /**
+   * Destructor
+   */
+  ~TraceManagerStreamline() override = default;
+
+  /**
+   * Obtain the LogContextFunction method used for tracing
+   */
+  Dali::Integration::Trace::LogContextFunction GetLogContextFunction() final;
+
+private:
+  /**
+   * LogContext method (Generic specific) used for tracing
+   */
+  static void LogContext(bool start, const char* tag, const char* message);
+};
+
+} // namespace Dali::Internal::Adaptor
index 627cb5b..dd2fb0d 100644 (file)
@@ -270,7 +270,13 @@ CXXFLAGS+=" -D_ARCH_ARM_ -lgcc"
 
 CFLAGS+=" -DWAYLAND"
 CXXFLAGS+=" -DWAYLAND"
-cmake_flags=" -DENABLE_WAYLAND=ON -DENABLE_ATSPI=ON -DENABLE_TRACE=ON"
+cmake_flags=" -DENABLE_WAYLAND=ON -DENABLE_ATSPI=ON"
+
+%if 0%{?enable_streamline}
+cmake_flags+=" -DENABLE_TRACE_STREAMLINE=ON"
+%else
+cmake_flags+=" -DENABLE_TRACE=ON"
+%endif
 
 # Use this conditional when Tizen version is 5.x or greater
 %if 0%{?tizen_version_major} >= 5