From a9e88f3f202392e5f4023a4ebf53636e406b1106 Mon Sep 17 00:00:00 2001 From: Jakob Bornecrantz Date: Sat, 31 Aug 2019 12:46:25 +0100 Subject: [PATCH] aux/util: Add variable tracking code --- src/xrt/auxiliary/CMakeLists.txt | 2 + src/xrt/auxiliary/util/u_var.cpp | 243 +++++++++++++++++++++++++++++++ src/xrt/auxiliary/util/u_var.h | 127 ++++++++++++++++ 3 files changed, 372 insertions(+) create mode 100644 src/xrt/auxiliary/util/u_var.cpp create mode 100644 src/xrt/auxiliary/util/u_var.h diff --git a/src/xrt/auxiliary/CMakeLists.txt b/src/xrt/auxiliary/CMakeLists.txt index 3db7338fb..8a4ecf041 100644 --- a/src/xrt/auxiliary/CMakeLists.txt +++ b/src/xrt/auxiliary/CMakeLists.txt @@ -47,6 +47,8 @@ set(UTIL_SOURCE_FILES util/u_sink_queue.c util/u_time.cpp util/u_time.h + util/u_var.cpp + util/u_var.h ) # Common includes diff --git a/src/xrt/auxiliary/util/u_var.cpp b/src/xrt/auxiliary/util/u_var.cpp new file mode 100644 index 000000000..49e2b490d --- /dev/null +++ b/src/xrt/auxiliary/util/u_var.cpp @@ -0,0 +1,243 @@ +// Copyright 2019, Collabora, Ltd. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Variable tracking code. + * @author Jakob Bornecrantz + * @ingroup aux_util + */ + +#include "util/u_var.h" +#include "util/u_time.h" +#include "util/u_misc.h" +#include "util/u_debug.h" +#include "util/u_device.h" + +#include +#include +#include +#include + + +/* + * + * Enums, Classes and Defines. + * + */ + + +class Var +{ +public: + std::string name; + u_var_kind kind; + union { + void *ptr; + struct xrt_colour_rgb_u8 *rgb_u8; + struct xrt_colour_rgb_f32 *rgb_f32; + bool *boolean; + }; +}; + +class Obj +{ +public: + std::string name = {}; + std::vector vars = {}; +}; + +class Tracker +{ +public: + std::unordered_map counters = {}; + std::unordered_map map = {}; + bool on; + bool tested; + +public: + int + getNumber(std::string name) + { + auto s = counters.find(name); + int count = (s != counters.end() ? s->second : 0) + 1; + counters[name] = count; + + return count; + } +}; + +static class Tracker tracker; + + +/* + * + * Helper functions. + * + */ + +static bool +get_on() +{ + if (tracker.tested) { + return tracker.on; + } + tracker.on = debug_get_bool_option("XRT_TRACK_VARIABLES", false); + tracker.tested = true; + + return tracker.on; +} + +static void +add_var(void *root, void *ptr, u_var_kind kind, const char *c_name) +{ + auto s = tracker.map.find((ptrdiff_t)root); + if (s == tracker.map.end()) { + return; + } + + Var var; + var.name = std::string(c_name); + var.kind = kind; + var.ptr = ptr; + + s->second.vars.push_back(var); +} + + +/* + * + * Exported functions. + * + */ + +extern "C" void +u_var_force_on(void) +{ + tracker.on = true; + tracker.tested = true; +} + +extern "C" void +u_var_add_root(void *root, const char *c_name, bool number) +{ + if (!get_on()) { + return; + } + + auto name = std::string(c_name); + + if (number) { + int count = tracker.getNumber(name); + + std::stringstream ss; + ss << name << " #" << count; + name = ss.str(); + } + + auto &obj = tracker.map[(ptrdiff_t)root] = Obj(); + obj.name = name; +} + +extern "C" void +u_var_remove_root(void *root) +{ + if (!get_on()) { + return; + } + + auto s = tracker.map.find((ptrdiff_t)root); + if (s == tracker.map.end()) { + return; + } + + tracker.map.erase(s); +} + +extern "C" void +u_var_add_bool(void *obj, bool *ptr, const char *c_name) +{ + if (!get_on()) { + return; + } + + add_var(obj, ptr, U_VAR_KIND_BOOL, c_name); +} + +extern "C" void +u_var_add_u8(void *obj, uint8_t *ptr, const char *c_name) +{ + if (!get_on()) { + return; + } + + add_var(obj, ptr, U_VAR_KIND_U8, c_name); +} + +extern "C" void +u_var_add_u32(void *obj, uint32_t *ptr, const char *c_name) +{ + if (!get_on()) { + return; + } + + add_var(obj, ptr, U_VAR_KIND_U32, c_name); +} + +extern "C" void +u_var_add_rgb_u8(void *obj, struct xrt_colour_rgb_u8 *ptr, const char *c_name) +{ + if (!get_on()) { + return; + } + + add_var(obj, ptr, U_VAR_KIND_RGB_U8, c_name); +} + +extern "C" void +u_var_add_rgb_f32(void *obj, struct xrt_colour_rgb_f32 *ptr, const char *c_name) +{ + if (!get_on()) { + return; + } + + add_var(obj, ptr, U_VAR_KIND_RGB_F32, c_name); +} + +extern "C" void +u_var_add_text(void *obj, const char *ptr, const char *c_name) +{ + if (!get_on()) { + return; + } + + add_var(obj, (void *)ptr, U_VAR_KIND_TEXT, c_name); +} + +extern "C" void +u_var_visit(u_var_root_cb enter, + u_var_root_cb exit, + u_var_elm_cb elem, + void *priv) +{ + if (!get_on()) { + return; + } + + std::vector tmp; + tmp.reserve(tracker.map.size()); + + for (auto &n : tracker.map) { + tmp.push_back(&n.second); + } + + for (Obj *obj : tmp) { + enter(obj->name.c_str(), priv); + + for (auto &var : obj->vars) { + elem(var.name.c_str(), (u_var_kind)var.kind, var.ptr, + priv); + } + + exit(obj->name.c_str(), priv); + } +} diff --git a/src/xrt/auxiliary/util/u_var.h b/src/xrt/auxiliary/util/u_var.h new file mode 100644 index 000000000..03c404c86 --- /dev/null +++ b/src/xrt/auxiliary/util/u_var.h @@ -0,0 +1,127 @@ +// Copyright 2019, Collabora, Ltd. +// SPDX-License-Identifier: BSL-1.0 +/*! + * @file + * @brief Variable tracking code. + * @author Jakob Bornecrantz + * @ingroup aux_util + */ + +#pragma once + +#include "xrt/xrt_defines.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/*! + * @ingroup aux_util + * @{ + */ + +/*! + * What kind of variable is this tracking. + */ +enum u_var_kind +{ + U_VAR_KIND_BOOL, + U_VAR_KIND_U8, + U_VAR_KIND_U32, + U_VAR_KIND_RGB_U8, + U_VAR_KIND_RGB_F32, + U_VAR_KIND_TEXT, +}; + +/*! + * Callback for entering and leaving root nodes. + */ +typedef void (*u_var_root_cb)(const char *, void *); + +/*! + * Callback on each variable a root node has. + */ +typedef void (*u_var_elm_cb)(const char *, enum u_var_kind, void *, void *); + +/*! + * Add a named root object, the u_var subsystem is completely none-invasive + * to the object it's tracking. The root pointer is used as a entry into a + * hashmap of hidden objecrs. When not active all calls are stubs and have no + * side-effects. + * + * This is intended only for debugging and is turned off by default, as this all + * very very unsafe. It is just pointers straight into objects, completely + * ignores ownership or any safe practices. + * + * If it's stupid, but it works, it ain't stupid. + * + * ```c + * // On create + * u_var_add_root((void*)psmv, "PS Move Controller", psmv_var_updated_callback); + * u_var_add_rgb_u8((void*)psmv, &psmv->led_color, "LED"); + * u_var_add_bool((void*)psmv, &psmv->print_spew, "Spew"); + * u_var_add_bool((void*)psmv, &psmv->print_debug, "Debug"); + * + * // On destroy, only need to destroy the root object. + * u_var_remove_root((void*)psmv); + * ``` + * + * @ingroup aux_util + */ +void +u_var_add_root(void *, const char *, bool); + +/*! + * Remove the root node. + */ +void +u_var_remove_root(void *); + +/*! + * Visit all root nodes and their variables. + */ +void +u_var_visit(u_var_root_cb enter, + u_var_root_cb exit, + u_var_elm_cb e_cb, + void *priv); + +/*! + * This forces the variable tracking code to on, it is disabled by default. + */ +void +u_var_force_on(void); + +//! Add a variable to track on a root node, does not claim ownership. +void +u_var_add_rgb_u8(void *, struct xrt_colour_rgb_u8 *, const char *); + +//! Add a variable to track on a root node, does not claim ownership. +void +u_var_add_rgb_f32(void *, struct xrt_colour_rgb_f32 *, const char *); + +//! Add a variable to track on a root node, does not claim ownership. +void +u_var_add_u8(void *, uint8_t *, const char *); + +//! Add a variable to track on a root node, does not claim ownership. +void +u_var_add_u32(void *, uint32_t *, const char *); + +//! Add a variable to track on a root node, does not claim ownership. +void +u_var_add_bool(void *, bool *, const char *); + +//! Add a variable to track on a root node, does not claim ownership. +void +u_var_add_text(void *, const char *, const char *); + +/*! + * @} + */ + + +#ifdef __cplusplus +} +#endif