diff --git a/3rdparty/flatbuffers/LICENSE.txt b/3rdparty/flatbuffers/LICENSE.txt new file mode 100644 index 00000000000000..a4c5efd822fb2c --- /dev/null +++ b/3rdparty/flatbuffers/LICENSE.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2014 Google Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/3rdparty/flatbuffers/base.h b/3rdparty/flatbuffers/base.h new file mode 100644 index 00000000000000..5fe501779dedba --- /dev/null +++ b/3rdparty/flatbuffers/base.h @@ -0,0 +1,267 @@ +#ifndef FLATBUFFERS_BASE_H_ +#define FLATBUFFERS_BASE_H_ + +// clang-format off +#if defined(FLATBUFFERS_MEMORY_LEAK_TRACKING) && \ + defined(_MSC_VER) && defined(_DEBUG) + #define _CRTDBG_MAP_ALLOC +#endif + +#include + +#if !defined(FLATBUFFERS_ASSERT) +#define FLATBUFFERS_ASSERT assert +#endif + +#ifndef ARDUINO +#include +#endif + +#include +#include +#include + +#if defined(FLATBUFFERS_MEMORY_LEAK_TRACKING) && \ + defined(_MSC_VER) && defined(_DEBUG) + #include + #define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__) + #define new DEBUG_NEW +#endif + +#if defined(ARDUINO) && !defined(ARDUINOSTL_M_H) + #include +#else + #include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#ifdef _STLPORT_VERSION + #define FLATBUFFERS_CPP98_STL +#endif +#ifndef FLATBUFFERS_CPP98_STL + #include +#endif + +#include "flatbuffers/stl_emulation.h" + +/// @cond FLATBUFFERS_INTERNAL +#if __cplusplus <= 199711L && \ + (!defined(_MSC_VER) || _MSC_VER < 1600) && \ + (!defined(__GNUC__) || \ + (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ < 40400)) + #error A C++11 compatible compiler with support for the auto typing is \ + required for FlatBuffers. + #error __cplusplus _MSC_VER __GNUC__ __GNUC_MINOR__ __GNUC_PATCHLEVEL__ +#endif + +#if !defined(__clang__) && \ + defined(__GNUC__) && \ + (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__ < 40600) + // Backwards compatability for g++ 4.4, and 4.5 which don't have the nullptr + // and constexpr keywords. Note the __clang__ check is needed, because clang + // presents itself as an older GNUC compiler. + #ifndef nullptr_t + const class nullptr_t { + public: + template inline operator T*() const { return 0; } + private: + void operator&() const; + } nullptr = {}; + #endif + #ifndef constexpr + #define constexpr const + #endif +#endif + +// The wire format uses a little endian encoding (since that's efficient for +// the common platforms). +#if defined(__s390x__) + #define FLATBUFFERS_LITTLEENDIAN 0 +#endif // __s390x__ +#if !defined(FLATBUFFERS_LITTLEENDIAN) + #if defined(__GNUC__) || defined(__clang__) + #ifdef __BIG_ENDIAN__ + #define FLATBUFFERS_LITTLEENDIAN 0 + #else + #define FLATBUFFERS_LITTLEENDIAN 1 + #endif // __BIG_ENDIAN__ + #elif defined(_MSC_VER) + #if defined(_M_PPC) + #define FLATBUFFERS_LITTLEENDIAN 0 + #else + #define FLATBUFFERS_LITTLEENDIAN 1 + #endif + #else + #error Unable to determine endianness, define FLATBUFFERS_LITTLEENDIAN. + #endif +#endif // !defined(FLATBUFFERS_LITTLEENDIAN) + +#define FLATBUFFERS_VERSION_MAJOR 1 +#define FLATBUFFERS_VERSION_MINOR 9 +#define FLATBUFFERS_VERSION_REVISION 0 +#define FLATBUFFERS_STRING_EXPAND(X) #X +#define FLATBUFFERS_STRING(X) FLATBUFFERS_STRING_EXPAND(X) + +#if (!defined(_MSC_VER) || _MSC_VER > 1600) && \ + (!defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 407)) + #define FLATBUFFERS_FINAL_CLASS final + #define FLATBUFFERS_OVERRIDE override +#else + #define FLATBUFFERS_FINAL_CLASS + #define FLATBUFFERS_OVERRIDE +#endif + +#if (!defined(_MSC_VER) || _MSC_VER >= 1900) && \ + (!defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 406)) + #define FLATBUFFERS_CONSTEXPR constexpr +#else + #define FLATBUFFERS_CONSTEXPR +#endif + +#if (defined(__cplusplus) && __cplusplus >= 201402L) || \ + (defined(__cpp_constexpr) && __cpp_constexpr >= 201304) + #define FLATBUFFERS_CONSTEXPR_CPP14 FLATBUFFERS_CONSTEXPR +#else + #define FLATBUFFERS_CONSTEXPR_CPP14 +#endif + +#if defined(__GXX_EXPERIMENTAL_CXX0X__) && __GNUC__ * 10 + __GNUC_MINOR__ >= 46 || \ + defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023026 + #define FLATBUFFERS_NOEXCEPT noexcept +#else + #define FLATBUFFERS_NOEXCEPT +#endif + +// NOTE: the FLATBUFFERS_DELETE_FUNC macro may change the access mode to +// private, so be sure to put it at the end or reset access mode explicitly. +#if (!defined(_MSC_VER) || _MSC_FULL_VER >= 180020827) && \ + (!defined(__GNUC__) || (__GNUC__ * 100 + __GNUC_MINOR__ >= 404)) + #define FLATBUFFERS_DELETE_FUNC(func) func = delete; +#else + #define FLATBUFFERS_DELETE_FUNC(func) private: func; +#endif + +#if defined(_MSC_VER) + #pragma warning(push) + #pragma warning(disable: 4127) // C4127: conditional expression is constant +#endif + +#ifndef FLATBUFFERS_HAS_STRING_VIEW + // Only provide flatbuffers::string_view if __has_include can be used + // to detect a header that provides an implementation + #if defined(__has_include) + // Check for std::string_view (in c++17) + #if __has_include() && (__cplusplus > 201402) + #include + namespace flatbuffers { + typedef std::string_view string_view; + } + #define FLATBUFFERS_HAS_STRING_VIEW 1 + // Check for std::experimental::string_view (in c++14, compiler-dependent) + #elif __has_include() && (__cplusplus > 201103) + #include + namespace flatbuffers { + typedef std::experimental::string_view string_view; + } + #define FLATBUFFERS_HAS_STRING_VIEW 1 + #endif + #endif // __has_include +#endif // !FLATBUFFERS_HAS_STRING_VIEW + +/// @endcond + +/// @file +namespace flatbuffers { + +/// @cond FLATBUFFERS_INTERNAL +// Our default offset / size type, 32bit on purpose on 64bit systems. +// Also, using a consistent offset type maintains compatibility of serialized +// offset values between 32bit and 64bit systems. +typedef uint32_t uoffset_t; + +// Signed offsets for references that can go in both directions. +typedef int32_t soffset_t; + +// Offset/index used in v-tables, can be changed to uint8_t in +// format forks to save a bit of space if desired. +typedef uint16_t voffset_t; + +typedef uintmax_t largest_scalar_t; + +// In 32bits, this evaluates to 2GB - 1 +#define FLATBUFFERS_MAX_BUFFER_SIZE ((1ULL << (sizeof(soffset_t) * 8 - 1)) - 1) + +// We support aligning the contents of buffers up to this size. +#define FLATBUFFERS_MAX_ALIGNMENT 16 + +template T EndianSwap(T t) { + #if defined(_MSC_VER) + #define FLATBUFFERS_BYTESWAP16 _byteswap_ushort + #define FLATBUFFERS_BYTESWAP32 _byteswap_ulong + #define FLATBUFFERS_BYTESWAP64 _byteswap_uint64 + #else + #if defined(__GNUC__) && __GNUC__ * 100 + __GNUC_MINOR__ < 408 && !defined(__clang__) + // __builtin_bswap16 was missing prior to GCC 4.8. + #define FLATBUFFERS_BYTESWAP16(x) \ + static_cast(__builtin_bswap32(static_cast(x) << 16)) + #else + #define FLATBUFFERS_BYTESWAP16 __builtin_bswap16 + #endif + #define FLATBUFFERS_BYTESWAP32 __builtin_bswap32 + #define FLATBUFFERS_BYTESWAP64 __builtin_bswap64 + #endif + if (sizeof(T) == 1) { // Compile-time if-then's. + return t; + } else if (sizeof(T) == 2) { + union { T t; uint16_t i; } u; + u.t = t; + u.i = FLATBUFFERS_BYTESWAP16(u.i); + return u.t; + } else if (sizeof(T) == 4) { + union { T t; uint32_t i; } u; + u.t = t; + u.i = FLATBUFFERS_BYTESWAP32(u.i); + return u.t; + } else if (sizeof(T) == 8) { + union { T t; uint64_t i; } u; + u.t = t; + u.i = FLATBUFFERS_BYTESWAP64(u.i); + return u.t; + } else { + FLATBUFFERS_ASSERT(0); + } +} + + +template T EndianScalar(T t) { + #if FLATBUFFERS_LITTLEENDIAN + return t; + #else + return EndianSwap(t); + #endif +} + +template T ReadScalar(const void *p) { + return EndianScalar(*reinterpret_cast(p)); +} + +template void WriteScalar(void *p, T t) { + *reinterpret_cast(p) = EndianScalar(t); +} + +// Computes how many bytes you'd have to pad to be able to write an +// "scalar_size" scalar if the buffer had grown to "buf_size" (downwards in +// memory). +inline size_t PaddingBytes(size_t buf_size, size_t scalar_size) { + return ((~buf_size) + 1) & (scalar_size - 1); +} + +} // namespace flatbuffers +#endif // FLATBUFFERS_BASE_H_ diff --git a/3rdparty/flatbuffers/readme.md b/3rdparty/flatbuffers/readme.md new file mode 100644 index 00000000000000..a7c0e93fd35c71 --- /dev/null +++ b/3rdparty/flatbuffers/readme.md @@ -0,0 +1,59 @@ +![logo](http://google.github.io/flatbuffers/fpl_logo_small.png) FlatBuffers +=========== + +[![Join the chat at https://gitter.im/google/flatbuffers](https://badges.gitter.im/google/flatbuffers.svg)](https://gitter.im/google/flatbuffers?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Build Status](https://travis-ci.org/google/flatbuffers.svg?branch=master)](https://travis-ci.org/google/flatbuffers) [![Build status](https://ci.appveyor.com/api/projects/status/yg5idd2fnusv1n10?svg=true)](https://ci.appveyor.com/project/gwvo/flatbuffers) + +**FlatBuffers** is an efficient cross platform serialization library for games and +other memory constrained apps. It allows you to directly access serialized data without +unpacking/parsing it first, while still having great forwards/backwards compatibility. + +**Go to our [landing page][] to browse our documentation.** + +## Supported operating systems +* Android +* Windows +* MacOS X +* Linux + +## Supported programming languages +* C++ +* C# +* C +* Go +* Java +* JavaScript +* PHP +* Python + +*and many more in progress...* + +## Contribution +* [FlatBuffers Google Group][] to discuss FlatBuffers with other developers and users. +* [FlatBuffers Issues Tracker][] to submit an issue. +* [stackoverflow.com][] with [`flatbuffers` tag][] for any questions regarding FlatBuffers. + +*To contribute to this project,* see [CONTRIBUTING][]. + +## Integration +For applications on Google Play that integrate this tool, usage is tracked. +This tracking is done automatically using the embedded version string +(**`flatbuffer_version_string`**), and helps us continue to optimize it. Aside from +consuming a few extra bytes in your application binary, it shouldn't affect +your application at all. We use this information to let us know if FlatBuffers +is useful and if we should continue to invest in it. Since this is open +source, you are free to remove the version string but we would appreciate if +you would leave it in. + +## Licensing +*Flatbuffers* is licensed under the Apache License, Version 2.0. See [LICENSE][] for the full license text. + +
+ + [CONTRIBUTING]: http://github.com/google/flatbuffers/blob/master/CONTRIBUTING.md + [`flatbuffers` tag]: https://stackoverflow.com/questions/tagged/flatbuffers + [FlatBuffers Google Group]: https://groups.google.com/forum/#!forum/flatbuffers + [FlatBuffers Issues Tracker]: http://github.com/google/flatbuffers/issues + [stackoverflow.com]: http://stackoverflow.com/search?q=flatbuffers + [landing page]: http://google.github.io/flatbuffers + [LICENSE]: https://github.com/google/flatbuffers/blob/master/LICENSE.txt diff --git a/3rdparty/flatbuffers/stl_emulation.h b/3rdparty/flatbuffers/stl_emulation.h new file mode 100644 index 00000000000000..7e7e978a450f1a --- /dev/null +++ b/3rdparty/flatbuffers/stl_emulation.h @@ -0,0 +1,228 @@ +/* + * Copyright 2017 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLATBUFFERS_STL_EMULATION_H_ +#define FLATBUFFERS_STL_EMULATION_H_ + +// clang-format off + +#include +#include +#include +#include +#include + +#if defined(_STLPORT_VERSION) && !defined(FLATBUFFERS_CPP98_STL) + #define FLATBUFFERS_CPP98_STL +#endif // defined(_STLPORT_VERSION) && !defined(FLATBUFFERS_CPP98_STL) + +#if defined(FLATBUFFERS_CPP98_STL) + #include +#endif // defined(FLATBUFFERS_CPP98_STL) + +// This header provides backwards compatibility for C++98 STLs like stlport. +namespace flatbuffers { + +// Retrieve ::back() from a string in a way that is compatible with pre C++11 +// STLs (e.g stlport). +inline char& string_back(std::string &value) { + return value[value.length() - 1]; +} + +inline char string_back(const std::string &value) { + return value[value.length() - 1]; +} + +// Helper method that retrieves ::data() from a vector in a way that is +// compatible with pre C++11 STLs (e.g stlport). +template inline T *vector_data(std::vector &vector) { + // In some debug environments, operator[] does bounds checking, so &vector[0] + // can't be used. + return vector.empty() ? nullptr : &vector[0]; +} + +template inline const T *vector_data( + const std::vector &vector) { + return vector.empty() ? nullptr : &vector[0]; +} + +template +inline void vector_emplace_back(std::vector *vector, V &&data) { + #if defined(FLATBUFFERS_CPP98_STL) + vector->push_back(data); + #else + vector->emplace_back(std::forward(data)); + #endif // defined(FLATBUFFERS_CPP98_STL) +} + +#ifndef FLATBUFFERS_CPP98_STL + #if !(defined(_MSC_VER) && _MSC_VER <= 1700 /* MSVC2012 */) + template + using numeric_limits = std::numeric_limits; + #else + template class numeric_limits : + public std::numeric_limits {}; + #endif // !(defined(_MSC_VER) && _MSC_VER <= 1700 /* MSVC2012 */) +#else + template class numeric_limits : + public std::numeric_limits {}; + + template <> class numeric_limits { + public: + static unsigned long long min() { return 0ULL; } + static unsigned long long max() { return ~0ULL; } + }; + + template <> class numeric_limits { + public: + static long long min() { + return static_cast(1ULL << ((sizeof(long long) << 3) - 1)); + } + static long long max() { + return static_cast( + (1ULL << ((sizeof(long long) << 3) - 1)) - 1); + } + }; +#endif // FLATBUFFERS_CPP98_STL + +#if !(defined(_MSC_VER) && _MSC_VER <= 1700 /* MSVC2012 */) + #ifndef FLATBUFFERS_CPP98_STL + template using is_scalar = std::is_scalar; + template using is_same = std::is_same; + template using is_floating_point = std::is_floating_point; + template using is_unsigned = std::is_unsigned; + #else + // Map C++ TR1 templates defined by stlport. + template using is_scalar = std::tr1::is_scalar; + template using is_same = std::tr1::is_same; + template using is_floating_point = + std::tr1::is_floating_point; + template using is_unsigned = std::tr1::is_unsigned; + #endif // !FLATBUFFERS_CPP98_STL +#else + // MSVC 2010 doesn't support C++11 aliases. + template struct is_scalar : public std::is_scalar {}; + template struct is_same : public std::is_same {}; + template struct is_floating_point : + public std::is_floating_point {}; + template struct is_unsigned : public std::is_unsigned {}; +#endif // !(defined(_MSC_VER) && _MSC_VER <= 1700 /* MSVC2012 */) + +#ifndef FLATBUFFERS_CPP98_STL + #if !(defined(_MSC_VER) && _MSC_VER <= 1700 /* MSVC2012 */) + template using unique_ptr = std::unique_ptr; + #else + // MSVC 2010 doesn't support C++11 aliases. + // We're manually "aliasing" the class here as we want to bring unique_ptr + // into the flatbuffers namespace. We have unique_ptr in the flatbuffers + // namespace we have a completely independent implemenation (see below) + // for C++98 STL implementations. + template class unique_ptr : public std::unique_ptr { + public: + unique_ptr() {} + explicit unique_ptr(T* p) : std::unique_ptr(p) {} + unique_ptr(std::unique_ptr&& u) { *this = std::move(u); } + unique_ptr(unique_ptr&& u) { *this = std::move(u); } + unique_ptr& operator=(std::unique_ptr&& u) { + std::unique_ptr::reset(u.release()); + return *this; + } + unique_ptr& operator=(unique_ptr&& u) { + std::unique_ptr::reset(u.release()); + return *this; + } + unique_ptr& operator=(T* p) { + return std::unique_ptr::operator=(p); + } + }; + #endif // !(defined(_MSC_VER) && _MSC_VER <= 1700 /* MSVC2012 */) +#else + // Very limited implementation of unique_ptr. + // This is provided simply to allow the C++ code generated from the default + // settings to function in C++98 environments with no modifications. + template class unique_ptr { + public: + typedef T element_type; + + unique_ptr() : ptr_(nullptr) {} + explicit unique_ptr(T* p) : ptr_(p) {} + unique_ptr(unique_ptr&& u) : ptr_(nullptr) { reset(u.release()); } + unique_ptr(const unique_ptr& u) : ptr_(nullptr) { + reset(const_cast(&u)->release()); + } + ~unique_ptr() { reset(); } + + unique_ptr& operator=(const unique_ptr& u) { + reset(const_cast(&u)->release()); + return *this; + } + + unique_ptr& operator=(unique_ptr&& u) { + reset(u.release()); + return *this; + } + + unique_ptr& operator=(T* p) { + reset(p); + return *this; + } + + const T& operator*() const { return *ptr_; } + T* operator->() const { return ptr_; } + T* get() const noexcept { return ptr_; } + explicit operator bool() const { return ptr_ != nullptr; } + + // modifiers + T* release() { + T* value = ptr_; + ptr_ = nullptr; + return value; + } + + void reset(T* p = nullptr) { + T* value = ptr_; + ptr_ = p; + if (value) delete value; + } + + void swap(unique_ptr& u) { + T* temp_ptr = ptr_; + ptr_ = u.ptr_; + u.ptr_ = temp_ptr; + } + + private: + T* ptr_; + }; + + template bool operator==(const unique_ptr& x, + const unique_ptr& y) { + return x.get() == y.get(); + } + + template bool operator==(const unique_ptr& x, + const D* y) { + return static_cast(x.get()) == y; + } + + template bool operator==(const unique_ptr& x, intptr_t y) { + return reinterpret_cast(x.get()) == y; + } +#endif // !FLATBUFFERS_CPP98_STL + +} // namespace flatbuffers + +#endif // FLATBUFFERS_STL_EMULATION_H_ diff --git a/3rdparty/flatbuffers/util.h b/3rdparty/flatbuffers/util.h new file mode 100644 index 00000000000000..cf2949a2074142 --- /dev/null +++ b/3rdparty/flatbuffers/util.h @@ -0,0 +1,515 @@ +/* + * Copyright 2014 Google Inc. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FLATBUFFERS_UTIL_H_ +#define FLATBUFFERS_UTIL_H_ + +#include +#include +#include +#include +#include +#ifndef FLATBUFFERS_PREFER_PRINTF +# include +#else // FLATBUFFERS_PREFER_PRINTF +# include +# include +#endif // FLATBUFFERS_PREFER_PRINTF +#include +#ifdef _WIN32 +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# ifndef NOMINMAX +# define NOMINMAX +# endif +# include // Must be included before +# include +# include +# undef interface // This is also important because of reasons +#else +# include +#endif +#include +#include + +#include "flatbuffers/base.h" + +namespace flatbuffers { + +#ifdef FLATBUFFERS_PREFER_PRINTF +template size_t IntToDigitCount(T t) { + size_t digit_count = 0; + // Count the sign for negative numbers + if (t < 0) digit_count++; + // Count a single 0 left of the dot for fractional numbers + if (-1 < t && t < 1) digit_count++; + // Count digits until fractional part + T eps = std::numeric_limits::epsilon(); + while (t <= (-1 + eps) || (1 - eps) <= t) { + t /= 10; + digit_count++; + } + return digit_count; +} + +template size_t NumToStringWidth(T t, int precision = 0) { + size_t string_width = IntToDigitCount(t); + // Count the dot for floating point numbers + if (precision) string_width += (precision + 1); + return string_width; +} + +template std::string NumToStringImplWrapper(T t, const char* fmt, + int precision = 0) { + size_t string_width = NumToStringWidth(t, precision); + std::string s(string_width, 0x00); + // Allow snprintf to use std::string trailing null to detect buffer overflow + snprintf(const_cast(s.data()), (s.size()+1), fmt, precision, t); + return s; +} +#endif // FLATBUFFERS_PREFER_PRINTF + +// Convert an integer or floating point value to a string. +// In contrast to std::stringstream, "char" values are +// converted to a string of digits, and we don't use scientific notation. +template std::string NumToString(T t) { + // clang-format off + #ifndef FLATBUFFERS_PREFER_PRINTF + std::stringstream ss; + ss << t; + return ss.str(); + #else // FLATBUFFERS_PREFER_PRINTF + auto v = static_cast(t); + return NumToStringImplWrapper(v, "%.*lld"); + #endif // FLATBUFFERS_PREFER_PRINTF + // clang-format on +} +// Avoid char types used as character data. +template<> inline std::string NumToString(signed char t) { + return NumToString(static_cast(t)); +} +template<> inline std::string NumToString(unsigned char t) { + return NumToString(static_cast(t)); +} +#if defined(FLATBUFFERS_CPP98_STL) +template<> inline std::string NumToString(long long t) { + char buf[21]; // (log((1 << 63) - 1) / log(10)) + 2 + snprintf(buf, sizeof(buf), "%lld", t); + return std::string(buf); +} + +template<> +inline std::string NumToString(unsigned long long t) { + char buf[22]; // (log((1 << 63) - 1) / log(10)) + 1 + snprintf(buf, sizeof(buf), "%llu", t); + return std::string(buf); +} +#endif // defined(FLATBUFFERS_CPP98_STL) + +// Special versions for floats/doubles. +template std::string FloatToString(T t, int precision) { + // clang-format off + #ifndef FLATBUFFERS_PREFER_PRINTF + // to_string() prints different numbers of digits for floats depending on + // platform and isn't available on Android, so we use stringstream + std::stringstream ss; + // Use std::fixed to suppress scientific notation. + ss << std::fixed; + // Default precision is 6, we want that to be higher for doubles. + ss << std::setprecision(precision); + ss << t; + auto s = ss.str(); + #else // FLATBUFFERS_PREFER_PRINTF + auto v = static_cast(t); + auto s = NumToStringImplWrapper(v, "%0.*f", precision); + #endif // FLATBUFFERS_PREFER_PRINTF + // clang-format on + // Sadly, std::fixed turns "1" into "1.00000", so here we undo that. + auto p = s.find_last_not_of('0'); + if (p != std::string::npos) { + // Strip trailing zeroes. If it is a whole number, keep one zero. + s.resize(p + (s[p] == '.' ? 2 : 1)); + } + return s; +} + +template<> inline std::string NumToString(double t) { + return FloatToString(t, 12); +} +template<> inline std::string NumToString(float t) { + return FloatToString(t, 6); +} + +// Convert an integer value to a hexadecimal string. +// The returned string length is always xdigits long, prefixed by 0 digits. +// For example, IntToStringHex(0x23, 8) returns the string "00000023". +inline std::string IntToStringHex(int i, int xdigits) { + // clang-format off + #ifndef FLATBUFFERS_PREFER_PRINTF + std::stringstream ss; + ss << std::setw(xdigits) << std::setfill('0') << std::hex << std::uppercase + << i; + return ss.str(); + #else // FLATBUFFERS_PREFER_PRINTF + return NumToStringImplWrapper(i, "%.*X", xdigits); + #endif // FLATBUFFERS_PREFER_PRINTF + // clang-format on +} + +// Portable implementation of strtoll(). +inline int64_t StringToInt(const char *str, char **endptr = nullptr, + int base = 10) { + // clang-format off + #ifdef _MSC_VER + return _strtoi64(str, endptr, base); + #else + return strtoll(str, endptr, base); + #endif + // clang-format on +} + +// Portable implementation of strtoull(). +inline uint64_t StringToUInt(const char *str, char **endptr = nullptr, + int base = 10) { + // clang-format off + #ifdef _MSC_VER + return _strtoui64(str, endptr, base); + #else + return strtoull(str, endptr, base); + #endif + // clang-format on +} + +typedef bool (*LoadFileFunction)(const char *filename, bool binary, + std::string *dest); +typedef bool (*FileExistsFunction)(const char *filename); + +LoadFileFunction SetLoadFileFunction(LoadFileFunction load_file_function); + +FileExistsFunction SetFileExistsFunction( + FileExistsFunction file_exists_function); + +// Check if file "name" exists. +bool FileExists(const char *name); + +// Check if "name" exists and it is also a directory. +bool DirExists(const char *name); + +// Load file "name" into "buf" returning true if successful +// false otherwise. If "binary" is false data is read +// using ifstream's text mode, otherwise data is read with +// no transcoding. +bool LoadFile(const char *name, bool binary, std::string *buf); + +// Save data "buf" of length "len" bytes into a file +// "name" returning true if successful, false otherwise. +// If "binary" is false data is written using ifstream's +// text mode, otherwise data is written with no +// transcoding. +inline bool SaveFile(const char *name, const char *buf, size_t len, + bool binary) { + std::ofstream ofs(name, binary ? std::ofstream::binary : std::ofstream::out); + if (!ofs.is_open()) return false; + ofs.write(buf, len); + return !ofs.bad(); +} + +// Save data "buf" into file "name" returning true if +// successful, false otherwise. If "binary" is false +// data is written using ifstream's text mode, otherwise +// data is written with no transcoding. +inline bool SaveFile(const char *name, const std::string &buf, bool binary) { + return SaveFile(name, buf.c_str(), buf.size(), binary); +} + +// Functionality for minimalistic portable path handling. + +// The functions below behave correctly regardless of whether posix ('/') or +// Windows ('/' or '\\') separators are used. + +// Any new separators inserted are always posix. + +// We internally store paths in posix format ('/'). Paths supplied +// by the user should go through PosixPath to ensure correct behavior +// on Windows when paths are string-compared. + +static const char kPathSeparator = '/'; +static const char kPathSeparatorWindows = '\\'; +static const char *PathSeparatorSet = "\\/"; // Intentionally no ':' + +// Returns the path with the extension, if any, removed. +inline std::string StripExtension(const std::string &filepath) { + size_t i = filepath.find_last_of("."); + return i != std::string::npos ? filepath.substr(0, i) : filepath; +} + +// Returns the extension, if any. +inline std::string GetExtension(const std::string &filepath) { + size_t i = filepath.find_last_of("."); + return i != std::string::npos ? filepath.substr(i + 1) : ""; +} + +// Return the last component of the path, after the last separator. +inline std::string StripPath(const std::string &filepath) { + size_t i = filepath.find_last_of(PathSeparatorSet); + return i != std::string::npos ? filepath.substr(i + 1) : filepath; +} + +// Strip the last component of the path + separator. +inline std::string StripFileName(const std::string &filepath) { + size_t i = filepath.find_last_of(PathSeparatorSet); + return i != std::string::npos ? filepath.substr(0, i) : ""; +} + +// Concatenates a path with a filename, regardless of wether the path +// ends in a separator or not. +inline std::string ConCatPathFileName(const std::string &path, + const std::string &filename) { + std::string filepath = path; + if (filepath.length()) { + char &filepath_last_character = string_back(filepath); + if (filepath_last_character == kPathSeparatorWindows) { + filepath_last_character = kPathSeparator; + } else if (filepath_last_character != kPathSeparator) { + filepath += kPathSeparator; + } + } + filepath += filename; + // Ignore './' at the start of filepath. + if (filepath[0] == '.' && filepath[1] == kPathSeparator) { + filepath.erase(0, 2); + } + return filepath; +} + +// Replaces any '\\' separators with '/' +inline std::string PosixPath(const char *path) { + std::string p = path; + std::replace(p.begin(), p.end(), '\\', '/'); + return p; +} + +// This function ensure a directory exists, by recursively +// creating dirs for any parts of the path that don't exist yet. +inline void EnsureDirExists(const std::string &filepath) { + auto parent = StripFileName(filepath); + if (parent.length()) EnsureDirExists(parent); + // clang-format off + #ifdef _WIN32 + (void)_mkdir(filepath.c_str()); + #else + mkdir(filepath.c_str(), S_IRWXU|S_IRGRP|S_IXGRP); + #endif + // clang-format on +} + +// Obtains the absolute path from any other path. +// Returns the input path if the absolute path couldn't be resolved. +inline std::string AbsolutePath(const std::string &filepath) { + // clang-format off + #ifdef FLATBUFFERS_NO_ABSOLUTE_PATH_RESOLUTION + return filepath; + #else + #ifdef _WIN32 + char abs_path[MAX_PATH]; + return GetFullPathNameA(filepath.c_str(), MAX_PATH, abs_path, nullptr) + #else + char abs_path[PATH_MAX]; + return realpath(filepath.c_str(), abs_path) + #endif + ? abs_path + : filepath; + #endif // FLATBUFFERS_NO_ABSOLUTE_PATH_RESOLUTION + // clang-format on +} + +// To and from UTF-8 unicode conversion functions + +// Convert a unicode code point into a UTF-8 representation by appending it +// to a string. Returns the number of bytes generated. +inline int ToUTF8(uint32_t ucc, std::string *out) { + FLATBUFFERS_ASSERT(!(ucc & 0x80000000)); // Top bit can't be set. + // 6 possible encodings: http://en.wikipedia.org/wiki/UTF-8 + for (int i = 0; i < 6; i++) { + // Max bits this encoding can represent. + uint32_t max_bits = 6 + i * 5 + static_cast(!i); + if (ucc < (1u << max_bits)) { // does it fit? + // Remaining bits not encoded in the first byte, store 6 bits each + uint32_t remain_bits = i * 6; + // Store first byte: + (*out) += static_cast((0xFE << (max_bits - remain_bits)) | + (ucc >> remain_bits)); + // Store remaining bytes: + for (int j = i - 1; j >= 0; j--) { + (*out) += static_cast(((ucc >> (j * 6)) & 0x3F) | 0x80); + } + return i + 1; // Return the number of bytes added. + } + } + FLATBUFFERS_ASSERT(0); // Impossible to arrive here. + return -1; +} + +// Converts whatever prefix of the incoming string corresponds to a valid +// UTF-8 sequence into a unicode code. The incoming pointer will have been +// advanced past all bytes parsed. +// returns -1 upon corrupt UTF-8 encoding (ignore the incoming pointer in +// this case). +inline int FromUTF8(const char **in) { + int len = 0; + // Count leading 1 bits. + for (int mask = 0x80; mask >= 0x04; mask >>= 1) { + if (**in & mask) { + len++; + } else { + break; + } + } + if ((static_cast(**in) << len) & 0x80) return -1; // Bit after leading 1's must be 0. + if (!len) return *(*in)++; + // UTF-8 encoded values with a length are between 2 and 4 bytes. + if (len < 2 || len > 4) { return -1; } + // Grab initial bits of the code. + int ucc = *(*in)++ & ((1 << (7 - len)) - 1); + for (int i = 0; i < len - 1; i++) { + if ((**in & 0xC0) != 0x80) return -1; // Upper bits must 1 0. + ucc <<= 6; + ucc |= *(*in)++ & 0x3F; // Grab 6 more bits of the code. + } + // UTF-8 cannot encode values between 0xD800 and 0xDFFF (reserved for + // UTF-16 surrogate pairs). + if (ucc >= 0xD800 && ucc <= 0xDFFF) { return -1; } + // UTF-8 must represent code points in their shortest possible encoding. + switch (len) { + case 2: + // Two bytes of UTF-8 can represent code points from U+0080 to U+07FF. + if (ucc < 0x0080 || ucc > 0x07FF) { return -1; } + break; + case 3: + // Three bytes of UTF-8 can represent code points from U+0800 to U+FFFF. + if (ucc < 0x0800 || ucc > 0xFFFF) { return -1; } + break; + case 4: + // Four bytes of UTF-8 can represent code points from U+10000 to U+10FFFF. + if (ucc < 0x10000 || ucc > 0x10FFFF) { return -1; } + break; + } + return ucc; +} + +#ifndef FLATBUFFERS_PREFER_PRINTF +// Wraps a string to a maximum length, inserting new lines where necessary. Any +// existing whitespace will be collapsed down to a single space. A prefix or +// suffix can be provided, which will be inserted before or after a wrapped +// line, respectively. +inline std::string WordWrap(const std::string in, size_t max_length, + const std::string wrapped_line_prefix, + const std::string wrapped_line_suffix) { + std::istringstream in_stream(in); + std::string wrapped, line, word; + + in_stream >> word; + line = word; + + while (in_stream >> word) { + if ((line.length() + 1 + word.length() + wrapped_line_suffix.length()) < + max_length) { + line += " " + word; + } else { + wrapped += line + wrapped_line_suffix + "\n"; + line = wrapped_line_prefix + word; + } + } + wrapped += line; + + return wrapped; +} +#endif // !FLATBUFFERS_PREFER_PRINTF + +inline bool EscapeString(const char *s, size_t length, std::string *_text, + bool allow_non_utf8, bool natural_utf8) { + std::string &text = *_text; + text += "\""; + for (uoffset_t i = 0; i < length; i++) { + char c = s[i]; + switch (c) { + case '\n': text += "\\n"; break; + case '\t': text += "\\t"; break; + case '\r': text += "\\r"; break; + case '\b': text += "\\b"; break; + case '\f': text += "\\f"; break; + case '\"': text += "\\\""; break; + case '\\': text += "\\\\"; break; + default: + if (c >= ' ' && c <= '~') { + text += c; + } else { + // Not printable ASCII data. Let's see if it's valid UTF-8 first: + const char *utf8 = s + i; + int ucc = FromUTF8(&utf8); + if (ucc < 0) { + if (allow_non_utf8) { + text += "\\x"; + text += IntToStringHex(static_cast(c), 2); + } else { + // There are two cases here: + // + // 1) We reached here by parsing an IDL file. In that case, + // we previously checked for non-UTF-8, so we shouldn't reach + // here. + // + // 2) We reached here by someone calling GenerateText() + // on a previously-serialized flatbuffer. The data might have + // non-UTF-8 Strings, or might be corrupt. + // + // In both cases, we have to give up and inform the caller + // they have no JSON. + return false; + } + } else { + if (natural_utf8) { + // utf8 points to past all utf-8 bytes parsed + text.append(s + i, static_cast(utf8 - s - i)); + } else if (ucc <= 0xFFFF) { + // Parses as Unicode within JSON's \uXXXX range, so use that. + text += "\\u"; + text += IntToStringHex(ucc, 4); + } else if (ucc <= 0x10FFFF) { + // Encode Unicode SMP values to a surrogate pair using two \u + // escapes. + uint32_t base = ucc - 0x10000; + auto high_surrogate = (base >> 10) + 0xD800; + auto low_surrogate = (base & 0x03FF) + 0xDC00; + text += "\\u"; + text += IntToStringHex(high_surrogate, 4); + text += "\\u"; + text += IntToStringHex(low_surrogate, 4); + } + // Skip past characters recognized. + i = static_cast(utf8 - s - 1); + } + } + break; + } + } + text += "\""; + return true; +} + +} // namespace flatbuffers + +#endif // FLATBUFFERS_UTIL_H_ diff --git a/3rdparty/minitrace/LICENSE b/3rdparty/minitrace/LICENSE new file mode 100644 index 00000000000000..f7469311e1e79c --- /dev/null +++ b/3rdparty/minitrace/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Henrik Rydgård + +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 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. \ No newline at end of file diff --git a/3rdparty/minitrace/README.md b/3rdparty/minitrace/README.md new file mode 100644 index 00000000000000..70e7179efca88a --- /dev/null +++ b/3rdparty/minitrace/README.md @@ -0,0 +1,103 @@ +minitrace +========= +by Henrik Rydgård 2014 (hrydgard+minitrace@gmail.com) + +MIT licensed, feel free to use however you want. If you use it for something cool, I'd love to hear about it! + +This is a C library with C++ helpers for producing JSON traces suitable for Chrome's excellent built-in trace viewer (chrome://tracing). + +Extremely simple to build and use. Tested on Mac and Windows, but should compile anywhere you can use ANSI C with few or no changes. + +Sample output (see example code below): + +![minitrace](http://www.ppsspp.org/img/minitrace.png) + +Remember to be careful when interpreting the output. This is not a sampling profiler, so it only records start and stop times for blocks. This means that blocks grow even when the CPU is off running another thread, and that it can look like work is being done on more blocks at a time than you have CPUs. + + +How to use +---------- + + 1. Include minitrace.c and minitrace.h in your project. #include minitrace.h in some common header. + + 2. In your initialization code: + + mtr_init("trace.json"); + + 3. In your exit code: + + mtr_shutdown(); + + 4. In all functions you want to profile: + + // C + MTR_BEGIN("GFX", "RasterizeTriangle") + ... + MTR_END("GFX", "RasterizeTriangle") + + // C++ + MTR_SCOPE("GFX", "RasterizeTriangle") + + 5. In Google Chrome open "about:tracing" + + 6. Click Open, and choose your trace.json + + 7. Navigate the trace view using the WASD keys, and Look for bottlenecks and optimize your application. + + 8. In your final release build, build with + + -DMTR_DISABLE + + +By default, it will collect 1 million tracepoints and then stop. You can change this behaviour, see the +top of the header file. + +Note: Please only use string literals in MTR statements. + +Example code +------------ + + int main(int argc, const char *argv[]) { + int i; + mtr_init("trace.json"); + + MTR_META_PROCESS_NAME("minitrace_test"); + MTR_META_THREAD_NAME("main thread"); + + int long_running_thing_1; + int long_running_thing_2; + + MTR_START("background", "long_running", &long_running_thing_1); + MTR_START("background", "long_running", &long_running_thing_2); + + MTR_BEGIN("main", "outer"); + usleep(80000); + for (i = 0; i < 3; i++) { + MTR_BEGIN("main", "inner"); + usleep(40000); + MTR_END("main", "inner"); + usleep(10000); + } + MTR_STEP("background", "long_running", &long_running_thing_1, "middle step"); + usleep(80000); + MTR_END("main", "outer"); + + usleep(50000); + MTR_INSTANT("main", "the end"); + usleep(10000); + MTR_FINISH("background", "long_running", &long_running_thing_1); + MTR_FINISH("background", "long_running", &long_running_thing_2); + + mtr_flush(); + mtr_shutdown(); + return 0; + } + +The output will result in something looking a little like the picture at the top of this readme. + +Future plans: + + * Builtin background flush thread support with better synchronization, no more fixed limit + * Support for more trace arguments, more tracing types + +If you use this, feel free to tell me how, and what issues you may have had. hrydgard+minitrace@gmail.com diff --git a/3rdparty/minitrace/minitrace.cpp b/3rdparty/minitrace/minitrace.cpp new file mode 100644 index 00000000000000..d3c832207e7578 --- /dev/null +++ b/3rdparty/minitrace/minitrace.cpp @@ -0,0 +1,379 @@ +// minitrace +// Copyright 2014 by Henrik Rydgård +// http://www.github.com/hrydgard/minitrace +// Released under the MIT license. + +// See minitrace.h for basic documentation. + +#include +#include +#include + +#ifdef _WIN32 +#pragma warning (disable:4996) +#define WIN32_LEAN_AND_MEAN +#include +#define __thread __declspec(thread) +#define pthread_mutex_t CRITICAL_SECTION +#define pthread_mutex_init(a, b) InitializeCriticalSection(a) +#define pthread_mutex_lock(a) EnterCriticalSection(a) +#define pthread_mutex_unlock(a) LeaveCriticalSection(a) +#define pthread_mutex_destroy(a) DeleteCriticalSection(a) +#else +#include +#include +#include +#include +#endif + +#include "minitrace.h" + +#define ARRAY_SIZE(x) sizeof(x)/sizeof(x[0]) + +namespace minitrace { + +// Ugh, this struct is already pretty heavy. +// Will probably need to move arguments to a second buffer to support more than one. +typedef struct raw_event { + const char *name; + const char *cat; + void *id; + int64_t ts; + uint32_t pid; + uint32_t tid; + char ph; + mtr_arg_type arg_type; + const char *arg_name; + union { + const char *a_str; + int a_int; + double a_double; + }; +} raw_event_t; + +static raw_event_t *buffer; +static volatile int count; +static int is_tracing = 0; +static int64_t time_offset; +static int first_line = 1; +static FILE *file; +static __thread int cur_thread_id; // Thread local storage +static pthread_mutex_t mutex; + +#define STRING_POOL_SIZE 100 +static char *str_pool[100]; + +// Tiny portability layer. +// Exposes: +// get_cur_thread_id() +// mtr_time_s() +// pthread basics +#ifdef _WIN32 +static int get_cur_thread_id() { + return (int)GetCurrentThreadId(); +} + +static uint64_t _frequency = 0; +static uint64_t _starttime = 0; + +inline int64_t mtr_time_usec(){ + static int64_t prev = 0; + if (_frequency == 0) { + QueryPerformanceFrequency((LARGE_INTEGER*)&_frequency); + QueryPerformanceCounter((LARGE_INTEGER*)&_starttime); + } + __int64 time; + QueryPerformanceCounter((LARGE_INTEGER*)&time); + int64_t now = 1.0e6 * ((double) (time - _starttime) / (double) _frequency); + if( now <= prev) now = prev + 1; + prev = now; + return now; +} + +// Ctrl+C handling for Windows console apps +static BOOL WINAPI CtrlHandler(DWORD fdwCtrlType) { + if (is_tracing && fdwCtrlType == CTRL_C_EVENT) { + printf("Ctrl-C detected! Flushing trace and shutting down.\n\n"); + mtr_flush(); + mtr_shutdown(); + } + ExitProcess(1); +} + +void mtr_register_sigint_handler() { + // For console apps: + SetConsoleCtrlHandler(&CtrlHandler, TRUE); +} + +#else + +static inline int get_cur_thread_id() { + return (int)(intptr_t)pthread_self(); +} + +#if defined(BLACKBERRY) +inline int64_t mtr_time_usec(){ + static int64_t prev = 0; + struct timespec time; + clock_gettime(CLOCK_MONOTONIC, &time); // Linux must use CLOCK_MONOTONIC_RAW due to time warps + int64_t now = time.tv_sec*1000000 + time.tv_nsec / 1000; + if( now <= prev) now = prev + 1; + prev = now; + return now; +} +#else +int64_t mtr_time_usec() +{ + static int64_t prev = 0; + struct timeval tv; + gettimeofday(&tv, NULL); + int64_t now = 1000000*tv.tv_sec + tv.tv_usec; + if( now <= prev) now = prev + 1; + prev = now; + return now; +} +#endif // !BLACKBERRY + +static void termination_handler(int signum) { + if (is_tracing) { + printf("Ctrl-C detected! Flushing trace and shutting down.\n\n"); + mtr_flush(); + fwrite("\n]}\n", 1, 4, file); + fclose(file); + } + exit(1); +} + +void mtr_register_sigint_handler() { +#ifndef MTR_ENABLED + return; +#endif + // Avoid altering set-to-be-ignored handlers while registering. + if (signal(SIGINT, &termination_handler) == SIG_IGN) + signal(SIGINT, SIG_IGN); +} + +#endif + +void mtr_init(const char *json_file) { +#ifndef MTR_ENABLED + return; +#endif + buffer = (raw_event_t *)malloc(INTERNAL_MINITRACE_BUFFER_SIZE * sizeof(raw_event_t)); + is_tracing = 1; + count = 0; + file = fopen(json_file, "wb"); + const char *header = "{\"traceEvents\":[\n"; + fwrite(header, 1, strlen(header), file); + time_offset = mtr_time_usec(); + first_line = 1; + pthread_mutex_init(&mutex, 0); +} + +void mtr_shutdown() { + int i; +#ifndef MTR_ENABLED + return; +#endif + is_tracing = 0; + mtr_flush(); + fwrite("\n]}\n", 1, 4, file); + fclose(file); + pthread_mutex_destroy(&mutex); + file = 0; + free(buffer); + buffer = 0; + for (i = 0; i < STRING_POOL_SIZE; i++) { + if (str_pool[i]) { + free(str_pool[i]); + str_pool[i] = 0; + } + } +} + +const char *mtr_pool_string(const char *str) { + int i; + for (i = 0; i < STRING_POOL_SIZE; i++) { + if (!str_pool[i]) { + str_pool[i] = (char *)malloc(strlen(str) + 1); + strcpy(str_pool[i], str); + return str_pool[i]; + } else { + if (!strcmp(str, str_pool[i])) + return str_pool[i]; + } + } + return "string pool full"; +} + +void mtr_start() { +#ifndef MTR_ENABLED + return; +#endif + is_tracing = 1; +} + +void mtr_stop() { +#ifndef MTR_ENABLED + return; +#endif + is_tracing = 0; +} + +// TODO: fwrite more than one line at a time. +void mtr_flush() { +#ifndef MTR_ENABLED + return; +#endif + int i = 0; + char linebuf[1024]; + char arg_buf[256]; + char id_buf[256]; + // We have to lock while flushing. So we really should avoid flushing as much as possible. + + + pthread_mutex_lock(&mutex); + int old_tracing = is_tracing; + is_tracing = 0; // Stop logging even if using interlocked increments instead of the mutex. Can cause data loss. + + for (i = 0; i < count; i++) { + raw_event_t *raw = &buffer[i]; + int len; + switch (raw->arg_type) { + case MTR_ARG_TYPE_INT: + snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":%i", raw->arg_name, raw->a_int); + break; + case MTR_ARG_TYPE_STRING_CONST: + snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%s\"", raw->arg_name, raw->a_str); + break; + case MTR_ARG_TYPE_STRING_COPY: + if (strlen(raw->a_str) > 700) { + ((char*)raw->a_str)[700] = 0; + } + snprintf(arg_buf, ARRAY_SIZE(arg_buf), "\"%s\":\"%s\"", raw->arg_name, raw->a_str); + break; + case MTR_ARG_TYPE_NONE: + default: + arg_buf[0] = '\0'; + break; + } + if (raw->id) { + switch (raw->ph) { + case 'S': + case 'T': + case 'F': + // TODO: Support full 64-bit pointers + snprintf(id_buf, ARRAY_SIZE(id_buf), ",\"id\":\"0x%08x\"", (uint32_t)(uintptr_t)raw->id); + break; + case 'X': + snprintf(id_buf, ARRAY_SIZE(id_buf), ",\"dur\":%i", (int)raw->a_double); + break; + } + } else { + id_buf[0] = 0; + } + const char *cat = raw->cat; +#ifdef _WIN32 + // On Windows, we often end up with backslashes in category. + { + char temp[256]; + int len = (int)strlen(cat); + int i; + if (len > 255) len = 255; + for (i = 0; i < len; i++) { + temp[i] = cat[i] == '\\' ? '/' : cat[i]; + } + temp[len] = 0; + cat = temp; + } +#endif + + len = snprintf(linebuf, ARRAY_SIZE(linebuf), "%s{\"cat\":\"%s\",\"pid\":%i,\"tid\":%i,\"ts\":%" PRId64 ",\"ph\":\"%c\",\"name\":\"%s\",\"args\":{%s}%s}", + first_line ? "" : ",\n", + cat, raw->pid, raw->tid, raw->ts - time_offset, raw->ph, raw->name, arg_buf, id_buf); + fwrite(linebuf, 1, len, file); + fflush(file); + first_line = 0; + } + count = 0; + is_tracing = old_tracing; + pthread_mutex_unlock(&mutex); +} + +void internal_mtr_raw_event(const char *category, const char *name, char ph, void *id) { +#ifndef MTR_ENABLED + return; +#endif + if (!is_tracing || count >= INTERNAL_MINITRACE_BUFFER_SIZE) + return; + int64_t ts = mtr_time_usec(); + if (!cur_thread_id) { + cur_thread_id = get_cur_thread_id(); + } + +#if 0 && _WIN32 // TODO: This needs testing + int bufPos = InterlockedIncrement(&count); + raw_event_t *ev = &buffer[count - 1]; +#else + pthread_mutex_lock(&mutex); + raw_event_t *ev = &buffer[count]; + count++; + pthread_mutex_unlock(&mutex); +#endif + + ev->cat = category; + ev->name = name; + ev->id = id; + ev->ph = ph; + if (ev->ph == 'X') { + int64_t x; + memcpy(&x, id, sizeof(int64_t)); + ev->ts = x; + ev->a_double = (ts - x); + } else { + ev->ts = ts; + } + ev->tid = cur_thread_id; + ev->pid = 0; +} + +void internal_mtr_raw_event_arg(const char *category, const char *name, char ph, void *id, mtr_arg_type arg_type, const char *arg_name, void *arg_value) { +#ifndef MTR_ENABLED + return; +#endif + if (!is_tracing || count >= INTERNAL_MINITRACE_BUFFER_SIZE) + return; + if (!cur_thread_id) { + cur_thread_id = get_cur_thread_id(); + } + int64_t ts = mtr_time_usec(); + +#if 0 && _WIN32 // TODO: This needs testing + int bufPos = InterlockedIncrement(&count); + raw_event_t *ev = &buffer[count - 1]; +#else + pthread_mutex_lock(&mutex); + raw_event_t *ev = &buffer[count]; + count++; + pthread_mutex_unlock(&mutex); +#endif + + ev->cat = category; + ev->name = name; + ev->id = id; + ev->ts = ts; + ev->ph = ph; + ev->tid = cur_thread_id; + ev->pid = 0; + ev->arg_type = arg_type; + ev->arg_name = arg_name; + switch (arg_type) { + case MTR_ARG_TYPE_INT: ev->a_int = (int)(uintptr_t)arg_value; break; + case MTR_ARG_TYPE_STRING_CONST: ev->a_str = (const char*)arg_value; break; + case MTR_ARG_TYPE_STRING_COPY: ev->a_str = strdup((const char*)arg_value); break; + default: + break; + } +} + +} diff --git a/3rdparty/minitrace/minitrace.h b/3rdparty/minitrace/minitrace.h new file mode 100644 index 00000000000000..628b1611945440 --- /dev/null +++ b/3rdparty/minitrace/minitrace.h @@ -0,0 +1,267 @@ +#ifndef MINITRACE_H +#define MINITRACE_H + +// Minitrace +// +// Copyright 2014 by Henrik Rydgård +// http://www.github.com/hrydgard/minitrace +// Released under the MIT license. +// +// Ultra-light dependency free library for performance tracing C/C++ applications. +// Produces traces compatible with Google Chrome's trace viewer. +// Simply open "about:tracing" in Chrome and load the produced JSON. +// +// This contains far less template magic than the original libraries from Chrome +// because this is meant to be usable from C. +// +// See README.md for a tutorial. +// +// The trace format is documented here: +// https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/edit +// More: +// http://www.altdevblogaday.com/2012/08/21/using-chrometracing-to-view-your-inline-profiling-data/ + +#include + +#define MTR_ENABLED + +// If MTR_ENABLED is not defined, Minitrace does nothing and has near zero overhead. +// Preferably, set this flag in your build system. If you can't just uncomment this line. +// #define MTR_ENABLED + +// By default, will collect up to 1000000 events, then you must flush. +// It's recommended that you simply call mtr_flush on a background thread +// occasionally. It's safe...ish. +#define INTERNAL_MINITRACE_BUFFER_SIZE 1000000 + +//#define MTR_ENABLED + +namespace minitrace { + +// Initializes Minitrace. Must be called very early during startup of your executable, +// before any MTR_ statements.. +void mtr_init(const char *json_file); + +// Shuts down minitrace cleanly, flushing the trace buffer. +void mtr_shutdown(); + +// Lets you enable and disable Minitrace at runtime. +// May cause strange discontinuities in the output. +// Minitrace is enabled on startup by default. +void mtr_start(); +void mtr_stop(); + +// Flushes the collected data to disk, clearing the buffer for new data. +void mtr_flush(); + +// Returns the current time in seconds. Used internally by Minitrace. No caching. +int64_t mtr_time_usec(); + +// Registers a handler that will flush the trace on Ctrl+C. +// Works on Linux and MacOSX, and in Win32 console applications. +void mtr_register_sigint_handler(); + +// Utility function that should rarely be used. +// If str is semi dynamic, store it permanently in a small pool so we don't need to malloc it. +// The pool fills up fast though and performance isn't great. +// Returns a fixed string if the pool is full. +const char *mtr_pool_string(const char *str); + +// Commented-out types will be supported in the future. +typedef enum { + MTR_ARG_TYPE_NONE = 0, + MTR_ARG_TYPE_INT = 1, // I + // MTR_ARG_TYPE_FLOAT = 2, // TODO + // MTR_ARG_TYPE_DOUBLE = 3, // TODO + MTR_ARG_TYPE_STRING_CONST = 8, // C + MTR_ARG_TYPE_STRING_COPY = 9, + // MTR_ARG_TYPE_JSON_COPY = 10, +} mtr_arg_type; + +// TODO: Add support for more than one argument (metadata) per event +// Having more costs speed and memory. +#define MTR_MAX_ARGS 1 + +// Only use the macros to call these. +void internal_mtr_raw_event(const char *category, const char *name, char ph, void *id); +void internal_mtr_raw_event_arg(const char *category, const char *name, char ph, void *id, mtr_arg_type arg_type, const char *arg_name, void *arg_value); + +#ifdef MTR_ENABLED + +// c - category. Can be filtered by in trace viewer (or at least that's the intention). +// A good use is to pass __FILE__, there are macros further below that will do it for you. +// n - name. Pass __FUNCTION__ in most cases, unless you are marking up parts of one. + +// Scopes. In C++, use MTR_SCOPE. In C, always match them within the same scope. +#define MTR_BEGIN(c, n) internal_mtr_raw_event(c, n, 'B', 0) +#define MTR_END(c, n) internal_mtr_raw_event(c, n, 'E', 0) +#define MTR_SCOPE(c, n) MTRScopedTrace ____mtr_scope(c, n) +#define MTR_SCOPE_LIMIT(c, n, l) MTRScopedTraceLimit ____mtr_scope(c, n, l) + +// Async events. Can span threads. ID identifies which events to connect in the view. +#define MTR_START(c, n, id) internal_mtr_raw_event(c, n, 'S', (void *)(id)) +#define MTR_STEP(c, n, id, step) internal_mtr_raw_event_arg(c, n, 'T', (void *)(id), MTR_ARG_TYPE_STRING_CONST, "step", (void *)(step)) +#define MTR_FINISH(c, n, id) internal_mtr_raw_event(c, n, 'F', (void *)(id)) + +// Flow events. Like async events, but displayed in a more fancy way in the viewer. +#define MTR_FLOW_START(c, n, id) internal_mtr_raw_event(c, n, 's', (void *)(id)) +#define MTR_FLOW_STEP(c, n, id, step) internal_mtr_raw_event_arg(c, n, 't', (void *)(id), MTR_ARG_TYPE_STRING_CONST, "step", (void *)(step)) +#define MTR_FLOW_FINISH(c, n, id) internal_mtr_raw_event(c, n, 'f', (void *)(id)) + +// The same macros, but with a single named argument which shows up as metadata in the viewer. +// _I for int. +// _C is for a const string arg. +// _S will copy the string, freeing on flush (expensive but sometimes necessary). +// but required if the string was generated dynamically. + +// Note that it's fine to match BEGIN_S with END and BEGIN with END_S, etc. +#define MTR_BEGIN_C(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'B', 0, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval)) +#define MTR_END_C(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'E', 0, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval)) +#define MTR_SCOPE_C(c, n, aname, astrval) MTRScopedTraceArg ____mtr_scope(c, n, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval)) + +#define MTR_BEGIN_S(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'B', 0, MTR_ARG_TYPE_STRING_COPY, aname, (void *)(astrval)) +#define MTR_END_S(c, n, aname, astrval) internal_mtr_raw_event_arg(c, n, 'E', 0, MTR_ARG_TYPE_STRING_COPY, aname, (void *)(astrval)) +#define MTR_SCOPE_S(c, n, aname, astrval) MTRScopedTraceArg ____mtr_scope(c, n, MTR_ARG_TYPE_STRING_COPY, aname, (void *)(astrval)) + +#define MTR_BEGIN_I(c, n, aname, aintval) internal_mtr_raw_event_arg(c, n, 'B', 0, MTR_ARG_TYPE_INT, aname, (void*)(intptr_t)(aintval)) +#define MTR_END_I(c, n, aname, aintval) internal_mtr_raw_event_arg(c, n, 'E', 0, MTR_ARG_TYPE_INT, aname, (void*)(intptr_t)(aintval)) +#define MTR_SCOPE_I(c, n, aname, aintval) MTRScopedTraceArg ____mtr_scope(c, n, MTR_ARG_TYPE_INT, aname, (void*)(intptr_t)(aintval)) + +// Instant events. For things with no duration. +#define MTR_INSTANT(c, n) internal_mtr_raw_event(c, n, 'I', 0) +#define MTR_INSTANT_C(c, n, aname, astrval) internal_mtr_raw_event(c, n, 'I', 0, MTR_ARG_TYPE_STRING_CONST, aname, (void *)(astrval)) +#define MTR_INSTANT_I(c, n, aname, aintval) internal_mtr_raw_event(c, n, 'I', 0, MTR_ARG_TYPE_INT, aname, (void *)(aintval)) + +// Counters (can't do multi-value counters yet) +#define MTR_COUNTER(c, n, val) internal_mtr_raw_event_arg(c, n, 'C', 0, MTR_ARG_TYPE_INT, n, (void *)(intptr_t)(val)) + +// Metadata. Call at the start preferably. Must be const strings. + +#define MTR_META_PROCESS_NAME(n) internal_mtr_raw_event_arg("", "process_name", 'M', 0, MTR_ARG_TYPE_STRING_COPY, "name", (void *)(n)) +#define MTR_META_THREAD_NAME(n) internal_mtr_raw_event_arg("", "thread_name", 'M', 0, MTR_ARG_TYPE_STRING_COPY, "name", (void *)(n)) +#define MTR_META_THREAD_SORT_INDEX(i) internal_mtr_raw_event_arg("", "thread_sort_index", 'M', 0, MTR_ARG_TYPE_INT, "sort_index", (void *)(i)) + +#else + +#define MTR_BEGIN(c, n) +#define MTR_END(c, n) +#define MTR_SCOPE(c, n) +#define MTR_START(c, n, id) +#define MTR_STEP(c, n, id, step) +#define MTR_FINISH(c, n, id) +#define MTR_FLOW_START(c, n, id) +#define MTR_FLOW_STEP(c, n, id, step) +#define MTR_FLOW_FINISH(c, n, id) +#define MTR_INSTANT(c, n) + +#define MTR_BEGIN_C(c, n, aname, astrval) +#define MTR_END_C(c, n, aname, astrval) +#define MTR_SCOPE_C(c, n, aname, astrval) + +#define MTR_BEGIN_S(c, n, aname, astrval) +#define MTR_END_S(c, n, aname, astrval) +#define MTR_SCOPE_S(c, n, aname, astrval) + +#define MTR_BEGIN_I(c, n, aname, aintval) +#define MTR_END_I(c, n, aname, aintval) +#define MTR_SCOPE_I(c, n, aname, aintval) + +#define MTR_INSTANT(c, n) +#define MTR_INSTANT_C(c, n, aname, astrval) +#define MTR_INSTANT_I(c, n, aname, aintval) + +// Counters (can't do multi-value counters yet) +#define MTR_COUNTER(c, n, val) + +// Metadata. Call at the start preferably. Must be const strings. + +#define MTR_META_PROCESS_NAME(n) + +#define MTR_META_THREAD_NAME(n) +#define MTR_META_THREAD_SORT_INDEX(i) + +#endif + +// Shortcuts for simple function timing with automatic categories and names. + +#define MTR_BEGIN_FUNC() MTR_BEGIN(__FILE__, __FUNCTION__) +#define MTR_END_FUNC() MTR_END(__FILE__, __FUNCTION__) +#define MTR_SCOPE_FUNC() MTR_SCOPE(__FILE__, __FUNCTION__) +#define MTR_INSTANT_FUNC() MTR_INSTANT(__FILE__, __FUNCTION__) +#define MTR_SCOPE_FUNC_LIMIT_S(l) MTRScopedTraceLimit ____mtr_scope(__FILE__, __FUNCTION__, l) +#define MTR_SCOPE_FUNC_LIMIT_MS(l) MTRScopedTraceLimit ____mtr_scope(__FILE__, __FUNCTION__, 1) + +// Same, but with a single argument of the usual types. +#define MTR_BEGIN_FUNC_S(aname, arg) MTR_BEGIN_S(__FILE__, __FUNCTION__, aname, arg) +#define MTR_END_FUNC_S(aname, arg) MTR_END_S(__FILE__, __FUNCTION__, aname, arg) +#define MTR_SCOPE_FUNC_S(aname, arg) MTR_SCOPE_S(__FILE__, __FUNCTION__, aname, arg) + +#define MTR_BEGIN_FUNC_C(aname, arg) MTR_BEGIN_C(__FILE__, __FUNCTION__, aname, arg) +#define MTR_END_FUNC_C(aname, arg) MTR_END_C(__FILE__, __FUNCTION__, aname, arg) +#define MTR_SCOPE_FUNC_C(aname, arg) MTR_SCOPE_C(__FILE__, __FUNCTION__, aname, arg) + +#define MTR_BEGIN_FUNC_I(aname, arg) MTR_BEGIN_I(__FILE__, __FUNCTION__, aname, arg) +#define MTR_END_FUNC_I(aname, arg) MTR_END_I(__FILE__, __FUNCTION__, aname, arg) +#define MTR_SCOPE_FUNC_I(aname, arg) MTR_SCOPE_I(__FILE__, __FUNCTION__, aname, arg) + + +#ifdef MTR_ENABLED +// These are optimized to use X events (combined B and E). Much easier to do in C++ than in C. +class MTRScopedTrace { +public: + MTRScopedTrace(const char *category, const char *name) + : category_(category), name_(name) { + start_time_ = mtr_time_usec(); + } + ~MTRScopedTrace() { + internal_mtr_raw_event(category_, name_, 'X', &start_time_); + } + +private: + const char *category_; + const char *name_; + int64_t start_time_; +}; + +// Only outputs a block if execution time exceeded the limit. +// TODO: This will effectively call mtr_time_usec twice at the end, which is bad. +class MTRScopedTraceLimit { +public: + MTRScopedTraceLimit(const char *category, const char *name, double limit_s) + : category_(category), name_(name), limit_(limit_s) { + start_time_ = mtr_time_usec(); + } + ~MTRScopedTraceLimit() { + int64_t end_time = mtr_time_usec(); + if (end_time - start_time_ >= limit_) { + internal_mtr_raw_event(category_, name_, 'X', &start_time_); + } + } + +private: + const char *category_; + const char *name_; + double start_time_; + double limit_; +}; + +class MTRScopedTraceArg { +public: + MTRScopedTraceArg(const char *category, const char *name, mtr_arg_type arg_type, const char *arg_name, void *arg_value) + : category_(category), name_(name) { + internal_mtr_raw_event_arg(category, name, 'B', 0, arg_type, arg_name, arg_value); + } + ~MTRScopedTraceArg() { + internal_mtr_raw_event(category_, name_, 'E', 0); + } + +private: + const char *category_; + const char *name_; +}; + +#endif + +} //end namespace + +#endif diff --git a/CMakeLists.txt b/CMakeLists.txt index c9935e434bae28..42b34988518b59 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,9 +2,13 @@ cmake_minimum_required(VERSION 2.8) project(behavior_tree_core) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++11 -pthread -Werror=return-type") +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake") ############################################################# # http://answers.ros.org/question/230877/optionally-build-a-package-with-catkin/ +# +# This variable MUST NOT be set by the user manually, it will be detected +# automatically if you compile using catkin if( CATKIN_DEVEL_PREFIX OR CATKIN_BUILD_BINARY_PACKAGE) set(catkin_FOUND 1) add_definitions( -DUSING_ROS ) @@ -56,10 +60,26 @@ set(BT_Source src/behavior_tree.cpp src/xml_parsing.cpp + src/bt_file_logger.cpp 3rdparty/tinyXML2/tinyxml2.cpp + + 3rdparty/minitrace/minitrace.cpp ) include_directories(include 3rdparty/) +set(BEHAVIOR_TRE_LIBRARIES behavior_tree_core) + +find_package(ZMQ) + +if( ZMQ_FOUND ) + message(STATUS "ZeroMQ found.") + add_definitions( -DZMQ_FOUND ) + set(BT_Source ${BT_Source} src/bt_zmq_publisher.cpp ) + + set(BEHAVIOR_TRE_LIBRARIES behavior_tree_core zmq) +else() + message(WARNING "ZeroMQ NOT found. Skipping the build of [PublisherZMQ] and [bt_recorder].") +endif() ###################################################### # COMPILING LIBRARY @@ -97,13 +117,24 @@ elseif(GTEST_FOUND) endif() ###################################################### -# COMPILING EXAMPLES +# COMPILING EXAMPLES and TOOLS ###################################################### add_executable(crossdoor_example gtest/crossdoor_example.cpp ) -target_link_libraries(crossdoor_example - behavior_tree_core ) +target_link_libraries(crossdoor_example ${BEHAVIOR_TRE_LIBRARIES} ) + +add_executable(bt_log_cat tools/bt_log_cat.cpp ) +target_link_libraries(bt_log_cat behavior_tree_core ) + +if( ZMQ_FOUND ) + add_executable(bt_recorder tools/bt_recorder.cpp ) + target_link_libraries(bt_recorder ${BEHAVIOR_TRE_LIBRARIES} ) +endif() + +add_executable(simple_example gtest/simple_example.cpp ) +target_link_libraries(simple_example ${BEHAVIOR_TRE_LIBRARIES} ) + ###################################################### # INSTALLATION OF LIBRARY AND EXECUTABLE diff --git a/cmake/FindZMQ.cmake b/cmake/FindZMQ.cmake new file mode 100644 index 00000000000000..b0ae5dda9dacfe --- /dev/null +++ b/cmake/FindZMQ.cmake @@ -0,0 +1,59 @@ +# - Try to find ZMQ +# Once done this will define +# +# ZMQ_FOUND - system has ZMQ +# ZMQ_INCLUDE_DIRS - the ZMQ include directory +# ZMQ_LIBRARIES - Link these to use ZMQ +# ZMQ_DEFINITIONS - Compiler switches required for using ZMQ +# +# Copyright (c) 2011 Lee Hambley +# +# Redistribution and use is allowed according to the terms of the New +# BSD license. +# For details see the accompanying COPYING-CMAKE-SCRIPTS file. +# + +if (ZMQ_LIBRARIES AND ZMQ_INCLUDE_DIRS) + # in cache already + set(ZMQ_FOUND TRUE) +else (ZMQ_LIBRARIES AND ZMQ_INCLUDE_DIRS) + + find_path(ZMQ_INCLUDE_DIR + NAMES + zmq.h + PATHS + /usr/include + /usr/local/include + /opt/local/include + /sw/include + ) + + find_library(ZMQ_LIBRARY + NAMES + zmq + PATHS + /usr/lib + /usr/local/lib + /opt/local/lib + /sw/lib + ) + + set(ZMQ_INCLUDE_DIRS + ${ZMQ_INCLUDE_DIR} + ) + + if (ZMQ_LIBRARY) + set(ZMQ_LIBRARIES + ${ZMQ_LIBRARIES} + ${ZMQ_LIBRARY} + ) + endif (ZMQ_LIBRARY) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(ZMQ DEFAULT_MSG ZMQ_LIBRARIES ZMQ_INCLUDE_DIRS) + + # show the ZMQ_INCLUDE_DIRS and ZMQ_LIBRARIES variables only in the advanced view + mark_as_advanced(ZMQ_INCLUDE_DIRS ZMQ_LIBRARIES) + +endif (ZMQ_LIBRARIES AND ZMQ_INCLUDE_DIRS) + diff --git a/gtest/crossdoor_example.cpp b/gtest/crossdoor_example.cpp index 26c0af9fc61aa4..8b79519cf1641c 100644 --- a/gtest/crossdoor_example.cpp +++ b/gtest/crossdoor_example.cpp @@ -1,37 +1,43 @@ #include "crossdoor_dummy_nodes.h" #include "behavior_tree_core/xml_parsing.h" #include "behavior_tree_logger/bt_cout_logger.h" +#include "behavior_tree_logger/bt_minitrace_logger.h" +#include "behavior_tree_logger/bt_file_logger.h" + +#ifdef ZMQ_FOUND + #include "behavior_tree_logger/bt_zmq_publisher.h" +#endif // clang-format off const std::string xml_text = R"( - + - - + + - - - - + + + + - - - - - - - - + + + + + + + + - + - - + + - - )"; + + )"; // clang-format on @@ -42,7 +48,7 @@ int main(int argc, char** argv) BT::BehaviorTreeFactory factory; // register all the actions into the factory - CrossDoor cross_door(factory); + CrossDoor cross_door(factory, false); XMLParser parser; parser.loadFromText(xml_text); @@ -50,14 +56,23 @@ int main(int argc, char** argv) std::vector nodes; BT::TreeNodePtr root_node = parser.instantiateTree(factory, nodes); - StdCoutLogger logger(root_node.get()); + StdCoutLogger logger_cout(root_node.get()); + MinitraceLogger logger_minitrace(root_node.get(), "bt_trace.json"); + FileLogger logger_file( root_node.get(), "bt_trace.fbl", 32 ); + +#ifdef ZMQ_FOUND + PublisherZMQ publisher_zmq(root_node.get()); +#endif cross_door.CloseDoor(); std::cout << "---------------" << std::endl; root_node->executeTick(); + + std::this_thread::sleep_for( std::chrono::milliseconds(100) ); + std::cout << "---------------" << std::endl; - root_node->executeTick(); + while(1) root_node->executeTick(); std::cout << "---------------" << std::endl; return 0; } diff --git a/gtest/gtest_tree.cpp b/gtest/gtest_tree.cpp index 34a3867a49a7b9..54f77a440153ad 100644 --- a/gtest/gtest_tree.cpp +++ b/gtest/gtest_tree.cpp @@ -396,19 +396,19 @@ TEST_F(SequenceTripleActionTest, TripleAction) bool done_1 = false, done_2 = false, done_3 = false; auto sub1 = action_1.subscribeToStatusChange( - [&done_1](const TreeNode& , NodeStatus,NodeStatus status) + [&done_1](TimePoint, const TreeNode& , NodeStatus,NodeStatus status) { if( status == NodeStatus::SUCCESS) done_1 = true; }); auto sub2 = action_2.subscribeToStatusChange( - [&done_2](const TreeNode& , NodeStatus,NodeStatus status) + [&done_2](TimePoint, const TreeNode& , NodeStatus,NodeStatus status) { if( status == NodeStatus::SUCCESS) done_2 = true; }); auto sub3 = action_3.subscribeToStatusChange( - [&done_3](const TreeNode& , NodeStatus,NodeStatus status) + [&done_3](TimePoint, const TreeNode& , NodeStatus,NodeStatus status) { if( status == NodeStatus::SUCCESS) done_3 = true; }); diff --git a/gtest/include/crossdoor_dummy_nodes.h b/gtest/include/crossdoor_dummy_nodes.h index d520aabec7c7b9..159bf00784284b 100644 --- a/gtest/include/crossdoor_dummy_nodes.h +++ b/gtest/include/crossdoor_dummy_nodes.h @@ -4,8 +4,9 @@ using namespace BT; class CrossDoor { + int _multiplier; public: - CrossDoor(BT::BehaviorTreeFactory& factory) + CrossDoor(BT::BehaviorTreeFactory& factory, bool fast = true) { door_open_ = true; door_locked_ = false; @@ -17,36 +18,44 @@ class CrossDoor factory.registerSimpleAction("CloseDoor", [this]() { return CloseDoor(); }); factory.registerSimpleAction("IsDoorLocked", [this]() { return IsDoorLocked(); }); factory.registerSimpleAction("UnlockDoor", [this]() { return UnlockDoor(); }); + + _multiplier = fast ? 1 : 10; } BT::NodeStatus IsDoorOpen() { + std::this_thread::sleep_for( std::chrono::milliseconds(50) * _multiplier ); return door_open_ ? NodeStatus::SUCCESS : NodeStatus::FAILURE; } BT::NodeStatus IsDoorLocked() { + std::this_thread::sleep_for( std::chrono::milliseconds(50) * _multiplier ); return door_locked_ ? NodeStatus::SUCCESS : NodeStatus::FAILURE; } BT::NodeStatus UnlockDoor() { + std::this_thread::sleep_for( std::chrono::milliseconds(200) * _multiplier ); door_locked_ = false; return NodeStatus::SUCCESS; } BT::NodeStatus PassThroughDoor() { + std::this_thread::sleep_for( std::chrono::milliseconds(100) * _multiplier ); return door_open_ ? NodeStatus::SUCCESS : NodeStatus::FAILURE; } BT::NodeStatus PassThroughWindow() { + std::this_thread::sleep_for( std::chrono::milliseconds(100) * _multiplier ); return NodeStatus::SUCCESS; } BT::NodeStatus OpenDoor() { + std::this_thread::sleep_for( std::chrono::milliseconds(200) * _multiplier ); if (door_locked_) return NodeStatus::FAILURE; door_open_ = true; @@ -55,6 +64,7 @@ class CrossDoor BT::NodeStatus CloseDoor() { + std::this_thread::sleep_for( std::chrono::milliseconds(150) * _multiplier ); if (door_open_) { door_open_ = false; diff --git a/gtest/simple_example.cpp b/gtest/simple_example.cpp new file mode 100644 index 00000000000000..c5bcbfb88ba27a --- /dev/null +++ b/gtest/simple_example.cpp @@ -0,0 +1,104 @@ +#include "behavior_tree_core/xml_parsing.h" +#include "behavior_tree_logger/bt_cout_logger.h" +#include "behavior_tree_logger/bt_file_logger.h" + +// clang-format off + +class BatteryCondition: public BT::ConditionNode +{ +public: + BatteryCondition(const std::string& name): BT::ConditionNode(name) {} + BT::NodeStatus tick() override { + std::cout << "[ Battery: OK ]" << std::endl; + return BT::NodeStatus::SUCCESS; } +}; + +class TemperatureCondition: public BT::ConditionNode +{ +public: + TemperatureCondition(const std::string& name): BT::ConditionNode(name) {} + BT::NodeStatus tick() override { + std::cout << "[ Temperature: OK ]" << std::endl; + return BT::NodeStatus::SUCCESS; } +}; + +class MoveAction: public BT::ActionNode +{ +public: + MoveAction(const std::string& name): BT::ActionNode(name) {} + BT::NodeStatus tick() override { + std::cout << "[ Move: started ]" << std::endl; + std::this_thread::sleep_for( std::chrono::milliseconds(80) ); + std::cout << "[ Move: finished ]" << std::endl; + return BT::NodeStatus::SUCCESS; + } +}; + + +const std::string xml_text_A = R"( + + + + + + + + + + + + + + + )"; + +const std::string xml_text_B = R"( + + + + + + + + + + + + + )"; + + +// clang-format on + +int main(int argc, char** argv) +{ + using namespace BT; + + BT::BehaviorTreeFactory factory; + factory.registerNodeType("TemperatureOK"); + factory.registerNodeType("BatteryOK"); + factory.registerNodeType("Move"); + + XMLParser parser; + parser.loadFromText(xml_text_A); + + std::vector nodes; + BT::TreeNodePtr root_node = parser.instantiateTree(factory, nodes); + + StdCoutLogger logger_cout(root_node.get()); + FileLogger file_file( root_node.get(), "simple_trace.fbl", 32 ); + + + std::cout << "\n------- First executeTick() --------" << std::endl; + root_node->executeTick(); + std::cout << "\n------- sleep --------" << std::endl; + std::this_thread::sleep_for( std::chrono::milliseconds(50) ); + std::cout << "\n------- Second executeTick() --------" << std::endl; + root_node->executeTick(); + std::cout << "\n------- sleep --------" << std::endl; + std::this_thread::sleep_for( std::chrono::milliseconds(50) ); + std::cout << "\n------- Third executeTick() --------" << std::endl; + root_node->executeTick(); + std::cout << std::endl; + return 0; +} diff --git a/include/behavior_tree_core/tree_node.h b/include/behavior_tree_core/tree_node.h index dd366ce538b321..a925ef078098dc 100644 --- a/include/behavior_tree_core/tree_node.h +++ b/include/behavior_tree_core/tree_node.h @@ -90,6 +90,8 @@ enum SuccessPolicy // used to parametrize an object. It is up to the user's code to parse the string. typedef std::map NodeParameters; +typedef std::chrono::high_resolution_clock::time_point TimePoint; + // Abstract base class for Behavior Tree Nodes class TreeNode { @@ -106,7 +108,7 @@ class TreeNode virtual BT::NodeStatus tick() = 0; public: - // The constructor and the distructor + // The constructor and the destructor TreeNode(std::string name); virtual ~TreeNode() = default; @@ -128,7 +130,7 @@ class TreeNode virtual NodeType type() const = 0; - using StatusChangeSignal = Signal; + using StatusChangeSignal = Signal; using StatusChangeSubscriber = StatusChangeSignal::Subscriber; using StatusChangeCallback = StatusChangeSignal::CallableFunction; @@ -146,11 +148,17 @@ class TreeNode // get an unique identifier of this instance of treeNode uint16_t UID() const; + void setRegistrationName(const std::string& registration_name); + + const std::string& registrationName() const; + private: StatusChangeSignal state_change_signal_; - const uint16_t _uid; + const uint16_t uid_; + + std::string registration_name_; }; diff --git a/include/behavior_tree_core/xml_parsing.h b/include/behavior_tree_core/xml_parsing.h index 29eaa52d242176..a9ba06e780974e 100644 --- a/include/behavior_tree_core/xml_parsing.h +++ b/include/behavior_tree_core/xml_parsing.h @@ -50,7 +50,7 @@ inline NodePtr XMLParser::treeParsing(const tinyxml2::XMLElement* root_element, NodeParameters node_params; // Actions and Decorators have their own ID - if (element_name == "Action" || element_name == "Decorator") + if (element_name == "Action" || element_name == "Decorator" || element_name == "Condition") { node_ID = element->Attribute("ID"); } diff --git a/include/behavior_tree_logger/BT_logger.fbs b/include/behavior_tree_logger/BT_logger.fbs index 2fbbcba77eb94c..523f1d2ccb6eb3 100644 --- a/include/behavior_tree_logger/BT_logger.fbs +++ b/include/behavior_tree_logger/BT_logger.fbs @@ -1,33 +1,34 @@ namespace BT_Serialization; -enum Status : byte { +enum Status : byte { IDLE = 0, RUNNING, SUCCESS, FAILURE } -enum NodeType : byte { - UNDEFINED = 0, - ACTION, - CONDITION, - CONTROL, +enum Type : byte { + UNDEFINED = 0, + ACTION, + CONDITION, + CONTROL, DECORATOR, SUBTREE } struct Timestamp { - sec : uint32; - usec : uint32; + usec_since_epoch : uint64; } table TreeNode { - uid : uint16; - parend_uid : uint16; - type : NodeType; - name : string (required); + uid : uint16; + children_uid : [uint16]; + type : Type; + status : Status; + instance_name : string (required); + registration_name : string (required); } table BehaviorTree @@ -36,27 +37,20 @@ table BehaviorTree nodes : [TreeNode]; } -struct Status -{ - uid : uint16; - status : Status; -} - struct StatusChange { uid : uint16; prev_status : Status; status : Status; - usec_delta : uint32; + timestamp : Timestamp; } - table StatusChangeLog { - behavior_tree : BehaviorTree; - initial_status : [Status]; - initial_time : Timestamp; - state_changes : [StatusChange]; + behavior_tree : BehaviorTree; + state_changes : [StatusChange]; } root_type StatusChangeLog; + +root_type BehaviorTree; diff --git a/include/behavior_tree_logger/BT_logger_generated.h b/include/behavior_tree_logger/BT_logger_generated.h index 7600111d4d12ce..7c6630dab24af2 100644 --- a/include/behavior_tree_logger/BT_logger_generated.h +++ b/include/behavior_tree_logger/BT_logger_generated.h @@ -14,8 +14,6 @@ struct TreeNode; struct BehaviorTree; -struct Status; - struct StatusChange; struct StatusChangeLog; @@ -55,7 +53,7 @@ inline const char *EnumNameStatus(Status e) { return EnumNamesStatus()[index]; } -enum class NodeType : int8_t { +enum class Type : int8_t { UNDEFINED = 0, ACTION = 1, CONDITION = 2, @@ -66,19 +64,19 @@ enum class NodeType : int8_t { MAX = SUBTREE }; -inline const NodeType (&EnumValuesNodeType())[6] { - static const NodeType values[] = { - NodeType::UNDEFINED, - NodeType::ACTION, - NodeType::CONDITION, - NodeType::CONTROL, - NodeType::DECORATOR, - NodeType::SUBTREE +inline const Type (&EnumValuesType())[6] { + static const Type values[] = { + Type::UNDEFINED, + Type::ACTION, + Type::CONDITION, + Type::CONTROL, + Type::DECORATOR, + Type::SUBTREE }; return values; } -inline const char * const *EnumNamesNodeType() { +inline const char * const *EnumNamesType() { static const char * const names[] = { "UNDEFINED", "ACTION", @@ -91,74 +89,47 @@ inline const char * const *EnumNamesNodeType() { return names; } -inline const char *EnumNameNodeType(NodeType e) { +inline const char *EnumNameType(Type e) { const size_t index = static_cast(e); - return EnumNamesNodeType()[index]; + return EnumNamesType()[index]; } -FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) Timestamp FLATBUFFERS_FINAL_CLASS { +FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) Timestamp FLATBUFFERS_FINAL_CLASS { private: - uint32_t sec_; - uint32_t usec_; + uint64_t usec_since_epoch_; public: Timestamp() { memset(this, 0, sizeof(Timestamp)); } - Timestamp(uint32_t _sec, uint32_t _usec) - : sec_(flatbuffers::EndianScalar(_sec)), - usec_(flatbuffers::EndianScalar(_usec)) { - } - uint32_t sec() const { - return flatbuffers::EndianScalar(sec_); + Timestamp(uint64_t _usec_since_epoch) + : usec_since_epoch_(flatbuffers::EndianScalar(_usec_since_epoch)) { } - uint32_t usec() const { - return flatbuffers::EndianScalar(usec_); + uint64_t usec_since_epoch() const { + return flatbuffers::EndianScalar(usec_since_epoch_); } }; FLATBUFFERS_STRUCT_END(Timestamp, 8); -FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(2) Status FLATBUFFERS_FINAL_CLASS { - private: - uint16_t uid_; - int8_t status_; - int8_t padding0__; - - public: - Status() { - memset(this, 0, sizeof(Status)); - } - Status(uint16_t _uid, Status _status) - : uid_(flatbuffers::EndianScalar(_uid)), - status_(flatbuffers::EndianScalar(static_cast(_status))), - padding0__(0) { - (void)padding0__; - } - uint16_t uid() const { - return flatbuffers::EndianScalar(uid_); - } - Status status() const { - return static_cast(flatbuffers::EndianScalar(status_)); - } -}; -FLATBUFFERS_STRUCT_END(Status, 4); - -FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) StatusChange FLATBUFFERS_FINAL_CLASS { +FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(8) StatusChange FLATBUFFERS_FINAL_CLASS { private: uint16_t uid_; int8_t prev_status_; int8_t status_; - uint32_t usec_delta_; + int32_t padding0__; + Timestamp timestamp_; public: StatusChange() { memset(this, 0, sizeof(StatusChange)); } - StatusChange(uint16_t _uid, Status _prev_status, Status _status, uint32_t _usec_delta) + StatusChange(uint16_t _uid, Status _prev_status, Status _status, const Timestamp &_timestamp) : uid_(flatbuffers::EndianScalar(_uid)), prev_status_(flatbuffers::EndianScalar(static_cast(_prev_status))), status_(flatbuffers::EndianScalar(static_cast(_status))), - usec_delta_(flatbuffers::EndianScalar(_usec_delta)) { + padding0__(0), + timestamp_(_timestamp) { + (void)padding0__; } uint16_t uid() const { return flatbuffers::EndianScalar(uid_); @@ -169,38 +140,50 @@ FLATBUFFERS_MANUALLY_ALIGNED_STRUCT(4) StatusChange FLATBUFFERS_FINAL_CLASS { Status status() const { return static_cast(flatbuffers::EndianScalar(status_)); } - uint32_t usec_delta() const { - return flatbuffers::EndianScalar(usec_delta_); + const Timestamp ×tamp() const { + return timestamp_; } }; -FLATBUFFERS_STRUCT_END(StatusChange, 8); +FLATBUFFERS_STRUCT_END(StatusChange, 16); struct TreeNode FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { enum { VT_UID = 4, - VT_PAREND_UID = 6, + VT_CHILDREN_UID = 6, VT_TYPE = 8, - VT_NAME = 10 + VT_STATUS = 10, + VT_INSTANCE_NAME = 12, + VT_REGISTRATION_NAME = 14 }; uint16_t uid() const { return GetField(VT_UID, 0); } - uint16_t parend_uid() const { - return GetField(VT_PAREND_UID, 0); + const flatbuffers::Vector *children_uid() const { + return GetPointer *>(VT_CHILDREN_UID); } - NodeType type() const { - return static_cast(GetField(VT_TYPE, 0)); + Type type() const { + return static_cast(GetField(VT_TYPE, 0)); + } + Status status() const { + return static_cast(GetField(VT_STATUS, 0)); } - const flatbuffers::String *name() const { - return GetPointer(VT_NAME); + const flatbuffers::String *instance_name() const { + return GetPointer(VT_INSTANCE_NAME); + } + const flatbuffers::String *registration_name() const { + return GetPointer(VT_REGISTRATION_NAME); } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyField(verifier, VT_UID) && - VerifyField(verifier, VT_PAREND_UID) && + VerifyOffset(verifier, VT_CHILDREN_UID) && + verifier.Verify(children_uid()) && VerifyField(verifier, VT_TYPE) && - VerifyOffsetRequired(verifier, VT_NAME) && - verifier.Verify(name()) && + VerifyField(verifier, VT_STATUS) && + VerifyOffsetRequired(verifier, VT_INSTANCE_NAME) && + verifier.Verify(instance_name()) && + VerifyOffsetRequired(verifier, VT_REGISTRATION_NAME) && + verifier.Verify(registration_name()) && verifier.EndTable(); } }; @@ -211,14 +194,20 @@ struct TreeNodeBuilder { void add_uid(uint16_t uid) { fbb_.AddElement(TreeNode::VT_UID, uid, 0); } - void add_parend_uid(uint16_t parend_uid) { - fbb_.AddElement(TreeNode::VT_PAREND_UID, parend_uid, 0); + void add_children_uid(flatbuffers::Offset> children_uid) { + fbb_.AddOffset(TreeNode::VT_CHILDREN_UID, children_uid); } - void add_type(NodeType type) { + void add_type(Type type) { fbb_.AddElement(TreeNode::VT_TYPE, static_cast(type), 0); } - void add_name(flatbuffers::Offset name) { - fbb_.AddOffset(TreeNode::VT_NAME, name); + void add_status(Status status) { + fbb_.AddElement(TreeNode::VT_STATUS, static_cast(status), 0); + } + void add_instance_name(flatbuffers::Offset instance_name) { + fbb_.AddOffset(TreeNode::VT_INSTANCE_NAME, instance_name); + } + void add_registration_name(flatbuffers::Offset registration_name) { + fbb_.AddOffset(TreeNode::VT_REGISTRATION_NAME, registration_name); } explicit TreeNodeBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { @@ -228,7 +217,8 @@ struct TreeNodeBuilder { flatbuffers::Offset Finish() { const auto end = fbb_.EndTable(start_); auto o = flatbuffers::Offset(end); - fbb_.Required(o, TreeNode::VT_NAME); + fbb_.Required(o, TreeNode::VT_INSTANCE_NAME); + fbb_.Required(o, TreeNode::VT_REGISTRATION_NAME); return o; } }; @@ -236,13 +226,17 @@ struct TreeNodeBuilder { inline flatbuffers::Offset CreateTreeNode( flatbuffers::FlatBufferBuilder &_fbb, uint16_t uid = 0, - uint16_t parend_uid = 0, - NodeType type = NodeType::UNDEFINED, - flatbuffers::Offset name = 0) { + flatbuffers::Offset> children_uid = 0, + Type type = Type::UNDEFINED, + Status status = Status::IDLE, + flatbuffers::Offset instance_name = 0, + flatbuffers::Offset registration_name = 0) { TreeNodeBuilder builder_(_fbb); - builder_.add_name(name); - builder_.add_parend_uid(parend_uid); + builder_.add_registration_name(registration_name); + builder_.add_instance_name(instance_name); + builder_.add_children_uid(children_uid); builder_.add_uid(uid); + builder_.add_status(status); builder_.add_type(type); return builder_.Finish(); } @@ -250,15 +244,19 @@ inline flatbuffers::Offset CreateTreeNode( inline flatbuffers::Offset CreateTreeNodeDirect( flatbuffers::FlatBufferBuilder &_fbb, uint16_t uid = 0, - uint16_t parend_uid = 0, - NodeType type = NodeType::UNDEFINED, - const char *name = nullptr) { + const std::vector *children_uid = nullptr, + Type type = Type::UNDEFINED, + Status status = Status::IDLE, + const char *instance_name = nullptr, + const char *registration_name = nullptr) { return BT_Serialization::CreateTreeNode( _fbb, uid, - parend_uid, + children_uid ? _fbb.CreateVector(*children_uid) : 0, type, - name ? _fbb.CreateString(name) : 0); + status, + instance_name ? _fbb.CreateString(instance_name) : 0, + registration_name ? _fbb.CreateString(registration_name) : 0); } struct BehaviorTree FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { @@ -326,19 +324,11 @@ inline flatbuffers::Offset CreateBehaviorTreeDirect( struct StatusChangeLog FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { enum { VT_BEHAVIOR_TREE = 4, - VT_INITIAL_STATUS = 6, - VT_INITIAL_TIME = 8, - VT_STATE_CHANGES = 10 + VT_STATE_CHANGES = 6 }; const BehaviorTree *behavior_tree() const { return GetPointer(VT_BEHAVIOR_TREE); } - const flatbuffers::Vector *initial_status() const { - return GetPointer *>(VT_INITIAL_STATUS); - } - const Timestamp *initial_time() const { - return GetStruct(VT_INITIAL_TIME); - } const flatbuffers::Vector *state_changes() const { return GetPointer *>(VT_STATE_CHANGES); } @@ -346,9 +336,6 @@ struct StatusChangeLog FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { return VerifyTableStart(verifier) && VerifyOffset(verifier, VT_BEHAVIOR_TREE) && verifier.VerifyTable(behavior_tree()) && - VerifyOffset(verifier, VT_INITIAL_STATUS) && - verifier.Verify(initial_status()) && - VerifyField(verifier, VT_INITIAL_TIME) && VerifyOffset(verifier, VT_STATE_CHANGES) && verifier.Verify(state_changes()) && verifier.EndTable(); @@ -361,12 +348,6 @@ struct StatusChangeLogBuilder { void add_behavior_tree(flatbuffers::Offset behavior_tree) { fbb_.AddOffset(StatusChangeLog::VT_BEHAVIOR_TREE, behavior_tree); } - void add_initial_status(flatbuffers::Offset> initial_status) { - fbb_.AddOffset(StatusChangeLog::VT_INITIAL_STATUS, initial_status); - } - void add_initial_time(const Timestamp *initial_time) { - fbb_.AddStruct(StatusChangeLog::VT_INITIAL_TIME, initial_time); - } void add_state_changes(flatbuffers::Offset> state_changes) { fbb_.AddOffset(StatusChangeLog::VT_STATE_CHANGES, state_changes); } @@ -385,13 +366,9 @@ struct StatusChangeLogBuilder { inline flatbuffers::Offset CreateStatusChangeLog( flatbuffers::FlatBufferBuilder &_fbb, flatbuffers::Offset behavior_tree = 0, - flatbuffers::Offset> initial_status = 0, - const Timestamp *initial_time = 0, flatbuffers::Offset> state_changes = 0) { StatusChangeLogBuilder builder_(_fbb); builder_.add_state_changes(state_changes); - builder_.add_initial_time(initial_time); - builder_.add_initial_status(initial_status); builder_.add_behavior_tree(behavior_tree); return builder_.Finish(); } @@ -399,44 +376,40 @@ inline flatbuffers::Offset CreateStatusChangeLog( inline flatbuffers::Offset CreateStatusChangeLogDirect( flatbuffers::FlatBufferBuilder &_fbb, flatbuffers::Offset behavior_tree = 0, - const std::vector *initial_status = nullptr, - const Timestamp *initial_time = 0, const std::vector *state_changes = nullptr) { return BT_Serialization::CreateStatusChangeLog( _fbb, behavior_tree, - initial_status ? _fbb.CreateVector(*initial_status) : 0, - initial_time, state_changes ? _fbb.CreateVectorOfStructs(*state_changes) : 0); } -inline const BT_Serialization::StatusChangeLog *GetStatusChangeLog(const void *buf) { - return flatbuffers::GetRoot(buf); +inline const BT_Serialization::BehaviorTree *GetBehaviorTree(const void *buf) { + return flatbuffers::GetRoot(buf); } -inline const BT_Serialization::StatusChangeLog *GetSizePrefixedStatusChangeLog(const void *buf) { - return flatbuffers::GetSizePrefixedRoot(buf); +inline const BT_Serialization::BehaviorTree *GetSizePrefixedBehaviorTree(const void *buf) { + return flatbuffers::GetSizePrefixedRoot(buf); } -inline bool VerifyStatusChangeLogBuffer( +inline bool VerifyBehaviorTreeBuffer( flatbuffers::Verifier &verifier) { - return verifier.VerifyBuffer(nullptr); + return verifier.VerifyBuffer(nullptr); } -inline bool VerifySizePrefixedStatusChangeLogBuffer( +inline bool VerifySizePrefixedBehaviorTreeBuffer( flatbuffers::Verifier &verifier) { - return verifier.VerifySizePrefixedBuffer(nullptr); + return verifier.VerifySizePrefixedBuffer(nullptr); } -inline void FinishStatusChangeLogBuffer( +inline void FinishBehaviorTreeBuffer( flatbuffers::FlatBufferBuilder &fbb, - flatbuffers::Offset root) { + flatbuffers::Offset root) { fbb.Finish(root); } -inline void FinishSizePrefixedStatusChangeLogBuffer( +inline void FinishSizePrefixedBehaviorTreeBuffer( flatbuffers::FlatBufferBuilder &fbb, - flatbuffers::Offset root) { + flatbuffers::Offset root) { fbb.FinishSizePrefixed(root); } diff --git a/include/behavior_tree_logger/abstract_logger.h b/include/behavior_tree_logger/abstract_logger.h index b35de709721049..131c28a8e76777 100644 --- a/include/behavior_tree_logger/abstract_logger.h +++ b/include/behavior_tree_logger/abstract_logger.h @@ -9,44 +9,49 @@ namespace BT{ class StatusChangeLogger { public: + StatusChangeLogger(TreeNode* root_node); virtual ~StatusChangeLogger() = default; - virtual void callback(const TreeNode& node, + virtual void callback(BT::TimePoint timestamp, + const TreeNode& node, NodeStatus prev_status, NodeStatus status) = 0; - void setEnabled(bool enabled) { _enabled = enabled; } + virtual void flush() = 0; + + void setEnabled(bool enabled) { enabled_ = enabled; } - bool enabled() const { return _enabled; } + bool enabled() const { return enabled_; } // false by default. - bool showsTransitionToIdle() const { return _show_transition_to_idle; } + bool showsTransitionToIdle() const { return show_transition_to_idle_; } - void enableTransitionToIdle(bool enable ) { _show_transition_to_idle = enable; } + void enableTransitionToIdle(bool enable ) { show_transition_to_idle_ = enable; } private: - bool _enabled; - bool _show_transition_to_idle; - std::vector _subscribers; + bool enabled_; + bool show_transition_to_idle_; + std::vector subscribers_; }; //-------------------------------------------- -StatusChangeLogger::StatusChangeLogger(TreeNode *root_node): - _enabled(true), - _show_transition_to_idle(false) +inline StatusChangeLogger::StatusChangeLogger(TreeNode *root_node): + enabled_(true), + show_transition_to_idle_(true) { recursiveVisitor(root_node, [this](TreeNode* node) { - _subscribers.push_back( node->subscribeToStatusChange( - [this](const TreeNode& node, + subscribers_.push_back( node->subscribeToStatusChange( + [this](TimePoint timestamp, + const TreeNode& node, NodeStatus prev, NodeStatus status) { - if(_enabled && ( status != NodeStatus::IDLE || _show_transition_to_idle)) + if(enabled_ && ( status != NodeStatus::IDLE || show_transition_to_idle_)) { - this->callback(node,prev,status); + this->callback(timestamp, node,prev,status); } })); }); diff --git a/include/behavior_tree_logger/bt_cout_logger.h b/include/behavior_tree_logger/bt_cout_logger.h index dcaa1d081fe9d9..c29a5ab2172368 100644 --- a/include/behavior_tree_logger/bt_cout_logger.h +++ b/include/behavior_tree_logger/bt_cout_logger.h @@ -22,23 +22,31 @@ namespace BT{ class StdCoutLogger: public StatusChangeLogger { public: + StdCoutLogger(TreeNode* root_node): StatusChangeLogger(root_node) - {} - - virtual ~StdCoutLogger() = default; + { + static bool first_instance = true; + if( first_instance ) + { + first_instance = false; + } + else{ + throw std::logic_error("Only one instance of StdCoutLogger shall be created"); + } + } - virtual void callback(const TreeNode& node, + virtual void callback(TimePoint timestamp, + const TreeNode& node, NodeStatus prev_status, - NodeStatus status) + NodeStatus status) override { using namespace std::chrono; - auto now = high_resolution_clock::now(); - constexpr const char* whitespaces = " "; - constexpr const size_t ws_count = 20; + constexpr const char* whitespaces = " "; + constexpr const size_t ws_count = 25; - double since_epoch = duration( now.time_since_epoch() ).count(); + double since_epoch = duration( timestamp.time_since_epoch() ).count(); printf("[%.3f]: %s%s %s -> %s\n", since_epoch, node.name().c_str(), @@ -46,6 +54,8 @@ class StdCoutLogger: public StatusChangeLogger { toStr(prev_status, true), toStr(status, true) ); } + + virtual void flush() override { std::cout << std::flush; } }; diff --git a/include/behavior_tree_logger/bt_file_logger.h b/include/behavior_tree_logger/bt_file_logger.h new file mode 100644 index 00000000000000..74b6c2bcf4b282 --- /dev/null +++ b/include/behavior_tree_logger/bt_file_logger.h @@ -0,0 +1,40 @@ +#ifndef BT_FILE_LOGGER_H +#define BT_FILE_LOGGER_H + +#include +#include +#include +#include "abstract_logger.h" + +namespace BT{ + +class FileLogger: public StatusChangeLogger { + +public: + FileLogger(TreeNode* root_node, const char* filename, uint16_t buffer_size); + + virtual ~FileLogger() override; + + virtual void callback(TimePoint timestamp, + const TreeNode& node, + NodeStatus prev_status, + NodeStatus status) override; + + virtual void flush() override; + +private: + + std::ofstream file_os_; + + std::chrono::high_resolution_clock::time_point start_time; + + std::vector< std::array > buffer_; + + bool buffer_max_size_; +}; + + +} // end namespace + + +#endif // BT_FILE_LOGGER_H diff --git a/include/behavior_tree_logger/bt_flatbuffer_helper.h b/include/behavior_tree_logger/bt_flatbuffer_helper.h new file mode 100644 index 00000000000000..51f6616100ef96 --- /dev/null +++ b/include/behavior_tree_logger/bt_flatbuffer_helper.h @@ -0,0 +1,107 @@ +#ifndef BT_FLATBUFFER_HELPER_H +#define BT_FLATBUFFER_HELPER_H + +#include "abstract_logger.h" +#include "BT_logger_generated.h" + +namespace BT{ + + +inline BT_Serialization::Type convertToFlatbuffers(NodeType type) +{ + switch( type ){ + case BT::NodeType::ACTION: return BT_Serialization::Type::ACTION; + case BT::NodeType::DECORATOR: return BT_Serialization::Type::DECORATOR; + case BT::NodeType::CONTROL: return BT_Serialization::Type::CONTROL; + case BT::NodeType::CONDITION: return BT_Serialization::Type::CONDITION; + case BT::NodeType::SUBTREE: return BT_Serialization::Type::SUBTREE; + case BT::NodeType::UNDEFINED: return BT_Serialization::Type::UNDEFINED; + } + return BT_Serialization::Type::UNDEFINED; +} + +inline BT_Serialization::Status convertToFlatbuffers(NodeStatus type) +{ + switch( type ){ + case BT::NodeStatus::IDLE: return BT_Serialization::Status::IDLE; + case BT::NodeStatus::SUCCESS: return BT_Serialization::Status::SUCCESS; + case BT::NodeStatus::RUNNING: return BT_Serialization::Status::RUNNING; + case BT::NodeStatus::FAILURE: return BT_Serialization::Status::FAILURE; + } + return BT_Serialization::Status::IDLE; +} + +inline void CreateFlatbuffersBehaviorTree(flatbuffers::FlatBufferBuilder& builder, + BT::TreeNode *root_node) +{ + + std::vector> fb_nodes; + + recursiveVisitor(root_node, [&](TreeNode* node) + { + std::vector children_uid; + if (auto control = dynamic_cast(node)) + { + children_uid.reserve( control->children().size() ); + for (const auto& child : control->children()) + { + children_uid.push_back( child->UID() ); + } + } + else if (auto decorator = dynamic_cast(node)) + { + const auto& child = decorator->child(); + children_uid.push_back( child->UID() ); + } + + fb_nodes.push_back( BT_Serialization::CreateTreeNode( + builder, + node->UID(), + builder.CreateVector( children_uid ), + convertToFlatbuffers(node->type() ), + convertToFlatbuffers(node->status()), + builder.CreateString(node->name().c_str()), + builder.CreateString(node->registrationName().c_str()) + ) + ); + }); + + auto behavior_tree = BT_Serialization::CreateBehaviorTree( + builder, + root_node->UID(), + builder.CreateVector(fb_nodes) + ); + + builder.Finish(behavior_tree); +} + +/** Serialize manually the informations about state transition + * No flatbuffer serialization here + */ +inline +std::array SerializeTransition(uint16_t UID, + TimePoint timestamp, + NodeStatus prev_status, + NodeStatus status) +{ + using namespace std::chrono; + std::array buffer; + auto usec_since_epoch = duration_cast( timestamp.time_since_epoch() ).count(); + uint32_t t_sec = usec_since_epoch / 1000000; + uint32_t t_usec = usec_since_epoch % 1000000; + + flatbuffers::WriteScalar( &buffer[0], t_sec ); + flatbuffers::WriteScalar( &buffer[4], t_usec ); + flatbuffers::WriteScalar( &buffer[8], UID ); + + flatbuffers::WriteScalar( &buffer[10], + static_cast(convertToFlatbuffers(prev_status)) ); + flatbuffers::WriteScalar( &buffer[11], + static_cast(convertToFlatbuffers(status)) ); + + return buffer; +} + +} // end namespace + +#endif // BT_FLATBUFFER_HELPER_H diff --git a/include/behavior_tree_logger/bt_minitrace_logger.h b/include/behavior_tree_logger/bt_minitrace_logger.h new file mode 100644 index 00000000000000..3cc26f186789a0 --- /dev/null +++ b/include/behavior_tree_logger/bt_minitrace_logger.h @@ -0,0 +1,73 @@ +#ifndef BT_MINITRACE_LOGGER_H +#define BT_MINITRACE_LOGGER_H + +#include +#include "abstract_logger.h" +#include "minitrace/minitrace.h" + +namespace BT{ + + +class MinitraceLogger: public StatusChangeLogger { + +public: + MinitraceLogger(TreeNode* root_node, const char* filename_json): + StatusChangeLogger(root_node) + { + static bool first_instance = true; + if( first_instance ) + { + first_instance = false; + } + else{ + throw std::logic_error("Only one instance of MinitraceLogger shall be created"); + } + minitrace::mtr_register_sigint_handler(); + minitrace::mtr_init(filename_json); + this->enableTransitionToIdle(true); + } + + virtual ~MinitraceLogger() override + { + minitrace::mtr_flush(); + minitrace::mtr_shutdown(); + } + + virtual void callback(TimePoint timestamp, const TreeNode& node, + NodeStatus prev_status, + NodeStatus status) override + { + using namespace minitrace; + + const bool statusCompleted = (status == NodeStatus::SUCCESS || + status == NodeStatus::FAILURE); + + const char* category = toStr(node.type()); + const char* name = node.name().c_str(); + + if( prev_status == NodeStatus::IDLE && statusCompleted) + { + MTR_INSTANT(category, name); + } + else if( status == NodeStatus::RUNNING ) + { + MTR_BEGIN(category, name); + } + else if( prev_status == NodeStatus::RUNNING && statusCompleted ) + { + MTR_END( category, name ); + } + } + + virtual void flush() override { + minitrace::mtr_flush(); + } +private: + TimePoint prev_time_; +}; + + +} // end namespace + + +#endif // BT_MINITRACE_LOGGER_H diff --git a/include/behavior_tree_logger/bt_zmq_publisher.h b/include/behavior_tree_logger/bt_zmq_publisher.h new file mode 100644 index 00000000000000..4bbc662ad257de --- /dev/null +++ b/include/behavior_tree_logger/bt_zmq_publisher.h @@ -0,0 +1,52 @@ +#ifndef BT_ZMQ_PUBLISHER_H +#define BT_ZMQ_PUBLISHER_H + +#include +#include +#include +#include "abstract_logger.h" +#include "BT_logger_generated.h" + +namespace BT{ + +class PublisherZMQ: public StatusChangeLogger +{ +public: + PublisherZMQ(TreeNode* root_node, int max_msg_per_second = 25); + + virtual ~PublisherZMQ(); + +private: + + virtual void callback(TimePoint timestamp, + const TreeNode& node, + NodeStatus prev_status, NodeStatus status) override; + + virtual void flush() override; + + TreeNode *root_node_; + std::vector tree_buffer_; + std::vector status_buffer_; + std::vector< std::array > transition_buffer_; + std::chrono::microseconds min_time_between_msgs_; + + zmq::context_t zmq_context_; + zmq::socket_t zmq_publisher_; + zmq::socket_t zmq_server_; + + std::atomic_bool active_server_; + std::thread thread_; + + void createStatusBuffer(); + + TimePoint deadline_; + std::mutex mutex_; + std::atomic_bool send_pending_; + + std::future send_future_; +}; + + +} + +#endif // BT_ZMQ_PUBLISHER_H diff --git a/src/bt_factory.cpp b/src/bt_factory.cpp index bee438df61d94a..4ace64ae9dfc22 100644 --- a/src/bt_factory.cpp +++ b/src/bt_factory.cpp @@ -68,7 +68,9 @@ std::unique_ptr BehaviorTreeFactory::instantiateTreeNode(const std::st { throw BehaviorTreeException("ID '" + ID + "' not registered"); } - return it->second(name, params); + std::unique_ptr node = it->second(name, params); + node->setRegistrationName( ID ); + return node; } } // end namespace diff --git a/src/bt_file_logger.cpp b/src/bt_file_logger.cpp new file mode 100644 index 00000000000000..ab30281ba996f7 --- /dev/null +++ b/src/bt_file_logger.cpp @@ -0,0 +1,73 @@ +#include "behavior_tree_logger/bt_file_logger.h" +#include "behavior_tree_logger/bt_flatbuffer_helper.h" + +namespace BT{ + +FileLogger::FileLogger(BT::TreeNode *root_node, const char *filename, uint16_t buffer_size): + StatusChangeLogger(root_node), + buffer_max_size_(buffer_size) +{ + if( buffer_max_size_ != 0) + { + buffer_.reserve( buffer_max_size_ ); + } + + enableTransitionToIdle( true ); + + flatbuffers::FlatBufferBuilder builder(1024); + CreateFlatbuffersBehaviorTree( builder, root_node); + + //------------------------------------- + + file_os_.open( filename, std::ofstream::binary | std::ofstream::out); + + // serialize the length of the buffer in the first 4 bytes + char size_buff[4]; + flatbuffers::WriteScalar(size_buff, static_cast( builder.GetSize()) ); + + file_os_.write( size_buff, 4 ); + file_os_.write( reinterpret_cast(builder.GetBufferPointer()), + builder.GetSize() ); + +} + +FileLogger::~FileLogger() +{ + this->flush(); + file_os_.close(); +} + +void FileLogger::callback(TimePoint timestamp, const TreeNode &node, NodeStatus prev_status, NodeStatus status) +{ + std::array buffer = SerializeTransition( + node.UID(), + timestamp, + prev_status, + status ); + + if( buffer_max_size_ == 0 ) + { + file_os_.write( reinterpret_cast(buffer.data()), buffer.size() ); + } + else{ + buffer_.push_back( buffer ); + if( buffer_.size() >= buffer_max_size_) + { + this->flush(); + } + } +} + +void FileLogger::flush() +{ + for (const auto& array: buffer_ ) + { + file_os_.write( reinterpret_cast(array.data()), array.size() ); + } + file_os_.flush(); + buffer_.clear(); +} + + + +} diff --git a/src/bt_zmq_publisher.cpp b/src/bt_zmq_publisher.cpp new file mode 100644 index 00000000000000..db5c6356c784d4 --- /dev/null +++ b/src/bt_zmq_publisher.cpp @@ -0,0 +1,140 @@ +#include "behavior_tree_logger/bt_zmq_publisher.h" +#include "behavior_tree_logger/bt_flatbuffer_helper.h" +#include + +namespace BT { + +void PublisherZMQ::createStatusBuffer() +{ + status_buffer_.clear(); + recursiveVisitor(root_node_, [this](TreeNode* node) + { + size_t index = status_buffer_.size(); + status_buffer_.resize( index + 3 ); + flatbuffers::WriteScalar( &status_buffer_[index], node->UID() ); + flatbuffers::WriteScalar( &status_buffer_[index+2], + static_cast( convertToFlatbuffers(node->status())) ); + }); +} + +PublisherZMQ::PublisherZMQ(TreeNode *root_node, + int max_msg_per_second): + StatusChangeLogger(root_node), + root_node_(root_node), + min_time_between_msgs_( std::chrono::microseconds(1000*1000) / max_msg_per_second ), + zmq_context_(1), + zmq_publisher_( zmq_context_, ZMQ_PUB ), + zmq_server_( zmq_context_, ZMQ_REP ), + send_pending_(false) +{ + static bool first_instance = true; + if( first_instance ) + { + first_instance = false; + } + else{ + throw std::logic_error("Only one instance of PublisherZMQ shall be created"); + } + + flatbuffers::FlatBufferBuilder builder(1024); + CreateFlatbuffersBehaviorTree( builder, root_node); + + tree_buffer_.resize(builder.GetSize()); + memcpy( tree_buffer_.data(), builder.GetBufferPointer(), builder.GetSize() ); + + zmq_publisher_.bind( "tcp://*:1666" ); + zmq_server_.bind( "tcp://*:1667" ); + + active_server_ = true; + + thread_ = std::thread([this]() + { + while(active_server_) + { + zmq::message_t req; + try{ + zmq_server_.recv( &req ); + zmq::message_t reply ( tree_buffer_.size() ); + memcpy( reply.data(), tree_buffer_.data(), tree_buffer_.size() ); + zmq_server_.send( reply ); + } + catch( zmq::error_t& err) + { + active_server_ = false; + } + } + }); + + createStatusBuffer(); +} + +PublisherZMQ::~PublisherZMQ() +{ + active_server_ = false; + if( thread_.joinable()) + { + thread_.join(); + } + flush(); +} + +void PublisherZMQ::callback(TimePoint timestamp, const TreeNode &node, NodeStatus prev_status, NodeStatus status) +{ + using namespace std::chrono; + + std::array transition = SerializeTransition(node.UID(), + timestamp, prev_status, status); + { + std::unique_lock lock( mutex_ ); + transition_buffer_.push_back( transition ); + } + + if( !send_pending_ ) + { + send_pending_ = true; + send_future_ = std::async(std::launch::async, [this]() + { + std::this_thread::sleep_for( min_time_between_msgs_ ); + flush(); + } ); + } +} + +void PublisherZMQ::flush() +{ + zmq::message_t message; + { + std::unique_lock lock( mutex_ ); + + const size_t msg_size = status_buffer_.size() + 8 + + (transition_buffer_.size() * 12); + + message.rebuild( msg_size ); + uint8_t* data_ptr = static_cast( message.data() ); + + // first 4 bytes are the side of the header + flatbuffers::WriteScalar( data_ptr, status_buffer_.size() ); + data_ptr += sizeof(uint32_t); + // copy the header part + memcpy( data_ptr, status_buffer_.data(), status_buffer_.size() ); + data_ptr += status_buffer_.size(); + + // first 4 bytes are the side of the transition buffer + flatbuffers::WriteScalar( data_ptr, transition_buffer_.size() ); + data_ptr += sizeof(uint32_t); + + for (auto& transition: transition_buffer_) + { + memcpy( data_ptr, transition.data(), transition.size() ); + data_ptr += transition.size(); + } + transition_buffer_.clear(); + createStatusBuffer(); + } + + zmq_publisher_.send(message); + send_pending_ = false; + // printf("%.3f zmq send\n", std::chrono::duration( std::chrono::high_resolution_clock::now().time_since_epoch() ).count()); +} + +} diff --git a/src/decorator_negation_node.cpp b/src/decorator_negation_node.cpp index fd59ecff8963fd..6a596972a17889 100644 --- a/src/decorator_negation_node.cpp +++ b/src/decorator_negation_node.cpp @@ -19,6 +19,8 @@ BT::DecoratorNegationNode::DecoratorNegationNode(std::string name) : DecoratorNo BT::NodeStatus BT::DecoratorNegationNode::tick() { + setStatus( NodeStatus::RUNNING ); + const NodeStatus child_state = child_node_->executeTick(); switch (child_state) diff --git a/src/tree_node.cpp b/src/tree_node.cpp index 957269aff081d9..83349c895a5490 100644 --- a/src/tree_node.cpp +++ b/src/tree_node.cpp @@ -15,11 +15,11 @@ static uint8_t getUID() { - static uint8_t uid = 0; + static uint8_t uid = 1; return uid++; } -BT::TreeNode::TreeNode(std::string name) : name_(name), status_(NodeStatus::IDLE), _uid( getUID() ) +BT::TreeNode::TreeNode(std::string name) : name_(name), status_(NodeStatus::IDLE), uid_( getUID() ) { } @@ -41,7 +41,8 @@ void BT::TreeNode::setStatus(NodeStatus new_status) if (prev_status != new_status) { state_condition_variable_.notify_all(); - state_change_signal_.notify(*this, prev_status, new_status); + state_change_signal_.notify( std::chrono::high_resolution_clock::now(), + *this, prev_status, new_status); } } @@ -82,5 +83,15 @@ BT::TreeNode::StatusChangeSubscriber BT::TreeNode::subscribeToStatusChange(BT::T uint16_t BT::TreeNode::UID() const { - return _uid; + return uid_; +} + +void BT::TreeNode::setRegistrationName(const std::string ®istration_name) +{ + registration_name_ = registration_name; +} + +const std::string &BT::TreeNode::registrationName() const +{ + return registration_name_; } diff --git a/src/xml_parsing.cpp b/src/xml_parsing.cpp index 019ad8fb523116..041bcd1ab12ff2 100644 --- a/src/xml_parsing.cpp +++ b/src/xml_parsing.cpp @@ -83,7 +83,10 @@ bool XMLParser::verifyXML(std::vector& error_messages) const for (auto node = xml_root->FirstChildElement(); node != nullptr; node = node->NextSiblingElement()) { const char* name = node->Name(); - if (strEqual(name, "Action") || strEqual(name, "Decorator") || strEqual(name, "SubTree")) + if (strEqual(name, "Action") || + strEqual(name, "Decorator") || + strEqual(name, "SubTree") || + strEqual(name, "Condition")) { const char* ID = node->Attribute("ID"); if (!ID) @@ -135,6 +138,17 @@ bool XMLParser::verifyXML(std::vector& error_messages) const AppendError(node->GetLineNum(), "The node must have the attribute [ID]"); } } + else if (strEqual(name, "Condition")) + { + if (children_count != 0) + { + AppendError(node->GetLineNum(), "The node must not have any child"); + } + if (!node->Attribute("ID")) + { + AppendError(node->GetLineNum(), "The node must have the attribute [ID]"); + } + } else if (strEqual(name, "Sequence") || strEqual(name, "SequenceStar") || strEqual(name, "Fallback") || strEqual(name, "FallbackStar")) { diff --git a/tools/bt_log_cat.cpp b/tools/bt_log_cat.cpp new file mode 100644 index 00000000000000..9d860ecfe42f2e --- /dev/null +++ b/tools/bt_log_cat.cpp @@ -0,0 +1,99 @@ +#include +#include +#include +#include +#include "behavior_tree_logger/BT_logger_generated.h" + +int main(int argc, char* argv[]) +{ + if( argc != 2) + { + printf("Wrong number of arguments\nUsage: %s [filename]\n", argv[0]); + return 1; + } + + FILE* file = fopen(argv[1], "rb"); + + if( !file ) + { + printf("Failed to open file: [%s]\n", argv[1]); + return 1; + } + + fseek(file, 0L, SEEK_END); + const size_t length = ftell(file); + fseek(file, 0L, SEEK_SET); + std::vector buffer(length); + fread( buffer.data(), sizeof(char), length, file); + fclose(file); + + const int bt_header_size = flatbuffers::ReadScalar(&buffer[0]); + + auto behavior_tree = BT_Serialization::GetBehaviorTree( &buffer[4] ); + + std::unordered_map names_by_uid; + std::unordered_map node_by_uid; + + for( const BT_Serialization::TreeNode* node: *(behavior_tree->nodes()) ) + { + names_by_uid.insert( { node->uid(), std::string(node->instance_name()->c_str()) } ); + node_by_uid.insert( { node->uid(), node } ); + } + + printf("----------------------------\n"); + + std::function recursiveStep; + + recursiveStep = [&](uint16_t uid, int indent) + { + for(int i=0; i< indent; i++) + { + printf(" "); + names_by_uid[uid] = std::string(" ") + names_by_uid[uid]; + } + printf("%s\n", names_by_uid[uid].c_str() ); + std::cout << std::flush; + + const auto& node = node_by_uid[uid]; + + for (size_t i=0; i< node->children_uid()->size(); i++ ) + { + recursiveStep( node->children_uid()->Get(i), indent+1); + } + }; + + recursiveStep( behavior_tree->root_uid(), 0 ); + + printf("----------------------------\n"); + + constexpr const char* whitespaces = " "; + constexpr const size_t ws_count = 25; + + auto printStatus = [](BT_Serialization::Status status) + { + switch (status) + { + case BT_Serialization::Status::SUCCESS: return ( "\x1b[32m" "SUCCESS" "\x1b[0m"); // RED + case BT_Serialization::Status::FAILURE: return ( "\x1b[31m" "FAILURE" "\x1b[0m"); // GREEN + case BT_Serialization::Status::RUNNING: return ( "\x1b[33m" "RUNNING" "\x1b[0m"); // YELLOW + case BT_Serialization::Status::IDLE: return ( "\x1b[36m" "IDLE " "\x1b[0m"); // CYAN + } + return "Undefined"; + }; + + for( size_t index = bt_header_size + 4; index < length; index +=12 ) + { + const uint16_t uid = flatbuffers::ReadScalar(&buffer[index+8]); + const std::string& name = names_by_uid[uid]; + const uint32_t t_sec = flatbuffers::ReadScalar( &buffer[index] ); + const uint32_t t_usec = flatbuffers::ReadScalar( &buffer[index+4] ); + + printf("[%d.%06d]: %s%s %s -> %s\n", t_sec, t_usec, + name.c_str(), + &whitespaces[ std::min( ws_count, name.size())], + printStatus(flatbuffers::ReadScalar(&buffer[index+10] )), + printStatus(flatbuffers::ReadScalar(&buffer[index+11] )) ); + } + + return 0; +} diff --git a/tools/bt_recorder.cpp b/tools/bt_recorder.cpp new file mode 100644 index 00000000000000..81f2e632303616 --- /dev/null +++ b/tools/bt_recorder.cpp @@ -0,0 +1,99 @@ +#include +#include +#include +#include +#include +#include +#include "behavior_tree_logger/BT_logger_generated.h" + +// http://zguide.zeromq.org/cpp:interrupt +static bool s_interrupted = false; + +static void s_signal_handler (int) +{ + s_interrupted = true; +} + +static void CatchSignals (void) +{ + struct sigaction action; + action.sa_handler = s_signal_handler; + action.sa_flags = 0; + sigemptyset (&action.sa_mask); + sigaction (SIGINT, &action, NULL); + sigaction (SIGTERM, &action, NULL); +} + +int main(int argc, char* argv[]) +{ + if( argc != 2) + { + printf("Wrong number of arguments\nUsage: %s [filename]\n", argv[0]); + return 1; + } + + // register CTRL+C signal handler + CatchSignals(); + + zmq::context_t context (1); + + // Socket to talk to server + std::cout << "Trying to connect to [tcp://localhost:1666]\n" << std::endl; + + zmq::socket_t subscriber (context, ZMQ_SUB); + subscriber.connect("tcp://localhost:1666"); + + // Subscribe to everything + subscriber.setsockopt(ZMQ_SUBSCRIBE, "", 0); + + printf("----------- Started -----------------\n"); + + bool first_message = true; + std::ofstream file_os; + + while( !s_interrupted ) + { + zmq::message_t update; + zmq::message_t msg; + try { + subscriber.recv (&update); + } + catch(zmq::error_t& e) + { + if( !s_interrupted ) + { + std::cout << "subscriber.recv() failed with exception: "<< + e.what() << std::endl; + return -1; + } + } + + if( !s_interrupted ) + { + char* data_ptr = static_cast( update.data() ); + const uint32_t header_size = flatbuffers::ReadScalar( data_ptr ); + + if( first_message ) + { + printf("First message received\n"); + first_message = false; + + file_os.open( argv[1], std::ofstream::binary | std::ofstream::out); + file_os.write( data_ptr, 4 + header_size ) ; + } + data_ptr += 4 + header_size; + + const uint32_t transition_count = flatbuffers::ReadScalar( data_ptr ); + data_ptr += sizeof( uint32_t ); + + file_os.write( data_ptr, 12*transition_count) ; + } + } + + subscriber.close(); + + printf("Results saved to file\n"); + file_os.close(); + + return 0; +}