From bdfe7a3e588c26a5b3eca0636a2353de2af24ff6 Mon Sep 17 00:00:00 2001 From: Derek Foreman Date: Thu, 9 Jan 2025 16:19:20 -0600 Subject: [PATCH] libweston: Add support for perfetto profiling This borrows heavily from the Mesa project's perfetto instrumentation, and for now just adds perfetto to the build and a collection of useful macros for adding trace points. The atomics have been cargo culted in - Weston currently has no need of such things, but I guess they're harmless for now and could be useful if we consider threads in the future. Signed-off-by: Derek Foreman --- .gitlab-ci.yml | 5 + .gitlab-ci/leak-sanitizer.supp | 3 + libweston/compositor.c | 3 + libweston/meson.build | 11 + libweston/perfetto/u_atomic.h | 369 +++++++++++++++++++++++++++++++ libweston/perfetto/u_perfetto.cc | 189 ++++++++++++++++ libweston/perfetto/u_perfetto.h | 127 +++++++++++ libweston/weston-trace.h | 145 ++++++++++++ meson.build | 12 +- meson_options.txt | 6 + subprojects/perfetto.wrap | 6 + 11 files changed, 875 insertions(+), 1 deletion(-) create mode 100644 libweston/perfetto/u_atomic.h create mode 100644 libweston/perfetto/u_perfetto.cc create mode 100644 libweston/perfetto/u_perfetto.h create mode 100644 libweston/weston-trace.h create mode 100644 subprojects/perfetto.wrap diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 8bdae2f95..2bbbc2287 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -271,6 +271,8 @@ aarch64-debian-container_prep: variables: CC: clang-$LLVM_VERSION CC_LD: lld-$LLVM_VERSION + CXX: clang++-$LLVM_VERSION + CXX_LD: lld-$LLVM_VERSION MESON_TOOLCHAIN_OPTIONS: "$MESON_OPTIONS -Db_lundef=false" # clang+ASan+undef=boom # Extends the core build templates to also provide for running our testing. We @@ -430,6 +432,9 @@ aarch64-debian-container_prep: MESON_OPTIONS: > -Doptimization=0 -Db_coverage=true + --force-fallback-for=perfetto + -Dperfetto=true + -Dperfetto:werror=false -Dwerror=true -Dtest-skip-is-failure=true after_script: diff --git a/.gitlab-ci/leak-sanitizer.supp b/.gitlab-ci/leak-sanitizer.supp index 5135912f0..3066b1b45 100644 --- a/.gitlab-ci/leak-sanitizer.supp +++ b/.gitlab-ci/leak-sanitizer.supp @@ -11,3 +11,6 @@ leak:wl_shm_buffer_begin_access leak:g_malloc0 leak:sysprof_collector_get leak:/ld-*.so* + +# Add all of perfetto, since it's not easy to clean up after it +leak:perfetto diff --git a/libweston/compositor.c b/libweston/compositor.c index 6918ac647..2af39c467 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -86,6 +86,7 @@ #include "output-capture.h" #include "pixman-renderer.h" #include "renderer-gl/gl-renderer.h" +#include "weston-trace.h" #include "weston-log-internal.h" @@ -9564,6 +9565,8 @@ weston_compositor_create(struct wl_display *display, if (!ec) return NULL; + util_perfetto_init(); + if (test_data) ec->test_data = *test_data; diff --git a/libweston/meson.build b/libweston/meson.build index dde0af7e1..8fcc6fc81 100644 --- a/libweston/meson.build +++ b/libweston/meson.build @@ -104,11 +104,22 @@ if get_option('backend-vnc') deps_libweston += dep_pam endif +if get_option('perfetto') + srcs_libweston += [ + 'perfetto/u_perfetto.cc', + 'perfetto/u_perfetto.h', + ] + dep_perfetto = dependency('perfetto', fallback: ['perfetto', 'dep_perfetto']) + deps_libweston += dep_perfetto + config_h.set('HAVE_PERFETTO', '1') +endif + lib_weston = shared_library( 'weston-@0@'.format(libweston_major), srcs_libweston, include_directories: common_inc, install: true, + cpp_args: '-std=c++17', version: '0.0.@0@'.format(libweston_revision), link_whole: lib_libshared, dependencies: deps_libweston diff --git a/libweston/perfetto/u_atomic.h b/libweston/perfetto/u_atomic.h new file mode 100644 index 000000000..93a036bc7 --- /dev/null +++ b/libweston/perfetto/u_atomic.h @@ -0,0 +1,369 @@ +/** + * Many similar implementations exist. See for example libwsbm + * or the linux kernel include/atomic.h + * + * No copyright claimed on this file. + * + */ + +/* This file is from the Mesa project */ + +#ifndef U_ATOMIC_H +#define U_ATOMIC_H + +#include +#include + +/* Favor OS-provided implementations. + * + * Where no OS-provided implementation is available, fall back to + * locally coded assembly, compiler intrinsic or ultimately a + * mutex-based implementation. + */ +#if defined(__sun) +#define PIPE_ATOMIC_OS_SOLARIS +#elif defined(_MSC_VER) +#define PIPE_ATOMIC_MSVC_INTRINSIC +#elif defined(__GNUC__) +#define PIPE_ATOMIC_GCC_INTRINSIC +#else +#error "Unsupported platform" +#endif + + +/* Implementation using GCC-provided synchronization intrinsics + */ +#if defined(PIPE_ATOMIC_GCC_INTRINSIC) + +#define PIPE_ATOMIC "GCC Sync Intrinsics" + +#if defined(USE_GCC_ATOMIC_BUILTINS) + +/* The builtins with explicit memory model are available since GCC 4.7. */ +#define p_atomic_set(_v, _i) __atomic_store_n((_v), (_i), __ATOMIC_RELEASE) +#define p_atomic_read(_v) __atomic_load_n((_v), __ATOMIC_ACQUIRE) +#define p_atomic_read_relaxed(_v) __atomic_load_n((_v), __ATOMIC_RELAXED) +#define p_atomic_dec_zero(v) (__atomic_sub_fetch((v), 1, __ATOMIC_ACQ_REL) == 0) +#define p_atomic_inc(v) (void) __atomic_add_fetch((v), 1, __ATOMIC_ACQ_REL) +#define p_atomic_dec(v) (void) __atomic_sub_fetch((v), 1, __ATOMIC_ACQ_REL) +#define p_atomic_add(v, i) (void) __atomic_add_fetch((v), (i), __ATOMIC_ACQ_REL) +#define p_atomic_inc_return(v) __atomic_add_fetch((v), 1, __ATOMIC_ACQ_REL) +#define p_atomic_dec_return(v) __atomic_sub_fetch((v), 1, __ATOMIC_ACQ_REL) +#define p_atomic_add_return(v, i) __atomic_add_fetch((v), (i), __ATOMIC_ACQ_REL) +#define p_atomic_fetch_add(v, i) __atomic_fetch_add((v), (i), __ATOMIC_ACQ_REL) +#define p_atomic_xchg(v, i) __atomic_exchange_n((v), (i), __ATOMIC_ACQ_REL) +#define PIPE_NATIVE_ATOMIC_XCHG + +#else + +#define p_atomic_set(_v, _i) (*(_v) = (_i)) +#define p_atomic_read(_v) (*(_v)) +#define p_atomic_read_relaxed(_v) (*(_v)) +#define p_atomic_dec_zero(v) (__sync_sub_and_fetch((v), 1) == 0) +#define p_atomic_inc(v) (void) __sync_add_and_fetch((v), 1) +#define p_atomic_dec(v) (void) __sync_sub_and_fetch((v), 1) +#define p_atomic_add(v, i) (void) __sync_add_and_fetch((v), (i)) +#define p_atomic_inc_return(v) __sync_add_and_fetch((v), 1) +#define p_atomic_dec_return(v) __sync_sub_and_fetch((v), 1) +#define p_atomic_add_return(v, i) __sync_add_and_fetch((v), (i)) +#define p_atomic_fetch_add(v, i) __sync_fetch_and_add((v), (i)) + +#endif + +/* There is no __atomic_* compare and exchange that returns the current value. + * Also, GCC 5.4 seems unable to optimize a compound statement expression that + * uses an additional stack variable with __atomic_compare_exchange[_n]. + */ +#define p_atomic_cmpxchg(v, old, _new) \ + __sync_val_compare_and_swap((v), (old), (_new)) +#define p_atomic_cmpxchg_ptr(v, old, _new) p_atomic_cmpxchg(v, old, _new) + +#endif + + + +/* Unlocked version for single threaded environments, such as some + * windows kernel modules. + */ +#if defined(PIPE_ATOMIC_OS_UNLOCKED) + +#define PIPE_ATOMIC "Unlocked" + +#define p_atomic_set(_v, _i) (*(_v) = (_i)) +#define p_atomic_read(_v) (*(_v)) +#define p_atomic_read_relaxed(_v) (*(_v)) +#define p_atomic_dec_zero(_v) (p_atomic_dec_return(_v) == 0) +#define p_atomic_inc(_v) ((void) p_atomic_inc_return(_v)) +#define p_atomic_dec(_v) ((void) p_atomic_dec_return(_v)) +#define p_atomic_add(_v, _i) ((void) p_atomic_add_return((_v), (_i))) +#define p_atomic_inc_return(_v) (++(*(_v))) +#define p_atomic_dec_return(_v) (--(*(_v))) +#define p_atomic_add_return(_v, _i) (*(_v) = *(_v) + (_i)) +#define p_atomic_fetch_add(_v, _i) (*(_v) = *(_v) + (_i), *(_v) - (_i)) +#define p_atomic_cmpxchg(_v, _old, _new) (*(_v) == (_old) ? (*(_v) = (_new), (_old)) : *(_v)) +#define p_atomic_cmpxchg_ptr(_v, _old, _new) p_atomic_cmpxchg(_v, _old, _new) + +#endif + + +#if defined(PIPE_ATOMIC_MSVC_INTRINSIC) + +#define PIPE_ATOMIC "MSVC Intrinsics" + +/* We use the Windows header's Interlocked*64 functions instead of the + * _Interlocked*64 intrinsics wherever we can, as support for the latter varies + * with target CPU, whereas Windows headers take care of all portability + * issues: using intrinsics where available, falling back to library + * implementations where not. + */ +#include +#include + +__forceinline char _interlockedadd8(char volatile * _Addend, char _Value) +{ + return _InterlockedExchangeAdd8(_Addend, _Value) + _Value; +} + +__forceinline short _interlockedadd16(short volatile * _Addend, short _Value) +{ + return _InterlockedExchangeAdd16(_Addend, _Value) + _Value; +} + +/* MSVC supports decltype keyword, but it's only supported on C++ and doesn't + * quite work here; and if a C++-only solution is worthwhile, then it would be + * better to use templates / function overloading, instead of decltype magic. + * Therefore, we rely on implicit casting to LONGLONG for the functions that return + */ + +#define p_atomic_set(_v, _i) (*(_v) = (_i)) +#define p_atomic_read(_v) (*(_v)) +#define p_atomic_read_relaxed(_v) (*(_v)) + +#define p_atomic_dec_zero(_v) \ + (p_atomic_dec_return(_v) == 0) + +#define p_atomic_inc(_v) \ + ((void) p_atomic_inc_return(_v)) + +#define p_atomic_inc_return(_v) (\ + sizeof *(_v) == sizeof(char) ? p_atomic_add_return((_v), 1) : \ + sizeof *(_v) == sizeof(short) ? _InterlockedIncrement16((short *) (_v)) : \ + sizeof *(_v) == sizeof(long) ? _InterlockedIncrement ((long *) (_v)) : \ + sizeof *(_v) == sizeof(__int64) ? _interlockedincrement64((__int64 *)(_v)) : \ + (assert(!"should not get here"), 0)) + +#define p_atomic_dec(_v) \ + ((void) p_atomic_dec_return(_v)) + +#define p_atomic_dec_return(_v) (\ + sizeof *(_v) == sizeof(char) ? p_atomic_add_return((_v), -1) : \ + sizeof *(_v) == sizeof(short) ? _InterlockedDecrement16((short *) (_v)) : \ + sizeof *(_v) == sizeof(long) ? _InterlockedDecrement ((long *) (_v)) : \ + sizeof *(_v) == sizeof(__int64) ? _interlockeddecrement64((__int64 *)(_v)) : \ + (assert(!"should not get here"), 0)) + +#define p_atomic_add(_v, _i) \ + ((void) p_atomic_fetch_add((_v), (_i))) + +#define p_atomic_add_return(_v, _i) (\ + sizeof *(_v) == sizeof(char) ? _interlockedadd8 ((char *) (_v), (_i)) : \ + sizeof *(_v) == sizeof(short) ? _interlockedadd16((short *) (_v), (_i)) : \ + sizeof *(_v) == sizeof(long) ? _interlockedadd ((long *) (_v), (_i)) : \ + sizeof *(_v) == sizeof(__int64) ? _interlockedadd64((__int64 *)(_v), (_i)) : \ + (assert(!"should not get here"), 0)) + +#define p_atomic_fetch_add(_v, _i) (\ + sizeof *(_v) == sizeof(char) ? _InterlockedExchangeAdd8 ((char *) (_v), (_i)) : \ + sizeof *(_v) == sizeof(short) ? _InterlockedExchangeAdd16((short *) (_v), (_i)) : \ + sizeof *(_v) == sizeof(long) ? _InterlockedExchangeAdd ((long *) (_v), (_i)) : \ + sizeof *(_v) == sizeof(__int64) ? _interlockedexchangeadd64((__int64 *)(_v), (_i)) : \ + (assert(!"should not get here"), 0)) + +#define p_atomic_cmpxchg(_v, _old, _new) (\ + sizeof *(_v) == sizeof(char) ? _InterlockedCompareExchange8 ((char *) (_v), (char) (_new), (char) (_old)) : \ + sizeof *(_v) == sizeof(short) ? _InterlockedCompareExchange16((short *) (_v), (short) (_new), (short) (_old)) : \ + sizeof *(_v) == sizeof(long) ? _InterlockedCompareExchange ((long *) (_v), (long) (_new), (long) (_old)) : \ + sizeof *(_v) == sizeof(__int64) ? _InterlockedCompareExchange64((__int64 *)(_v), (__int64)(_new), (__int64)(_old)) : \ + (assert(!"should not get here"), 0)) + +#if defined(_WIN64) +#define p_atomic_cmpxchg_ptr(_v, _old, _new) (void *)_InterlockedCompareExchange64((__int64 *)(_v), (__int64)(_new), (__int64)(_old)) +#else +#define p_atomic_cmpxchg_ptr(_v, _old, _new) (void *)_InterlockedCompareExchange((long *)(_v), (long)(_new), (long)(_old)) +#endif + +#define PIPE_NATIVE_ATOMIC_XCHG +#define p_atomic_xchg(_v, _new) (\ + sizeof *(_v) == sizeof(char) ? _InterlockedExchange8 ((char *) (_v), (char) (_new)) : \ + sizeof *(_v) == sizeof(short) ? _InterlockedExchange16((short *) (_v), (short) (_new)) : \ + sizeof *(_v) == sizeof(long) ? _InterlockedExchange ((long *) (_v), (long) (_new)) : \ + sizeof *(_v) == sizeof(__int64) ? _interlockedexchange64((__int64 *)(_v), (__int64)(_new)) : \ + (assert(!"should not get here"), 0)) + +#endif + +#if defined(PIPE_ATOMIC_OS_SOLARIS) + +#define PIPE_ATOMIC "Solaris OS atomic functions" + +#include +#include + +#define p_atomic_set(_v, _i) (*(_v) = (_i)) +#define p_atomic_read(_v) (*(_v)) + +#define p_atomic_dec_zero(v) (\ + sizeof(*v) == sizeof(uint8_t) ? atomic_dec_8_nv ((uint8_t *)(v)) == 0 : \ + sizeof(*v) == sizeof(uint16_t) ? atomic_dec_16_nv((uint16_t *)(v)) == 0 : \ + sizeof(*v) == sizeof(uint32_t) ? atomic_dec_32_nv((uint32_t *)(v)) == 0 : \ + sizeof(*v) == sizeof(uint64_t) ? atomic_dec_64_nv((uint64_t *)(v)) == 0 : \ + (assert(!"should not get here"), 0)) + +#define p_atomic_inc(v) (void) (\ + sizeof(*v) == sizeof(uint8_t) ? atomic_inc_8 ((uint8_t *)(v)) : \ + sizeof(*v) == sizeof(uint16_t) ? atomic_inc_16((uint16_t *)(v)) : \ + sizeof(*v) == sizeof(uint32_t) ? atomic_inc_32((uint32_t *)(v)) : \ + sizeof(*v) == sizeof(uint64_t) ? atomic_inc_64((uint64_t *)(v)) : \ + (assert(!"should not get here"), 0)) + +#define p_atomic_inc_return(v) (__typeof(*v))( \ + sizeof(*v) == sizeof(uint8_t) ? atomic_inc_8_nv ((uint8_t *)(v)) : \ + sizeof(*v) == sizeof(uint16_t) ? atomic_inc_16_nv((uint16_t *)(v)) : \ + sizeof(*v) == sizeof(uint32_t) ? atomic_inc_32_nv((uint32_t *)(v)) : \ + sizeof(*v) == sizeof(uint64_t) ? atomic_inc_64_nv((uint64_t *)(v)) : \ + (assert(!"should not get here"), 0)) + +#define p_atomic_dec(v) (void) ( \ + sizeof(*v) == sizeof(uint8_t) ? atomic_dec_8 ((uint8_t *)(v)) : \ + sizeof(*v) == sizeof(uint16_t) ? atomic_dec_16((uint16_t *)(v)) : \ + sizeof(*v) == sizeof(uint32_t) ? atomic_dec_32((uint32_t *)(v)) : \ + sizeof(*v) == sizeof(uint64_t) ? atomic_dec_64((uint64_t *)(v)) : \ + (assert(!"should not get here"), 0)) + +#define p_atomic_dec_return(v) (__typeof(*v))( \ + sizeof(*v) == sizeof(uint8_t) ? atomic_dec_8_nv ((uint8_t *)(v)) : \ + sizeof(*v) == sizeof(uint16_t) ? atomic_dec_16_nv((uint16_t *)(v)) : \ + sizeof(*v) == sizeof(uint32_t) ? atomic_dec_32_nv((uint32_t *)(v)) : \ + sizeof(*v) == sizeof(uint64_t) ? atomic_dec_64_nv((uint64_t *)(v)) : \ + (assert(!"should not get here"), 0)) + +#define p_atomic_add(v, i) (void) ( \ + sizeof(*v) == sizeof(uint8_t) ? atomic_add_8 ((uint8_t *)(v), (i)) : \ + sizeof(*v) == sizeof(uint16_t) ? atomic_add_16((uint16_t *)(v), (i)) : \ + sizeof(*v) == sizeof(uint32_t) ? atomic_add_32((uint32_t *)(v), (i)) : \ + sizeof(*v) == sizeof(uint64_t) ? atomic_add_64((uint64_t *)(v), (i)) : \ + (assert(!"should not get here"), 0)) + +#define p_atomic_add_return(v, i) (__typeof(*v)) ( \ + sizeof(*v) == sizeof(uint8_t) ? atomic_add_8_nv ((uint8_t *)(v), (i)) : \ + sizeof(*v) == sizeof(uint16_t) ? atomic_add_16_nv((uint16_t *)(v), (i)) : \ + sizeof(*v) == sizeof(uint32_t) ? atomic_add_32_nv((uint32_t *)(v), (i)) : \ + sizeof(*v) == sizeof(uint64_t) ? atomic_add_64_nv((uint64_t *)(v), (i)) : \ + (assert(!"should not get here"), 0)) + +#define p_atomic_fetch_add(v, i) (__typeof(*v)) ( \ + sizeof(*v) == sizeof(uint8_t) ? atomic_add_8_nv ((uint8_t *)(v), (i)) - (i) : \ + sizeof(*v) == sizeof(uint16_t) ? atomic_add_16_nv((uint16_t *)(v), (i)) - (i) : \ + sizeof(*v) == sizeof(uint32_t) ? atomic_add_32_nv((uint32_t *)(v), (i)) - (i) : \ + sizeof(*v) == sizeof(uint64_t) ? atomic_add_64_nv((uint64_t *)(v), (i)) - (i) : \ + (assert(!"should not get here"), 0)) + +#define p_atomic_cmpxchg(v, old, _new) (__typeof(*v))( \ + sizeof(*v) == sizeof(uint8_t) ? atomic_cas_8 ((uint8_t *)(v), (uint8_t )(old), (uint8_t )(_new)) : \ + sizeof(*v) == sizeof(uint16_t) ? atomic_cas_16((uint16_t *)(v), (uint16_t)(old), (uint16_t)(_new)) : \ + sizeof(*v) == sizeof(uint32_t) ? atomic_cas_32((uint32_t *)(v), (uint32_t)(old), (uint32_t)(_new)) : \ + sizeof(*v) == sizeof(uint64_t) ? atomic_cas_64((uint64_t *)(v), (uint64_t)(old), (uint64_t)(_new)) : \ + (assert(!"should not get here"), 0)) + +#if INTPTR_MAX == INT32_MAX +#define p_atomic_cmpxchg_ptr(v, old, _new) (__typeof(*v))(atomic_cas_32((uint32_t *)(v), (uint32_t)(old), (uint32_t)(_new))) +#else +#define p_atomic_cmpxchg_ptr(v, old, _new) (__typeof(*v))(atomic_cas_64((uint64_t *)(v), (uint64_t)(old), (uint64_t)(_new))) +#endif + +#endif + +#ifndef PIPE_ATOMIC +#error "No pipe_atomic implementation selected" +#endif + +#ifndef PIPE_NATIVE_ATOMIC_XCHG +static inline uint8_t p_atomic_xchg_8(uint8_t *v, uint8_t i) +{ + uint8_t actual = p_atomic_read(v); + uint8_t expected; + do { + expected = actual; + actual = p_atomic_cmpxchg(v, expected, i); + } while (expected != actual); + return actual; +} + +static inline uint16_t p_atomic_xchg_16(uint16_t *v, uint16_t i) +{ + uint16_t actual = p_atomic_read(v); + uint16_t expected; + do { + expected = actual; + actual = p_atomic_cmpxchg(v, expected, i); + } while (expected != actual); + return actual; +} + +static inline uint32_t p_atomic_xchg_32(uint32_t *v, uint32_t i) +{ + uint32_t actual = p_atomic_read(v); + uint32_t expected; + do { + expected = actual; + actual = p_atomic_cmpxchg(v, expected, i); + } while (expected != actual); + return actual; +} + +static inline uint64_t p_atomic_xchg_64(uint64_t *v, uint64_t i) +{ + uint64_t actual = p_atomic_read(v); + uint64_t expected; + do { + expected = actual; + actual = p_atomic_cmpxchg(v, expected, i); + } while (expected != actual); + return actual; +} + +#define p_atomic_xchg(v, i) (__typeof(*(v)))( \ + sizeof(*(v)) == sizeof(uint8_t) ? p_atomic_xchg_8 ((uint8_t *)(v), (uint8_t )(i)) : \ + sizeof(*(v)) == sizeof(uint16_t) ? p_atomic_xchg_16((uint16_t *)(v), (uint16_t)(i)) : \ + sizeof(*(v)) == sizeof(uint32_t) ? p_atomic_xchg_32((uint32_t *)(v), (uint32_t)(i)) : \ + sizeof(*(v)) == sizeof(uint64_t) ? p_atomic_xchg_64((uint64_t *)(v), (uint64_t)(i)) : \ + (assert(!"should not get here"), 0)) +#endif + +/* On x86 we can have sizeof(uint64_t) = 8 and _Alignof(uint64_t) = 4. causing split locks. The + * implementation does handle that correctly, but with an internal mutex. Extend the alignment to + * avoid this. + * `p_atomic_int64_t` and `p_atomic_uint64_t` are used for casting any pointer to + * `p_atomic_int64_t *` and `p_atomic_uint64_t *`. That's for telling the compiler is accessing + * the 64 bits atomic in 8 byte aligned way to avoid clang `misaligned atomic operation` warning. + * To define 64 bits atomic memeber in struct type, + * use `alignas(8) int64_t $member` or `alignas(8) uint64_t $member` is enough. + */ +typedef struct { +#ifndef __cplusplus + _Alignas(8) +#else + alignas(8) +#endif + int64_t value; +} p_atomic_int64_t; +typedef struct { +#ifndef __cplusplus + _Alignas(8) +#else + alignas(8) +#endif + uint64_t value; +} p_atomic_uint64_t; + +#endif /* U_ATOMIC_H */ diff --git a/libweston/perfetto/u_perfetto.cc b/libweston/perfetto/u_perfetto.cc new file mode 100644 index 000000000..9d0a2eb99 --- /dev/null +++ b/libweston/perfetto/u_perfetto.cc @@ -0,0 +1,189 @@ +/* + * Copyright © 2021 Google, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/* This code was lifted from Mesa, some code removed (eg: weston does not support + * WIN32), and edited to conform to weston coding style. + * + * The original copyright boilerplate has been retained. + */ + +#include "config.h" + +#include "u_perfetto.h" + +#include + +/* perfetto requires string literals */ +#define UTIL_PERFETTO_CATEGORY_DEFAULT_STR "weston.default" + +PERFETTO_DEFINE_CATEGORIES( + perfetto::Category(UTIL_PERFETTO_CATEGORY_DEFAULT_STR) + .SetDescription("Weston default events")); + +PERFETTO_TRACK_EVENT_STATIC_STORAGE(); + +int util_perfetto_tracing_state; + +static uint64_t util_perfetto_unique_id = 1; + +static uint32_t +clockid_to_perfetto_clock(clockid_t clock) +{ + switch (clock) { + case CLOCK_REALTIME: + return perfetto::protos::pbzero::BUILTIN_CLOCK_REALTIME; + case CLOCK_REALTIME_COARSE: + return perfetto::protos::pbzero::BUILTIN_CLOCK_REALTIME_COARSE; + case CLOCK_MONOTONIC: + return perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC; + case CLOCK_MONOTONIC_COARSE: + return perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC_COARSE; + case CLOCK_MONOTONIC_RAW: + return perfetto::protos::pbzero::BUILTIN_CLOCK_MONOTONIC_RAW; + case CLOCK_BOOTTIME: + return perfetto::protos::pbzero::BUILTIN_CLOCK_BOOTTIME; + } + return perfetto::protos::pbzero::BUILTIN_CLOCK_UNKNOWN; +} + +static void +util_perfetto_update_tracing_state(void) +{ + p_atomic_set(&util_perfetto_tracing_state, + TRACE_EVENT_CATEGORY_ENABLED(UTIL_PERFETTO_CATEGORY_DEFAULT_STR)); +} + +void +util_perfetto_trace_begin(const char *name) +{ + TRACE_EVENT_BEGIN(UTIL_PERFETTO_CATEGORY_DEFAULT_STR, nullptr, + [&](perfetto::EventContext ctx) { ctx.event()->set_name(name); }); +} + +void +util_perfetto_trace_end(void) +{ + TRACE_EVENT_END(UTIL_PERFETTO_CATEGORY_DEFAULT_STR); + + util_perfetto_update_tracing_state(); +} + +void +util_perfetto_trace_begin_flow(const char *fname, uint64_t id) +{ + TRACE_EVENT_BEGIN(UTIL_PERFETTO_CATEGORY_DEFAULT_STR, nullptr, + perfetto::Flow::ProcessScoped(id), + [&](perfetto::EventContext ctx) { ctx.event()->set_name(fname); }); +} + +void +util_perfetto_trace_full_begin(const char *fname, uint64_t track_id, uint64_t id, clockid_t clock, uint64_t timestamp) +{ + if (id) { + TRACE_EVENT_BEGIN(UTIL_PERFETTO_CATEGORY_DEFAULT_STR, + nullptr, + perfetto::Track(track_id), + perfetto::TraceTimestamp{clockid_to_perfetto_clock(clock), timestamp}, + perfetto::Flow::ProcessScoped(id), + [&](perfetto::EventContext ctx) { ctx.event()->set_name(fname); }); + return; + } + + TRACE_EVENT_BEGIN(UTIL_PERFETTO_CATEGORY_DEFAULT_STR, + nullptr, + perfetto::Track(track_id), + perfetto::TraceTimestamp{clockid_to_perfetto_clock(clock), timestamp}, + [&](perfetto::EventContext ctx) { ctx.event()->set_name(fname); }); +} + +uint64_t +util_perfetto_new_track(const char *name) +{ + uint64_t track_id = util_perfetto_next_id(); + auto track = perfetto::Track(track_id); + auto desc = track.Serialize(); + + desc.set_name(name); + perfetto::TrackEvent::SetTrackDescriptor(track, desc); + return track_id; +} + +void +util_perfetto_trace_full_end(const char *name, uint64_t track_id, clockid_t clock, uint64_t timestamp) +{ + TRACE_EVENT_END(UTIL_PERFETTO_CATEGORY_DEFAULT_STR, + perfetto::Track(track_id), + perfetto::TraceTimestamp{clockid_to_perfetto_clock(clock), timestamp}); + + util_perfetto_update_tracing_state(); +} + +void +util_perfetto_counter_set(const char *name, double value) +{ + TRACE_COUNTER(UTIL_PERFETTO_CATEGORY_DEFAULT_STR, + perfetto::DynamicString(name), value); +} + +uint64_t +util_perfetto_next_id(void) +{ + return p_atomic_inc_return(&util_perfetto_unique_id); +} + +class UtilPerfettoObserver : public perfetto::TrackEventSessionObserver { + public: + UtilPerfettoObserver() { perfetto::TrackEvent::AddSessionObserver(this); } + + void OnStart(const perfetto::DataSourceBase::StartArgs &) override { + util_perfetto_update_tracing_state(); + } + + /* XXX There is no PostStop callback. We have to call + * util_perfetto_update_tracing_state occasionally to poll. + */ +}; + +static void +util_perfetto_fini(void) +{ + perfetto::Tracing::Shutdown(); +} + +static bool perfetto_initialized = false; + +void +util_perfetto_init(void) +{ + if (perfetto_initialized) + return; + + perfetto::TracingInitArgs args; + args.backends = perfetto::kSystemBackend; + perfetto::Tracing::Initialize(args); + + static UtilPerfettoObserver observer; + perfetto::TrackEvent::Register(); + + atexit(&util_perfetto_fini); +} diff --git a/libweston/perfetto/u_perfetto.h b/libweston/perfetto/u_perfetto.h new file mode 100644 index 000000000..576348fce --- /dev/null +++ b/libweston/perfetto/u_perfetto.h @@ -0,0 +1,127 @@ +/* + * Copyright © 2021 Google, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +/* This code was lifted from Mesa, some code removed (eg: weston does not support + * WIN32), and edited to conform to weston coding style. + * + * The original copyright boilerplate has been retained. + */ + +#ifndef _UTIL_PERFETTO_H +#define _UTIL_PERFETTO_H + +#include "stdint.h" +#include + +#include "u_atomic.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef HAVE_PERFETTO + +extern int util_perfetto_tracing_state; + +void util_perfetto_init(void); + +static inline bool +util_perfetto_is_tracing_enabled(void) +{ + return p_atomic_read_relaxed(&util_perfetto_tracing_state); +} + +void util_perfetto_trace_begin(const char *name); + +void util_perfetto_trace_end(void); + +void util_perfetto_trace_begin_flow(const char *fname, uint64_t id); + +void util_perfetto_counter_set(const char *name, double value); + +void util_perfetto_trace_full_begin(const char *name, uint64_t track_id, uint64_t id, clockid_t clock, uint64_t timestamp); + +void util_perfetto_trace_full_end(const char *name, uint64_t track_id, clockid_t clock, uint64_t timestamp); + +uint64_t util_perfetto_next_id(void); + +uint64_t util_perfetto_new_track(const char *name); + +#else /* HAVE_PERFETTO */ + +static inline void +util_perfetto_init(void) +{ +} + +static inline bool +util_perfetto_is_tracing_enabled(void) +{ + return false; +} + +static inline void +util_perfetto_trace_begin(const char *name) +{ +} + +static inline void +util_perfetto_trace_end(void) +{ +} + +static inline void util_perfetto_trace_begin_flow(const char *fname, uint64_t id) +{ +} + +static inline void +util_perfetto_trace_full_begin(const char *name, uint64_t track_id, uint64_t id, clockid_t clock, uint64_t timestamp) +{ +} + +static inline void +util_perfetto_trace_full_end(const char *name, uint64_t track_id, clockid_t clock, uint64_t timestamp) +{ +} + +static inline void util_perfetto_counter_set(const char *name, double value) +{ +} + +static inline uint64_t util_perfetto_next_id(void) +{ + return 0; +} + +static inline uint64_t util_perfetto_new_track(const char *name) +{ + return 0; +} + +#endif /* HAVE_PERFETTO */ + +#ifdef __cplusplus +} +#endif + +#endif /* _UTIL_PERFETTO_H */ diff --git a/libweston/weston-trace.h b/libweston/weston-trace.h new file mode 100644 index 000000000..154663157 --- /dev/null +++ b/libweston/weston-trace.h @@ -0,0 +1,145 @@ +/* + * Copyright 2022 Google LLC + * SPDX-License-Identifier: MIT + */ + +/* This code was taken from the Mesa project, and heavily modified to + * suit weston's needs. + */ + +#ifndef WESTON_TRACE_H +#define WESTON_TRACE_H + +#include "perfetto/u_perfetto.h" + +#if defined(HAVE_PERFETTO) + +#if !defined(HAVE___BUILTIN_EXPECT) +# define __builtin_expect(x, y) (x) +#endif + +#ifndef likely +# ifdef HAVE___BUILTIN_EXPECT +# define likely(x) __builtin_expect(!!(x), 1) +# define unlikely(x) __builtin_expect(!!(x), 0) +# else +# define likely(x) (x) +# define unlikely(x) (x) +# endif +#endif + +/* note that util_perfetto_is_tracing_enabled always returns false until + * util_perfetto_init is called + */ +#define _WESTON_TRACE_BEGIN(name) \ + do { \ + if (unlikely(util_perfetto_is_tracing_enabled())) \ + util_perfetto_trace_begin(name); \ + } while (0) + +#define _WESTON_TRACE_FLOW_BEGIN(name, id) \ + do { \ + if (unlikely(util_perfetto_is_tracing_enabled())) \ + util_perfetto_trace_begin_flow(name, id); \ + } while (0) + +#define _WESTON_TRACE_END() \ + do { \ + if (unlikely(util_perfetto_is_tracing_enabled())) \ + util_perfetto_trace_end(); \ + } while (0) + +#define _WESTON_TRACE_SET_COUNTER(name, value) \ + do { \ + if (unlikely(util_perfetto_is_tracing_enabled())) \ + util_perfetto_counter_set(name, value); \ + } while (0) + +#define _WESTON_TRACE_TIMESTAMP_BEGIN(name, track_id, flow_id, clock, timestamp) \ + do { \ + if (unlikely(util_perfetto_is_tracing_enabled())) \ + util_perfetto_trace_full_begin(name, track_id, flow_id, \ + clock, timestamp); \ + } while (0) + +#define _WESTON_TRACE_TIMESTAMP_END(name, track_id, clock, timestamp) \ + do { \ + if (unlikely(util_perfetto_is_tracing_enabled())) \ + util_perfetto_trace_full_end(name, track_id, \ + clock, timestamp); \ + } while (0) + +#if __has_attribute(cleanup) && __has_attribute(unused) + +#define _WESTON_TRACE_SCOPE_VAR_CONCAT(name, suffix) name##suffix +#define _WESTON_TRACE_SCOPE_VAR(suffix) \ + _WESTON_TRACE_SCOPE_VAR_CONCAT(_weston_trace_scope_, suffix) + +/* This must expand to a single non-scoped statement for + * + * if (cond) + * _WESTON_TRACE_SCOPE(...) + * + * to work. + */ +#define _WESTON_TRACE_SCOPE(name) \ + int _WESTON_TRACE_SCOPE_VAR(__LINE__) \ + __attribute__((cleanup(_weston_trace_scope_end), unused)) = \ + _weston_trace_scope_begin(name) + +#define _WESTON_TRACE_SCOPE_FLOW(name, id) \ + int _WESTON_TRACE_SCOPE_VAR(__LINE__) \ + __attribute__((cleanup(_weston_trace_scope_end), unused)) = \ + _weston_trace_scope_flow_begin(name, id) + +static inline int +_weston_trace_scope_begin(const char *name) +{ + _WESTON_TRACE_BEGIN(name); + return 0; +} + +static inline int +_weston_trace_scope_flow_begin(const char *name, uint64_t *id) +{ + if (*id == 0) + *id = util_perfetto_next_id(); + _WESTON_TRACE_FLOW_BEGIN(name, *id); + return 0; +} + +static inline void +_weston_trace_scope_end(int *scope) +{ + _WESTON_TRACE_END(); +} + +#else + +#define _WESTON_TRACE_SCOPE(name) + +#endif /* __has_attribute(cleanup) && __has_attribute(unused) */ + +#else /* No perfetto, make these all do nothing */ + +#define _WESTON_TRACE_SCOPE(name) +#define _WESTON_TRACE_SCOPE_FLOW(name, id) +#define _WESTON_TRACE_FUNC() +#define _WESTON_TRACE_FUNC_FLOW(id) +#define _WESTON_TRACE_SET_COUNTER(name, value) +#define _WESTON_TRACE_TIMESTAMP_BEGIN(name, track_id, flow_id, clock, timestamp) +#define _WESTON_TRACE_TIMESTAMP_END(name, track_id, clock, timestamp) + +#endif /* HAVE_PERFETTO */ + +#define WESTON_TRACE_SCOPE(name) _WESTON_TRACE_SCOPE(name) +#define WESTON_TRACE_SCOPE_FLOW(name, id) _WESTON_TRACE_SCOPE_FLOW(name, id) +#define WESTON_TRACE_FUNC() _WESTON_TRACE_SCOPE(__func__) +#define WESTON_TRACE_FUNC_FLOW(id) _WESTON_TRACE_SCOPE_FLOW(__func__, id) +#define WESTON_TRACE_SET_COUNTER(name, value) _WESTON_TRACE_SET_COUNTER(name, value) +#define WESTON_TRACE_TIMESTAMP_BEGIN(name, track_id, flow_id, clock, timestamp) \ + _WESTON_TRACE_TIMESTAMP_BEGIN(name, track_id, flow_id, clock, timestamp) +#define WESTON_TRACE_TIMESTAMP_END(name, track_id, clock, timestamp) \ + _WESTON_TRACE_TIMESTAMP_END(name, track_id, clock, timestamp) + +#endif /* WESTON_TRACE_H */ diff --git a/meson.build b/meson.build index 779f25265..6e516c390 100644 --- a/meson.build +++ b/meson.build @@ -1,5 +1,5 @@ project('weston', - 'c', + ['c', 'cpp'], version: '14.0.90', default_options: [ 'warning_level=3', @@ -75,6 +75,16 @@ elif cc.has_header_symbol('sys/mkdev.h', 'major') config_h.set('MAJOR_IN_MKDEV', 1) endif +add_project_arguments(global_args, language: 'c') + +cpp = meson.get_compiler('cpp') + +global_cpp_args = cpp.get_supported_arguments( + '-Wno-unused-parameter', +) + +add_project_arguments(global_cpp_args, language: 'cpp') + optional_libc_funcs = [ 'mkostemp', 'strchrnul', 'initgroups', 'posix_fallocate', 'memfd_create', 'unreachable', diff --git a/meson_options.txt b/meson_options.txt index 0fbd42a9f..20701abd6 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -214,3 +214,9 @@ option( value: false, description: 'Generate documentation' ) +option( + 'perfetto', + type : 'boolean', + value : false, + description : 'Enable performance analysis with Perfetto.' +) diff --git a/subprojects/perfetto.wrap b/subprojects/perfetto.wrap new file mode 100644 index 000000000..87b5e389c --- /dev/null +++ b/subprojects/perfetto.wrap @@ -0,0 +1,6 @@ +[wrap-git] +directory = perfetto + +url = https://android.googlesource.com/platform/external/perfetto +revision = v47.0 +depth = 1