Initial commit

This commit is contained in:
InoriRus
2021-12-01 19:29:27 +10:00
parent b1e7dcdc5d
commit 43f49c8763
1843 changed files with 1111694 additions and 0 deletions
+274
View File
@@ -0,0 +1,274 @@
/************************************************
* creation time : 2017/08/20
* author : Blake Martin
* email : rationalcoder@gmail.com
************************************************/
/**
Lightweight profiler library for c++
Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin
Licensed under either of
* MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
* Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
at your option.
The MIT License
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.
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 EASY_PROFILER_ALIGNMENT_HELPERS_H
#define EASY_PROFILER_ALIGNMENT_HELPERS_H
#include <cstddef>
#include <cstdint>
#include <easy/details/easy_compiler_support.h>
//! Checks if a pointer is aligned.
//! \param ptr The pointer to check.
//! \param alignment The alignement (must be a power of 2)
//! \returns true if the memory is aligned.
//!
template <uint32_t ALIGNMENT>
EASY_FORCE_INLINE bool is_aligned(void* ptr)
{
static_assert((ALIGNMENT & 1) == 0, "Alignment must be a power of two.");
return ((uintptr_t)ptr & (ALIGNMENT-1)) == 0;
}
EASY_FORCE_INLINE void unaligned_zero16(void* ptr)
{
#ifndef EASY_ENABLE_STRICT_ALIGNMENT
*(uint16_t*)ptr = 0;
#else
((char*)ptr)[0] = 0;
((char*)ptr)[1] = 0;
#endif
}
EASY_FORCE_INLINE void unaligned_zero32(void* ptr)
{
#ifndef EASY_ENABLE_STRICT_ALIGNMENT
*(uint32_t*)ptr = 0;
#else
((char*)ptr)[0] = 0;
((char*)ptr)[1] = 0;
((char*)ptr)[2] = 0;
((char*)ptr)[3] = 0;
#endif
}
EASY_FORCE_INLINE void unaligned_zero64(void* ptr)
{
#ifdef EASY_ENABLE_STRICT_ALIGNMENT
// Assume unaligned is more common.
if (!is_aligned<alignof(uint64_t)>(ptr)) {
((char*)ptr)[0] = 0;
((char*)ptr)[1] = 0;
((char*)ptr)[2] = 0;
((char*)ptr)[3] = 0;
((char*)ptr)[4] = 0;
((char*)ptr)[5] = 0;
((char*)ptr)[6] = 0;
((char*)ptr)[7] = 0;
}
else
#endif
*(uint64_t*)ptr = 0;
}
template <typename T>
EASY_FORCE_INLINE void unaligned_store16(void* ptr, T val)
{
static_assert(sizeof(T) == 2, "16 bit type required.");
#ifndef EASY_ENABLE_STRICT_ALIGNMENT
*(T*)ptr = val;
#else
const char* const temp = (const char*)&val;
((char*)ptr)[0] = temp[0];
((char*)ptr)[1] = temp[1];
#endif
}
template <typename T>
EASY_FORCE_INLINE void unaligned_store32(void* ptr, T val)
{
static_assert(sizeof(T) == 4, "32 bit type required.");
#ifndef EASY_ENABLE_STRICT_ALIGNMENT
*(T*)ptr = val;
#else
const char* const temp = (const char*)&val;
((char*)ptr)[0] = temp[0];
((char*)ptr)[1] = temp[1];
((char*)ptr)[2] = temp[2];
((char*)ptr)[3] = temp[3];
#endif
}
template <typename T>
EASY_FORCE_INLINE void unaligned_store64(void* ptr, T val)
{
static_assert(sizeof(T) == 8, "64 bit type required.");
#ifdef EASY_ENABLE_STRICT_ALIGNMENT
// Assume unaligned is more common.
if (!is_aligned<alignof(T)>(ptr)) {
const char* const temp = (const char*)&val;
((char*)ptr)[0] = temp[0];
((char*)ptr)[1] = temp[1];
((char*)ptr)[2] = temp[2];
((char*)ptr)[3] = temp[3];
((char*)ptr)[4] = temp[4];
((char*)ptr)[5] = temp[5];
((char*)ptr)[6] = temp[6];
((char*)ptr)[7] = temp[7];
}
else
#endif
*(T*)ptr = val;
}
template <typename T>
EASY_FORCE_INLINE T unaligned_load16(const void* ptr)
{
static_assert(sizeof(T) == 2, "16 bit type required.");
#ifndef EASY_ENABLE_STRICT_ALIGNMENT
return *(const T*)ptr;
#else
T value;
((char*)&value)[0] = ((const char*)ptr)[0];
((char*)&value)[1] = ((const char*)ptr)[1];
return value;
#endif
}
template <typename T>
EASY_FORCE_INLINE T unaligned_load16(const void* ptr, T* val)
{
static_assert(sizeof(T) == 2, "16 bit type required.");
#ifndef EASY_ENABLE_STRICT_ALIGNMENT
*val = *(const T*)ptr;
#else
((char*)val)[0] = ((const char*)ptr)[0];
((char*)val)[1] = ((const char*)ptr)[1];
#endif
return *val;
}
template <typename T>
EASY_FORCE_INLINE T unaligned_load32(const void* ptr)
{
static_assert(sizeof(T) == 4, "32 bit type required.");
#ifndef EASY_ENABLE_STRICT_ALIGNMENT
return *(const T*)ptr;
#else
T value;
((char*)&value)[0] = ((const char*)ptr)[0];
((char*)&value)[1] = ((const char*)ptr)[1];
((char*)&value)[2] = ((const char*)ptr)[2];
((char*)&value)[3] = ((const char*)ptr)[3];
return value;
#endif
}
template <typename T>
EASY_FORCE_INLINE T unaligned_load32(const void* ptr, T* val)
{
static_assert(sizeof(T) == 4, "32 bit type required.");
#ifndef EASY_ENABLE_STRICT_ALIGNMENT
*val = *(const T*)ptr;
#else
((char*)&val)[0] = ((const char*)ptr)[0];
((char*)&val)[1] = ((const char*)ptr)[1];
((char*)&val)[2] = ((const char*)ptr)[2];
((char*)&val)[3] = ((const char*)ptr)[3];
#endif
return *val;
}
template <typename T>
EASY_FORCE_INLINE T unaligned_load64(const void* ptr)
{
static_assert(sizeof(T) == 8, "64 bit type required.");
#ifdef EASY_ENABLE_STRICT_ALIGNMENT
if (!is_aligned<alignof(T)>(ptr)) {
T value;
((char*)&value)[0] = ((const char*)ptr)[0];
((char*)&value)[1] = ((const char*)ptr)[1];
((char*)&value)[2] = ((const char*)ptr)[2];
((char*)&value)[3] = ((const char*)ptr)[3];
((char*)&value)[4] = ((const char*)ptr)[4];
((char*)&value)[5] = ((const char*)ptr)[5];
((char*)&value)[6] = ((const char*)ptr)[6];
((char*)&value)[7] = ((const char*)ptr)[7];
return value;
}
#endif
return *(const T*)ptr;
}
template <typename T>
EASY_FORCE_INLINE T unaligned_load64(const void* ptr, T* val)
{
static_assert(sizeof(T) == 8, "64 bit type required.");
#ifdef EASY_ENABLE_STRICT_ALIGNMENT
if (!is_aligned<alignof(T)>(ptr)) {
((char*)&val)[0] = ((const char*)ptr)[0];
((char*)&val)[1] = ((const char*)ptr)[1];
((char*)&val)[2] = ((const char*)ptr)[2];
((char*)&val)[3] = ((const char*)ptr)[3];
((char*)&val)[4] = ((const char*)ptr)[4];
((char*)&val)[5] = ((const char*)ptr)[5];
((char*)&val)[6] = ((const char*)ptr)[6];
((char*)&val)[7] = ((const char*)ptr)[7];
}
else
#endif
*val = *(const T*)ptr;
return *val;
}
#endif //EASY_PROFILER_ALIGNMENT_HELPERS_H
@@ -0,0 +1,59 @@
/**
Lightweight profiler library for c++
Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin
Licensed under either of
* MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
* Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
at your option.
The MIT License
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.
The Apache License, Version 2.0 (the "License");
You may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
**/
#include <easy/details/profiler_public_types.h>
namespace profiler
{
BaseBlockDescriptor::BaseBlockDescriptor(block_id_t _id, EasyBlockStatus _status, int _line,
block_type_t _block_type, color_t _color) EASY_NOEXCEPT
: m_id(_id)
, m_line(_line)
, m_type(_block_type)
, m_color(_color)
, m_status(_status)
{
}
} // end of namespace profiler.
+243
View File
@@ -0,0 +1,243 @@
/************************************************************************
* file name : block.cpp
* ----------------- :
* creation time : 2016/02/16
* authors : Sergey Yagovtsev, Victor Zarubkin
* emails : yse.sey@gmail.com, v.s.zarubkin@gmail.com
* ----------------- :
* description : The file contains implementation of profiling blocks
* :
* license : Lightweight profiler library for c++
* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin
* :
* : Licensed under either of
* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
* : at your option.
* :
* : The MIT License
* :
* : 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.
* :
* : The Apache License, Version 2.0 (the "License")
* :
* : You may not use this file except in compliance with the License.
* : You may obtain a copy of the License at
* :
* : http://www.apache.org/licenses/LICENSE-2.0
* :
* : Unless required by applicable law or agreed to in writing, software
* : distributed under the License is distributed on an "AS IS" BASIS,
* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* : See the License for the specific language governing permissions and
* : limitations under the License.
************************************************************************/
#include <easy/profiler.h>
#include "profile_manager.h"
#include "current_time.h"
namespace profiler {
#ifndef EASY_PROFILER_API_DISABLED
Event::Event(timestamp_t _begin_time) EASY_NOEXCEPT : m_begin(_begin_time), m_end(0)
{
}
Event::Event(timestamp_t _begin_time, timestamp_t _end_time) EASY_NOEXCEPT : m_begin(_begin_time), m_end(_end_time)
{
}
BaseBlockData::BaseBlockData(timestamp_t _begin_time, block_id_t _descriptor_id) EASY_NOEXCEPT
: Event(_begin_time)
, m_id(_descriptor_id)
{
}
BaseBlockData::BaseBlockData(timestamp_t _begin_time, timestamp_t _end_time, block_id_t _descriptor_id) EASY_NOEXCEPT
: Event(_begin_time, _end_time)
, m_id(_descriptor_id)
{
}
Block::Block(Block&& that) EASY_NOEXCEPT
: BaseBlockData(that.m_begin, that.m_id)
, m_name(that.m_name)
, m_status(that.m_status)
, m_isScoped(that.m_isScoped)
{
m_end = that.m_end;
that.m_end = that.m_begin;
}
Block::Block(timestamp_t _begin_time, block_id_t _descriptor_id, const char* _runtimeName) EASY_NOEXCEPT
: BaseBlockData(_begin_time, _descriptor_id)
, m_name(_runtimeName)
, m_status(::profiler::ON)
, m_isScoped(true)
{
}
Block::Block(timestamp_t _begin_time, timestamp_t _end_time, block_id_t _descriptor_id, const char* _runtimeName) EASY_NOEXCEPT
: BaseBlockData(_begin_time, _end_time, _descriptor_id)
, m_name(_runtimeName)
, m_status(::profiler::ON)
, m_isScoped(true)
{
}
Block::Block(const BaseBlockDescriptor* _descriptor, const char* _runtimeName, bool _scoped) EASY_NOEXCEPT
: BaseBlockData(1ULL, _descriptor->id())
, m_name(_runtimeName)
, m_status(_descriptor->status())
, m_isScoped(_scoped)
{
}
void Block::start()
{
m_begin = profiler::clock::now();
}
void Block::start(timestamp_t _time) EASY_NOEXCEPT
{
m_begin = _time;
}
void Block::finish()
{
m_end = profiler::clock::now();
}
void Block::finish(timestamp_t _time) EASY_NOEXCEPT
{
m_end = _time;
}
Block::~Block()
{
if (!finished())
::profiler::endBlock();
}
#else
Event::Event(timestamp_t) EASY_NOEXCEPT : m_begin(0), m_end(0)
{
}
Event::Event(timestamp_t, timestamp_t) EASY_NOEXCEPT : m_begin(0), m_end(0)
{
}
BaseBlockData::BaseBlockData(timestamp_t, block_id_t) EASY_NOEXCEPT
: Event(0, 0)
, m_id(~0U)
{
}
BaseBlockData::BaseBlockData(timestamp_t, timestamp_t, block_id_t) EASY_NOEXCEPT
: Event(0, 0)
, m_id(~0U)
{
}
Block::Block(Block&& that) EASY_NOEXCEPT
: BaseBlockData(0, ~0U)
, m_name("")
, m_status(::profiler::OFF)
, m_isScoped(that.m_isScoped)
{
}
Block::Block(timestamp_t, block_id_t, const char*) EASY_NOEXCEPT
: BaseBlockData(0, ~0U)
, m_name("")
, m_status(::profiler::OFF)
, m_isScoped(true)
{
}
Block::Block(timestamp_t, timestamp_t, block_id_t, const char*) EASY_NOEXCEPT
: BaseBlockData(0, ~0U)
, m_name("")
, m_status(::profiler::OFF)
, m_isScoped(true)
{
}
Block::Block(const BaseBlockDescriptor*, const char*, bool _scoped) EASY_NOEXCEPT
: BaseBlockData(0, ~0U)
, m_name("")
, m_status(::profiler::OFF)
, m_isScoped(_scoped)
{
}
void Block::start()
{
}
void Block::start(timestamp_t) EASY_NOEXCEPT
{
}
void Block::finish()
{
}
void Block::finish(timestamp_t) EASY_NOEXCEPT
{
}
Block::~Block()
{
}
#endif
//////////////////////////////////////////////////////////////////////////
CSwitchEvent::CSwitchEvent(timestamp_t _begin_time, thread_id_t _tid) EASY_NOEXCEPT
: Event(_begin_time)
, m_thread_id(_tid)
{
}
} // END of namespace profiler.
CSwitchBlock::CSwitchBlock(profiler::timestamp_t _begin_time, profiler::thread_id_t _tid, const char* _runtimeName) EASY_NOEXCEPT
: profiler::CSwitchEvent(_begin_time, _tid)
, m_name(_runtimeName)
{
}
//////////////////////////////////////////////////////////////////////////
+92
View File
@@ -0,0 +1,92 @@
/**
Lightweight profiler library for c++
Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin
Licensed under either of
* MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
* Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
at your option.
The MIT License
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.
The Apache License, Version 2.0 (the "License");
You may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
**/
#include <memory.h>
#include "block_descriptor.h"
#if EASY_BLOCK_DESC_FULL_COPY == 0
# define EASY_BLOCK_DESC_STRING_LEN(s) static_cast<uint16_t>(strlen(s) + 1)
# define EASY_BLOCK_DESC_STRING_VAL(s) s
#else
# define EASY_BLOCK_DESC_STRING_LEN(s) static_cast<uint16_t>(s.size() + 1)
# define EASY_BLOCK_DESC_STRING_VAL(s) s.c_str()
#endif
BlockDescriptor::BlockDescriptor(profiler::block_id_t _id, profiler::EasyBlockStatus _status, const char* _name,
const char* _filename, int _line, profiler::block_type_t _block_type,
profiler::color_t _color)
: Parent(_id, _status, _line, _block_type, _color)
, m_filename(_filename)
, m_name(_name)
{
}
const char* BlockDescriptor::name() const
{
return EASY_BLOCK_DESC_STRING_VAL(m_name);
}
const char* BlockDescriptor::filename() const
{
return EASY_BLOCK_DESC_STRING_VAL(m_filename);
}
uint16_t BlockDescriptor::nameSize() const
{
return EASY_BLOCK_DESC_STRING_LEN(m_name);
}
uint16_t BlockDescriptor::filenameSize() const
{
return EASY_BLOCK_DESC_STRING_LEN(m_filename);
}
void BlockDescriptor::destroy(BlockDescriptor* instance)
{
#if EASY_BLOCK_DESC_FULL_COPY == 0
if (instance != nullptr)
instance->~BlockDescriptor();
free(instance);
#else
delete instance;
#endif
}
+86
View File
@@ -0,0 +1,86 @@
/**
Lightweight profiler library for c++
Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin
Licensed under either of
* MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
* Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
at your option.
The MIT License
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.
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 EASY_PROFILER_BLOCK_DESCRIPTOR_H
#define EASY_PROFILER_BLOCK_DESCRIPTOR_H
#include <string>
#include <easy/details/profiler_public_types.h>
#ifndef EASY_BLOCK_DESC_FULL_COPY
# define EASY_BLOCK_DESC_FULL_COPY 1
#endif
class BlockDescriptor : public profiler::BaseBlockDescriptor
{
friend ProfileManager;
using Parent = profiler::BaseBlockDescriptor;
#if EASY_BLOCK_DESC_FULL_COPY == 0
using string_t = const char*;
#else
using string_t = std::string;
#endif
string_t m_filename; ///< Source file name where this block is declared
string_t m_name; ///< Static name of all blocks of the same type (blocks can have dynamic name) which is, in pair with descriptor id, a unique block identifier
public:
BlockDescriptor() = delete;
BlockDescriptor(const BlockDescriptor&) = delete;
BlockDescriptor& operator = (const BlockDescriptor&) = delete;
BlockDescriptor(profiler::block_id_t _id, profiler::EasyBlockStatus _status, const char* _name,
const char* _filename, int _line, profiler::block_type_t _block_type, profiler::color_t _color);
const char* name() const;
const char* filename() const;
uint16_t nameSize() const;
uint16_t filenameSize() const;
static void destroy(BlockDescriptor* instance);
}; // END of class BlockDescriptor.
#endif //EASY_PROFILER_BLOCK_DESCRIPTOR_H
+414
View File
@@ -0,0 +1,414 @@
/**
Lightweight profiler library for c++
Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin
Licensed under either of
* MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
* Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
at your option.
The MIT License
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.
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 EASY_PROFILER_CHUNK_ALLOCATOR_H
#define EASY_PROFILER_CHUNK_ALLOCATOR_H
#include <easy/details/easy_compiler_support.h>
#include <cstring>
#include <ostream>
#include "alignment_helpers.h"
//////////////////////////////////////////////////////////////////////////
#ifndef EASY_ENABLE_ALIGNMENT
# define EASY_ENABLE_ALIGNMENT 0
#endif
#ifndef EASY_ALIGNMENT_SIZE
# define EASY_ALIGNMENT_SIZE EASY_ALIGNOF(std::max_align_t)
#endif
EASY_CONSTEXPR auto EASY_ALIGN_SIZE = EASY_ALIGNMENT_SIZE;
#if EASY_ENABLE_ALIGNMENT == 0
# define EASY_ALIGNED(TYPE, VAR, A) TYPE VAR
# define EASY_MALLOC(MEMSIZE, A) malloc(MEMSIZE)
# define EASY_FREE(MEMPTR) free(MEMPTR)
#else
// MSVC and GNUC aligned versions of malloc are defined in malloc.h
# include <malloc.h>
# if defined(_MSC_VER)
# define EASY_ALIGNED(TYPE, VAR, A) __declspec(align(A)) TYPE VAR
# define EASY_MALLOC(MEMSIZE, A) _aligned_malloc(MEMSIZE, A)
# define EASY_FREE(MEMPTR) _aligned_free(MEMPTR)
# elif defined(__GNUC__)
# define EASY_ALIGNED(TYPE, VAR, A) TYPE VAR __attribute__((aligned(A)))
# define EASY_MALLOC(MEMSIZE, A) memalign(A, MEMSIZE)
# define EASY_FREE(MEMPTR) free(MEMPTR)
# else
# define EASY_ALIGNED(TYPE, VAR, A) TYPE VAR
# define EASY_MALLOC(MEMSIZE, A) malloc(MEMSIZE)
# define EASY_FREE(MEMPTR) free(MEMPTR)
# endif
#endif
//////////////////////////////////////////////////////////////////////////
template <const uint16_t N>
class chunk_allocator
{
static_assert(N != 0, "chunk_allocator<N> N must be a positive value");
struct chunk { EASY_ALIGNED(char, data[N], EASY_ALIGNMENT_SIZE); chunk* prev = nullptr; };
struct chunk_list
{
chunk* last;
chunk_list(const chunk_list&) = delete;
chunk_list(chunk_list&&) = delete;
chunk_list() : last(nullptr)
{
static_assert(sizeof(char) == 1, "easy_profiler logic error: sizeof(char) != 1 for this platform! Please, contact easy_profiler authors to resolve your problem.");
emplace_back();
}
~chunk_list()
{
do free_last(); while (last != nullptr);
}
void clear_all_except_last()
{
while (last->prev != nullptr)
free_last();
zero_last_chunk_size();
}
void emplace_back()
{
auto prev = last;
last = ::new (EASY_MALLOC(sizeof(chunk), EASY_ALIGNMENT_SIZE)) chunk();
last->prev = prev;
zero_last_chunk_size();
}
/** Invert current chunks list to enable to iterate over chunks list in direct order.
This method is used by serialize().
*/
void invert()
{
chunk* next = nullptr;
while (last->prev != nullptr) {
auto p = last->prev;
last->prev = next;
next = last;
last = p;
}
last->prev = next;
}
private:
void free_last()
{
auto p = last;
last = last->prev;
EASY_FREE(p);
}
void zero_last_chunk_size()
{
// Although there is no need for unaligned access stuff b/c a new chunk will
// usually be at least 8 byte aligned (and we only need 2 byte alignment),
// this is the only way I have been able to get rid of the GCC strict-aliasing warning
// without using std::memset. It's an extra line, but is just as fast as *(uint16_t*)last->data = 0;
char* const data = last->data;
*(uint16_t*)data = (uint16_t)0;
}
};
// Used in serialize(): workaround for no constexpr support in MSVC 2013.
EASY_STATIC_CONSTEXPR int_fast32_t MaxChunkOffset = N - sizeof(uint16_t);
EASY_STATIC_CONSTEXPR uint16_t OneBeforeN = static_cast<uint16_t>(N - 1);
chunk_list m_chunks; ///< List of chunks.
chunk* m_markedChunk; ///< Chunk marked by last closed frame
uint32_t m_size; ///< Number of elements stored(# of times allocate() has been called.)
uint32_t m_markedSize; ///< Number of elements to the moment when put_mark() has been called.
uint16_t m_chunkOffset; ///< Number of bytes used in the current chunk.
uint16_t m_markedChunkOffset; ///< Last byte in marked chunk for serializing.
public:
chunk_allocator(const chunk_allocator&) = delete;
chunk_allocator(chunk_allocator&&) = delete;
chunk_allocator() : m_markedChunk(nullptr), m_size(0), m_markedSize(0), m_chunkOffset(0), m_markedChunkOffset(0)
{
}
/** Allocate n bytes.
Automatically checks if there is enough preserved memory to store additional n bytes
and allocates additional buffer if needed.
*/
void* allocate(uint16_t n)
{
++m_size;
if (!need_expand(n))
{
// Temp to avoid extra load due to this* aliasing.
uint16_t chunkOffset = m_chunkOffset;
char* data = m_chunks.last->data + chunkOffset;
chunkOffset += n + sizeof(uint16_t);
m_chunkOffset = chunkOffset;
unaligned_store16(data, n);
data += sizeof(uint16_t);
// If there is enough space for at least another payload size,
// set it to zero.
if (chunkOffset < OneBeforeN)
unaligned_zero16(data + n);
return data;
}
m_chunkOffset = n + sizeof(uint16_t);
m_chunks.emplace_back();
char* data = m_chunks.last->data;
unaligned_store16(data, n);
data += sizeof(uint16_t);
// We assume here that it takes more than one element to fill a chunk.
unaligned_zero16(data + n);
return data;
}
/** Check if current storage is not enough to store additional n bytes.
*/
bool need_expand(uint16_t n) const
{
return (m_chunkOffset + n + sizeof(uint16_t)) > N;
}
uint32_t size() const
{
return m_size;
}
bool empty() const
{
return m_size == 0;
}
uint32_t markedSize() const
{
return m_markedSize;
}
bool markedEmpty() const
{
return m_markedSize == 0;
}
void clear()
{
m_size = 0;
m_markedSize = 0;
m_chunkOffset = 0;
m_markedChunk = nullptr;
m_chunks.clear_all_except_last(); // There is always at least one chunk
}
/** Serialize data to stream.
\warning Data will be cleared after serialization.
*/
void serialize(std::ostream& _outputStream)
{
// Chunks are stored in reversed order (stack).
// To be able to iterate them in direct order we have to invert the chunks list.
m_chunks.invert();
// Each chunk is an array of N bytes that can hold between
// 1(if the list isn't empty) and however many elements can fit in a chunk,
// where an element consists of a payload size + a payload as follows:
// elementStart[0..1]: size as a uint16_t
// elementStart[2..size-1]: payload.
// The maximum chunk offset is N-sizeof(uint16_t) b/c, if we hit that (or go past),
// there is either no space left, 1 byte left, or 2 bytes left, all of which are
// too small to cary more than a zero-sized element.
chunk* current = m_chunks.last;
bool isMarked;
do {
isMarked = (current == m_markedChunk);
const char* data = current->data;
const int_fast32_t maxOffset = isMarked ? m_markedChunkOffset : MaxChunkOffset;
int_fast32_t chunkOffset = 0; // signed int so overflow is not checked.
auto payloadSize = unaligned_load16<uint16_t>(data);
while (chunkOffset < maxOffset && payloadSize != 0)
{
const uint16_t chunkSize = sizeof(uint16_t) + payloadSize;
_outputStream.write(data, chunkSize);
data += chunkSize;
chunkOffset += chunkSize;
unaligned_load16(data, &payloadSize);
}
current = current->prev;
} while (current != nullptr && !isMarked);
clear();
}
void put_mark()
{
m_markedChunk = m_chunks.last;
m_markedSize = m_size;
m_markedChunkOffset = m_chunkOffset;
}
void* marked_allocate(uint16_t n)
{
chunk* marked = m_markedChunk;
if (marked == nullptr || (marked == m_chunks.last && m_markedSize == m_size))
{
auto data = allocate(n);
put_mark();
return data;
}
++m_markedSize;
uint16_t chunkOffset = m_markedChunkOffset;
if ((chunkOffset + n + sizeof(uint16_t)) <= N)
{
// Temp to avoid extra load due to this* aliasing.
char* data = marked->data + chunkOffset;
chunkOffset += n + sizeof(uint16_t);
m_markedChunkOffset = chunkOffset;
unaligned_store16(data, n);
data += sizeof(uint16_t);
// If there is enough space for at least another payload size,
// set it to zero.
if (chunkOffset < OneBeforeN)
unaligned_zero16(data + n);
if (marked == m_chunks.last && chunkOffset > m_chunkOffset)
m_chunkOffset = chunkOffset;
return data;
}
chunkOffset = n + sizeof(uint16_t);
m_markedChunkOffset = chunkOffset;
chunk* last = m_chunks.last;
if (marked == last)
{
m_chunks.emplace_back();
last = m_chunks.last;
m_chunkOffset = chunkOffset;
m_size = m_markedSize;
}
else
{
do last = last->prev; while (last->prev != m_markedChunk);
}
m_markedChunk = last;
char* data = last->data;
unaligned_store16(data, n);
data += sizeof(uint16_t);
// We assume here that it takes more than one element to fill a chunk.
unaligned_zero16(data + n);
return data;
}
}; // END of class chunk_allocator.
//////////////////////////////////////////////////////////////////////////
template <const uint16_t N, bool, bool>
struct aligned_size;
template <const uint16_t N, bool dummy>
struct aligned_size<N, true, dummy> {
EASY_STATIC_CONSTEXPR uint16_t Size = N;
};
template <const uint16_t N>
struct aligned_size<N, false, true> {
EASY_STATIC_CONSTEXPR uint16_t Size = static_cast<uint16_t>(N - (N % EASY_ALIGN_SIZE));
};
template <const uint16_t N>
struct aligned_size<N, false, false> {
EASY_STATIC_CONSTEXPR uint16_t Size = static_cast<uint16_t>(N + EASY_ALIGN_SIZE - (N % EASY_ALIGN_SIZE));
};
template <const size_t N>
struct get_aligned_size {
EASY_STATIC_CONSTEXPR uint16_t Size =
aligned_size<static_cast<uint16_t>(N), (N % EASY_ALIGN_SIZE) == 0, (N > (65536 - EASY_ALIGN_SIZE))>::Size;
};
static_assert(get_aligned_size<EASY_ALIGN_SIZE - 3>::Size == EASY_ALIGN_SIZE, "wrong get_aligned_size");
static_assert(get_aligned_size<2 * EASY_ALIGN_SIZE - 3>::Size == 2 * EASY_ALIGN_SIZE, "wrong get_aligned_size");
static_assert(get_aligned_size<65530>::Size == 65536 - EASY_ALIGN_SIZE, "wrong get_aligned_size");
static_assert(get_aligned_size<65526>::Size == 65536 - EASY_ALIGN_SIZE, "wrong get_aligned_size");
static_assert(get_aligned_size<65536 - EASY_ALIGN_SIZE>::Size == 65536 - EASY_ALIGN_SIZE, "wrong get_aligned_size");
static_assert(get_aligned_size<65536 + 3 - EASY_ALIGN_SIZE * 2>::Size == 65536 - EASY_ALIGN_SIZE, "wrong get_aligned_size");
//////////////////////////////////////////////////////////////////////////
#endif // EASY_PROFILER_CHUNK_ALLOCATOR_H
+86
View File
@@ -0,0 +1,86 @@
/**
Lightweight profiler library for c++
Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin
Licensed under either of
* MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
* Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
at your option.
The MIT License
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.
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 EASY_PROFILER_CURRENT_THREAD_H
#define EASY_PROFILER_CURRENT_THREAD_H
#include <easy/details/profiler_public_types.h>
#ifdef _WIN32
# include <Windows.h>
#elif defined(__APPLE__)
# include <pthread.h>
# include <Availability.h>
#elif defined(__QNX__)
# include <sys/mman.h>
# include <process.h>
# define __NR_gettid SYS_gettid
#else
# include <sys/types.h>
# include <unistd.h>
# include <sys/syscall.h>
#endif
inline profiler::thread_id_t getCurrentThreadId()
{
#ifdef _WIN32
return (profiler::thread_id_t)::GetCurrentThreadId();
#elif defined(__APPLE__)
# if (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_6) || \
(defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_8_0)
static EASY_THREAD_LOCAL uint64_t _id = 0;
if (!_id)
pthread_threadid_np(NULL, &_id);
return (profiler::thread_id_t)_id;
# else
return (profiler::thread_id_t)pthread_self();
# endif
#elif defined(__QNX__)
EASY_THREAD_LOCAL static const profiler::thread_id_t _id = (profiler::thread_id_t)gettid();
return _id;
#else
EASY_THREAD_LOCAL static const profiler::thread_id_t _id = (profiler::thread_id_t)syscall(__NR_gettid);
return _id;
#endif
}
#endif // EASY_PROFILER_CURRENT_THREAD_H
+180
View File
@@ -0,0 +1,180 @@
/**
Lightweight profiler library for c++
Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin
Licensed under either of
* MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
* Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
at your option.
The MIT License
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.
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 EASY_PROFILER_CURRENT_TIME_H
#define EASY_PROFILER_CURRENT_TIME_H
#include <easy/details/profiler_public_types.h>
#if defined(_MSC_VER) && _MSC_VER <= 1800
// std::chrono for MSVC2013 is broken - it has very low resolution of 16ms
// restrict usage of std::chrono
# if EASY_CHRONO_HIGHRES_CLOCK
# undef EASY_CHRONO_HIGHRES_CLOCK
# endif
# if EASY_CHRONO_STEADY_CLOCK
# undef EASY_CHRONO_STEADY_CLOCK
# endif
#endif
#if EASY_CHRONO_HIGHRES_CLOCK
# include <chrono>
# define EASY_CHRONO_CLOCK std::chrono::high_resolution_clock
#elif EASY_CHRONO_STEADY_CLOCK
# include <chrono>
# define EASY_CHRONO_CLOCK std::chrono::steady_clock
#elif defined(_WIN32)
# include <Windows.h>
#else
# include <chrono>
# include <time.h>
# ifdef __ARM_ARCH
# include <sys/time.h>
# endif//__ARM_ARCH
#endif
# ifdef __mips__
# include <sys/time.h>
# endif//__mips__
namespace profiler { namespace clock {
static inline profiler::timestamp_t now()
{
#if EASY_CHRONO_HIGHRES_CLOCK || EASY_CHRONO_STEADY_CLOCK
return (profiler::timestamp_t)EASY_CHRONO_CLOCK::now().time_since_epoch().count();
#elif defined(_WIN32)
//see https://msdn.microsoft.com/library/windows/desktop/dn553408(v=vs.85).aspx
LARGE_INTEGER elapsedMicroseconds;
if (!QueryPerformanceCounter(&elapsedMicroseconds))
return 0;
return (profiler::timestamp_t)elapsedMicroseconds.QuadPart;
#else// not _WIN32
#if (defined(__GNUC__) || defined(__ICC))
// part of code from google/benchmark library (Licensed under the Apache License, Version 2.0)
// see https://github.com/google/benchmark/blob/master/src/cycleclock.h#L111
#if defined(__i386__)
int64_t ret;
__asm__ volatile("rdtsc" : "=A"(ret));
return ret;
#elif defined(__x86_64__) || defined(__amd64__)
uint64_t low, high;
__asm__ volatile("rdtsc" : "=a"(low), "=d"(high));
return (high << 32) | low;
#elif defined(__powerpc__) || defined(__ppc__)
// This returns a time-base, which is not always precisely a cycle-count.
int64_t tbl, tbu0, tbu1;
asm("mftbu %0" : "=r"(tbu0));
asm("mftb %0" : "=r"(tbl));
asm("mftbu %0" : "=r"(tbu1));
tbl &= -static_cast<int64>(tbu0 == tbu1);
// high 32 bits in tbu1; low 32 bits in tbl (tbu0 is garbage)
return (tbu1 << 32) | tbl;
#elif defined(__sparc__)
int64_t tick;
asm(".byte 0x83, 0x41, 0x00, 0x00");
asm("mov %%g1, %0" : "=r"(tick));
return tick;
#elif defined(__ia64__)
int64_t itc;
asm("mov %0 = ar.itc" : "=r"(itc));
return itc;
#elif defined(_MSC_VER) && defined(_M_IX86)
// Older MSVC compilers (like 7.x) don't seem to support the
// __rdtsc intrinsic properly, so I prefer to use _asm instead
// when I know it will work. Otherwise, I'll use __rdtsc and hope
// the code is being compiled with a non-ancient compiler.
_asm rdtsc
#elif defined(_MSC_VER)
return __rdtsc();
#elif defined(__aarch64__)
// System timer of ARMv8 runs at a different frequency than the CPU's.
// The frequency is fixed, typically in the range 1-50MHz. It can be
// read at CNTFRQ special register. We assume the OS has set up
// the virtual timer properly.
int64_t virtual_timer_value;
asm volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer_value));
return virtual_timer_value;
#elif defined(__ARM_ARCH)
#if (__ARM_ARCH >= 6) // V6 is the earliest arch that has a standard cyclecount
uint32_t pmccntr;
uint32_t pmuseren;
uint32_t pmcntenset;
// Read the user mode perf monitor counter access permissions.
asm volatile("mrc p15, 0, %0, c9, c14, 0" : "=r"(pmuseren));
if (pmuseren & 1) { // Allows reading perfmon counters for user mode code.
asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r"(pmcntenset));
if (pmcntenset & 0x80000000ul) { // Is it counting?
asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(pmccntr));
// The counter is set up to count every 64th cycle
return static_cast<int64_t>(pmccntr) * 64; // Should optimize to << 6
}
}
#endif
struct timeval tv;
gettimeofday(&tv, nullptr);
return static_cast<int64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
#elif defined(__mips__)
// mips apparently only allows rdtsc for superusers, so we fall
// back to gettimeofday. It's possible clock_gettime would be better.
struct timeval tv;
gettimeofday(&tv, nullptr);
return static_cast<int64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
#else
#warning You need to define fast now() for your OS and CPU
return std::chrono::high_resolution_clock::now().time_since_epoch().count();
#define EASY_CHRONO_CLOCK std::chrono::high_resolution_clock
#endif
#else // not _WIN32, __GNUC__, __ICC
#warning You need to define fast now() for your OS and CPU
return std::chrono::high_resolution_clock::now().time_since_epoch().count();
#define EASY_CHRONO_CLOCK std::chrono::high_resolution_clock
#endif
#endif
}
} } // end of namespace profiler::clock.
#endif // EASY_PROFILER_CURRENT_TIME_H
+438
View File
@@ -0,0 +1,438 @@
/**
Lightweight profiler library for c++
Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin
Licensed under either of
* MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
* Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
at your option.
The MIT License
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.
The Apache License, Version 2.0 (the "License");
You may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
**/
#include <easy/easy_socket.h>
#include <string.h>
#include <thread>
#include <limits>
#if defined(__QNX__)
# include <sys/time.h>
#endif
#if defined(_WIN32)
# pragma comment (lib, "Ws2_32.lib")
# pragma comment (lib, "Mswsock.lib")
# pragma comment (lib, "AdvApi32.lib")
# ifdef max
# undef max
# endif
#else
# include <errno.h>
# include <sys/ioctl.h>
#endif
/////////////////////////////////////////////////////////////////
#if defined(_WIN32)
const int SOCK_ABORTED = WSAECONNABORTED;
const int SOCK_RESET = WSAECONNRESET;
const int SOCK_IN_PROGRESS = WSAEINPROGRESS;
#else
const int SOCK_ABORTED = ECONNABORTED;
const int SOCK_RESET = ECONNRESET;
const int SOCK_IN_PROGRESS = EINPROGRESS;
const int SOCK_BROKEN_PIPE = EPIPE;
const int SOCK_ENOENT = ENOENT;
#endif
const int SEND_BUFFER_SIZE = 64 * 1024 * 1024;
/////////////////////////////////////////////////////////////////
static int closeSocket(EasySocket::socket_t s)
{
#if defined(_WIN32)
return ::closesocket(s);
#else
return ::close(s);
#endif
}
/////////////////////////////////////////////////////////////////
bool EasySocket::checkSocket(socket_t s) const
{
return s > 0;
}
void EasySocket::setBlocking(EasySocket::socket_t s, bool blocking)
{
#if defined(_WIN32)
u_long iMode = blocking ? 0 : 1;//0 - blocking, 1 - non blocking
::ioctlsocket(s, FIONBIO, &iMode);
#else
int iMode = blocking ? 0 : 1;//0 - blocking, 1 - non blocking
::ioctl(s, FIONBIO, (char*)&iMode);
#endif
}
int EasySocket::bind(uint16_t port)
{
if (!checkSocket(m_socket))
return -1;
struct sockaddr_in server_address;
memset(&server_address, 0, sizeof(server_address));
server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = INADDR_ANY;
server_address.sin_port = htons(port);
auto res = ::bind(m_socket, (struct sockaddr *)&server_address, sizeof(server_address));
return res;
}
void EasySocket::flush()
{
if (m_socket)
closeSocket(m_socket);
if (m_replySocket != m_socket && m_replySocket != 0) {
// we do not need to close uninitialized reply socket
closeSocket(m_replySocket);
}
#if defined(_WIN32)
m_socket = 0;
m_replySocket = 0;
#else
m_wsaret = 0;
#endif
}
void EasySocket::checkResult(int result)
{
if (result >= 0)
{
m_state = ConnectionState::Connected;
return;
}
if (result == -1) // is this check necessary?
{
#if defined(_WIN32)
const int error_code = WSAGetLastError();
#else
const int error_code = errno;
#endif
switch (error_code)
{
case SOCK_ABORTED:
case SOCK_RESET:
#if !defined(_WIN32)
case SOCK_BROKEN_PIPE:
case SOCK_ENOENT:
#endif
#if defined (__APPLE__)
case ECONNREFUSED:
#endif
m_state = ConnectionState::Disconnected;
break;
case SOCK_IN_PROGRESS:
m_state = ConnectionState::Connecting;
break;
default:
break;
}
}
}
void EasySocket::init()
{
if (m_wsaret != 0)
return;
#if !defined(_WIN32)
const int protocol = 0;
#else
const int protocol = IPPROTO_TCP;
#endif
m_socket = ::socket(AF_INET, SOCK_STREAM, protocol);
if (!checkSocket(m_socket))
return;
setBlocking(m_socket, true);
#if !defined(_WIN32)
m_wsaret = 1;
#endif
const int opt = 1;
::setsockopt(m_socket, SOL_SOCKET, SO_REUSEADDR, (const char*)&opt, sizeof(int));
}
EasySocket::ConnectionState EasySocket::state() const
{
return m_state;
}
bool EasySocket::isDisconnected() const
{
return static_cast<int>(m_state) <= 0;
}
bool EasySocket::isConnected() const
{
return m_state == ConnectionState::Connected;
}
EasySocket::EasySocket()
{
#if defined(_WIN32)
WSADATA wsaData;
m_wsaret = WSAStartup(0x101, &wsaData);
#else
m_wsaret = 0;
#endif
init();
}
EasySocket::~EasySocket()
{
flush();
#if defined(_WIN32)
if (m_wsaret == 0)
WSACleanup();
#endif
}
void EasySocket::setReceiveTimeout(int milliseconds)
{
if (!isConnected() || !checkSocket(m_replySocket))
return;
if (milliseconds <= 0)
milliseconds = std::numeric_limits<int>::max() / 1000;
#if defined(_WIN32)
const DWORD timeout = static_cast<DWORD>(milliseconds);
::setsockopt(m_replySocket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(DWORD));
#else
struct timeval tv;
if (milliseconds >= 1000)
{
const int s = milliseconds / 1000;
const int us = (milliseconds - s * 1000) * 1000;
tv.tv_sec = s;
tv.tv_usec = us;
}
else
{
tv.tv_sec = 0;
tv.tv_usec = milliseconds * 1000;
}
::setsockopt(m_replySocket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof (struct timeval));
#endif
}
int EasySocket::send(const void* buffer, size_t nbytes)
{
if (!checkSocket(m_replySocket))
return -1;
#if defined(_WIN32) || defined(__APPLE__)
const int res = ::send(m_replySocket, (const char*)buffer, (int)nbytes, 0);
#else
const int res = (int)::send(m_replySocket, buffer, nbytes, MSG_NOSIGNAL);
#endif
checkResult(res);
return res;
}
int EasySocket::receive(void* buffer, size_t nbytes)
{
if (!checkSocket(m_replySocket))
return -1;
#if defined(_WIN32)
const int res = ::recv(m_replySocket, (char*)buffer, (int)nbytes, 0);
#else
const int res = (int)::read(m_replySocket, buffer, nbytes);
#endif
checkResult(res);
if (res == 0)
m_state = ConnectionState::Disconnected;
return res;
}
int EasySocket::listen(int count)
{
if (!checkSocket(m_socket))
return -1;
const int res = ::listen(m_socket, count);
checkResult(res);
return res;
}
int EasySocket::accept()
{
if (!checkSocket(m_socket))
return -1;
fd_set fdread;
FD_ZERO (&fdread);
FD_SET (m_socket, &fdread);
fd_set fdwrite = fdread;
fd_set fdexcl = fdread;
struct timeval tv { 0, 500 };
const int rc = ::select((int)m_socket + 1, &fdread, &fdwrite, &fdexcl, &tv);
if (rc <= 0)
return -1; // there is no connection for accept
m_replySocket = ::accept(m_socket, nullptr, nullptr);
checkResult((int)m_replySocket);
if (checkSocket(m_replySocket))
{
::setsockopt(m_replySocket, SOL_SOCKET, SO_SNDBUF, (char*)&SEND_BUFFER_SIZE, sizeof(int));
//const int flag = 1;
//const int result = setsockopt(m_replySocket,IPPROTO_TCP,TCP_NODELAY,(char *)&flag,sizeof(int));
#if defined(__APPLE__)
// Apple doesn't have MSG_NOSIGNAL, work around it
const int value = 1;
::setsockopt(m_replySocket, SOL_SOCKET, SO_NOSIGPIPE, &value, sizeof(value));
#endif
//setBlocking(m_replySocket,true);
}
return (int)m_replySocket;
}
bool EasySocket::setAddress(const char* address, uint16_t port)
{
m_server = ::gethostbyname(address);
if (m_server == nullptr)
return false;
memset((char*)&m_serverAddress, 0, sizeof(m_serverAddress));
m_serverAddress.sin_family = AF_INET;
memcpy((char*)&m_serverAddress.sin_addr.s_addr, (char*)m_server->h_addr, (size_t)m_server->h_length);
m_serverAddress.sin_port = htons(port);
return true;
}
int EasySocket::connect()
{
if (m_server == nullptr || m_socket <= 0)
return -1;
int res = 0;
//TODO: more intelligence
#if !defined(_WIN32)
setBlocking(m_socket, false);
const int sleepMs = 20;
const int waitSec = 1;
const int waitMs = waitSec * 1000 / sleepMs;
for (int counter = 0; counter++ < waitMs;)
{
res = ::connect(m_socket, (struct sockaddr*)&m_serverAddress, sizeof(m_serverAddress));
#if defined(__APPLE__)
// on Apple, treat EISCONN error as success
if (res == -1 && errno == EISCONN)
{
res = 0;
break;
}
#endif
checkResult(res);
if (res == 0)
{
break;
}
if (m_state == ConnectionState::Connecting)
{
std::this_thread::sleep_for(std::chrono::milliseconds(sleepMs));
continue;
}
if (isDisconnected())
{
break;
}
}
setBlocking(m_socket, true);
#else
res = ::connect(m_socket, (struct sockaddr*)&m_serverAddress, sizeof(m_serverAddress));
checkResult(res);
#endif
if (res == 0)
{
struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 0;
::setsockopt(m_socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(struct timeval));
#if defined(__APPLE__)
// Apple doesn't have MSG_NOSIGNAL, work around it
const int value = 1;
setsockopt(m_socket, SOL_SOCKET, SO_NOSIGPIPE, &value, sizeof(value));
#endif
m_replySocket = m_socket;
}
return res;
}
+23
View File
@@ -0,0 +1,23 @@
#ifndef EASY_PROFILER__EVENT_TRACE_STATUS__H_
#define EASY_PROFILER__EVENT_TRACE_STATUS__H_
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
enum class EventTracingEnableStatus : unsigned char
{
LaunchedSuccessfully = 0,
PermissionDenied,
AlreadyLaunched,
BadPropertiesSize,
OpenTraceFailed,
UnknownError,
};
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
#endif // EASY_PROFILER__EVENT_TRACE_STATUS__H_
+587
View File
@@ -0,0 +1,587 @@
/************************************************************************
* file name : event_trace_win.cpp
* ----------------- :
* creation time : 2016/09/04
* author : Victor Zarubkin
* email : v.s.zarubkin@gmail.com
* ----------------- :
* description : The file contains implementation of EasyEventTracer class used for tracing
* : Windows system events to get context switches.
* ----------------- :
* change log : * 2016/09/04 Victor Zarubkin: initial commit.
* :
* : * 2016/09/13 Victor Zarubkin: get process id and process name
* : of the owner of thread with id == CSwitch::NewThreadId.
* :
* : * 2016/09/17 Victor Zarubkin: added log messages printing.
* ----------------- :
* license : Lightweight profiler library for c++
* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin
* :
* : Licensed under either of
* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
* : at your option.
* :
* : The MIT License
* :
* : 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.
* :
* : 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.
************************************************************************/
#ifdef _WIN32
#include <memory.h>
#include <chrono>
#include <unordered_map>
#include <easy/profiler.h>
#include "profile_manager.h"
#include "current_time.h"
#include "event_trace_win.h"
#include <Psapi.h>
#ifdef __MINGW32__
#include <processthreadsapi.h>
#endif
//#include <Shellapi.h>
#if EASY_OPTION_LOG_ENABLED != 0
# include <iostream>
# ifndef EASY_ERRORLOG
# define EASY_ERRORLOG std::cerr
# endif
# ifndef EASY_LOG
# define EASY_LOG std::cerr
# endif
# ifndef EASY_ERROR
# define EASY_ERROR(LOG_MSG) EASY_ERRORLOG << "EasyProfiler ERROR: " << LOG_MSG
# endif
# ifndef EASY_WARNING
# define EASY_WARNING(LOG_MSG) EASY_ERRORLOG << "EasyProfiler WARNING: " << LOG_MSG
# endif
# ifndef EASY_LOGMSG
# define EASY_LOGMSG(LOG_MSG) EASY_LOG << "EasyProfiler INFO: " << LOG_MSG
# endif
# ifndef EASY_LOG_ONLY
# define EASY_LOG_ONLY(CODE) CODE
# endif
#else
# ifndef EASY_ERROR
# define EASY_ERROR(LOG_MSG)
# endif
# ifndef EASY_WARNING
# define EASY_WARNING(LOG_MSG)
# endif
# ifndef EASY_LOGMSG
# define EASY_LOGMSG(LOG_MSG)
# endif
# ifndef EASY_LOG_ONLY
# define EASY_LOG_ONLY(CODE)
# endif
#endif
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
extern const profiler::color_t EASY_COLOR_INTERNAL_EVENT;
#ifdef __MINGW32__
static std::atomic<uint64_t> TRACING_END_TIME = ATOMIC_VAR_INIT(~0ULL);
static char KERNEL_LOGGER[] = KERNEL_LOGGER_NAME;
#else
static std::atomic_uint64_t TRACING_END_TIME = ATOMIC_VAR_INIT(~0ULL);
#endif
/**
* Retrieve the process name of the given process.
*
* This method is NOT thread-safe: the returned string has to be copied somewhere before this
* method can be used by another thread or call.
*
* getProcessName() owns the returned string.
*
* \param len the process name string length [output].
*
* \return the process name string.
*/
static const char* getProcessName(HANDLE hProcess, std::size_t& len)
{
static TCHAR buf[MAX_PATH] = {};
len = static_cast<std::size_t>(GetModuleBaseName(hProcess, 0, buf, MAX_PATH));
if (len == 0)
return nullptr;
#if UNICODE
static char charbuf[MAX_PATH] = {};
len = std::wcstombs(charbuf, buf, len);
return charbuf;
#else
return buf;
#endif
}
EASY_CONSTEXPR decltype(EVENT_DESCRIPTOR::Opcode) SWITCH_CONTEXT_OPCODE = 36;
EASY_CONSTEXPR int RAW_TIMESTAMP_TIME_TYPE = 1;
//////////////////////////////////////////////////////////////////////////
struct ProcessInfo {
std::string name;
processid_t id = 0;
int8_t valid = 0;
};
//////////////////////////////////////////////////////////////////////////
// CSwitch class
// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa964744(v=vs.85).aspx
// EventType = 36
struct CSwitch
{
uint32_t NewThreadId;
uint32_t OldThreadId;
int8_t NewThreadPriority;
int8_t OldThreadPriority;
uint8_t PreviousCState;
int8_t SpareByte;
int8_t OldThreadWaitReason;
int8_t OldThreadWaitMode;
int8_t OldThreadState;
int8_t OldThreadWaitIdealProcessor;
uint32_t NewThreadWaitTime;
uint32_t Reserved;
};
//////////////////////////////////////////////////////////////////////////
struct do_not_calc_hash {
template <class T> inline size_t operator()(T _value) const {
return static_cast<size_t>(_value);
}
};
using thread_process_info_map = std::unordered_map<decltype(CSwitch::NewThreadId), ProcessInfo*, do_not_calc_hash>;
using process_info_map = std::unordered_map<processid_t, ProcessInfo, do_not_calc_hash>;
// Using static is safe because easyProcessTraceEvent() is called from one thread
static process_info_map PROCESS_INFO_TABLE;
static thread_process_info_map THREAD_PROCESS_INFO_TABLE;
//////////////////////////////////////////////////////////////////////////
void WINAPI easyProcessTraceEvent(PEVENT_RECORD _traceEvent)
{
if (_traceEvent->EventHeader.EventDescriptor.Opcode != SWITCH_CONTEXT_OPCODE)
return;
if (sizeof(CSwitch) != _traceEvent->UserDataLength)
return;
EASY_FUNCTION(EASY_COLOR_INTERNAL_EVENT, profiler::OFF);
auto _contextSwitchEvent = reinterpret_cast<CSwitch*>(_traceEvent->UserData);
const auto time = static_cast<profiler::timestamp_t>(_traceEvent->EventHeader.TimeStamp.QuadPart);
if (time > TRACING_END_TIME.load(std::memory_order_acquire))
return;
DWORD pid = 0;
const char* process_name = "";
// Trying to get target process name and id
auto it = THREAD_PROCESS_INFO_TABLE.find(_contextSwitchEvent->NewThreadId);
if (it == THREAD_PROCESS_INFO_TABLE.end())
{
auto hThread = OpenThread(THREAD_QUERY_LIMITED_INFORMATION, FALSE, _contextSwitchEvent->NewThreadId);
if (hThread != nullptr)
{
pid = GetProcessIdOfThread(hThread);
auto pinfo = &PROCESS_INFO_TABLE[pid];
if (pinfo->valid == 0)
{
if (pinfo->name.empty())
{
static char numbuf[128] = {};
sprintf(numbuf, "%u", pid);
pinfo->name = numbuf;
pinfo->id = pid;
}
/*
According to documentation, using GetModuleBaseName() requires
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ access rights.
But it works fine with PROCESS_QUERY_LIMITED_INFORMATION instead of PROCESS_QUERY_INFORMATION.
See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683196(v=vs.85).aspx
*/
//auto hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid);
//if (hProc == nullptr)
auto hProc = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, FALSE, pid);
if (hProc != nullptr)
{
std::size_t len = 0;
auto processName = getProcessName(hProc, len); // Using thread-unsafe method is safe because processTraceEvent() is called from one thread
if (len != 0)
{
pinfo->name.reserve(pinfo->name.size() + 2 + len);
pinfo->name.append(" ", 1);
pinfo->name.append(processName, len);
pinfo->valid = 1;
}
CloseHandle(hProc);
}
else
{
//auto err = GetLastError();
//printf("OpenProcess(%u) fail: GetLastError() == %u\n", pid, err);
pinfo->valid = -1;
if (pid == 4) {
pinfo->name.reserve(pinfo->name.size() + 8);
pinfo->name.append(" System", 7);
}
}
}
process_name = pinfo->name.c_str();
THREAD_PROCESS_INFO_TABLE[_contextSwitchEvent->NewThreadId] = pinfo;
CloseHandle(hThread);
}
else
{
//printf("Can not OpenThread(%u);\n", _contextSwitchEvent->NewThreadId);
THREAD_PROCESS_INFO_TABLE[_contextSwitchEvent->NewThreadId] = nullptr;
}
}
else
{
auto pinfo = it->second;
if (pinfo != nullptr)
process_name = pinfo->name.c_str();
else if (it->first == 0)
process_name = "System Idle";
else if (it->first == 4)
process_name = "System";
}
ProfileManager::instance().beginContextSwitch(_contextSwitchEvent->OldThreadId, time, _contextSwitchEvent->NewThreadId, process_name);
ProfileManager::instance().endContextSwitch(_contextSwitchEvent->NewThreadId, pid, time);
}
//////////////////////////////////////////////////////////////////////////
EasyEventTracer::Properties::Properties()
{
#if UNICODE
std::wcstombs(sessionName, KERNEL_LOGGER_NAME, sizeof(sessionName));
#else
std::strncpy(sessionName, KERNEL_LOGGER_NAME, sizeof(sessionName));
#endif
}
//////////////////////////////////////////////////////////////////////////
#ifndef EASY_MAGIC_STATIC_AVAILABLE
class EasyEventTracerInstance {
friend EasyEventTracer;
EasyEventTracer instance;
} EASY_EVENT_TRACER;
#endif
EasyEventTracer& EasyEventTracer::instance()
{
#ifndef EASY_MAGIC_STATIC_AVAILABLE
return EASY_EVENT_TRACER.instance;
#else
static EasyEventTracer tracer;
return tracer;
#endif
}
EasyEventTracer::EasyEventTracer()
{
m_lowPriority = ATOMIC_VAR_INIT(EASY_OPTION_LOW_PRIORITY_EVENT_TRACING);
THREAD_PROCESS_INFO_TABLE[0U] = nullptr;
}
EasyEventTracer::~EasyEventTracer()
{
disable();
}
bool EasyEventTracer::isLowPriority() const
{
return m_lowPriority.load(std::memory_order_acquire);
}
void EasyEventTracer::setLowPriority(bool _value)
{
m_lowPriority.store(_value, std::memory_order_release);
}
bool setPrivilege(HANDLE hToken, PTCHAR _privelegeName)
{
bool success = false;
if (hToken)
{
LUID privilegyId;
if (LookupPrivilegeValue(NULL, _privelegeName, &privilegyId))
{
TOKEN_PRIVILEGES tokenPrivilege;
tokenPrivilege.PrivilegeCount = 1;
tokenPrivilege.Privileges[0].Luid = privilegyId;
tokenPrivilege.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
success = AdjustTokenPrivileges(hToken, FALSE, &tokenPrivilege, sizeof(TOKEN_PRIVILEGES), NULL, NULL) != FALSE;
}
}
EASY_LOG_ONLY(
if (!success)
EASY_WARNING("Failed to set " << _privelegeName << " privilege for the application.\n");
)
return success;
}
void EasyEventTracer::setProcessPrivileges()
{
static bool alreadySet = false;
if (alreadySet)
return;
alreadySet = true;
HANDLE hToken = nullptr;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
{
#if EASY_OPTION_LOG_ENABLED != 0
const bool success = setPrivilege(hToken, SE_DEBUG_NAME);
if (!success)
EASY_WARNING("Some context switch events could not get process name.\n");
#else
setPrivilege(hToken, SE_DEBUG_NAME);
#endif
CloseHandle(hToken);
}
EASY_LOG_ONLY(
else {
EASY_WARNING("Failed to open process to adjust privileges.\n");
}
)
}
EventTracingEnableStatus EasyEventTracer::startTrace(bool _force, int _step)
{
using Status = EventTracingEnableStatus;
auto startTraceResult = StartTrace(&m_sessionHandle, KERNEL_LOGGER_NAME, props());
switch (startTraceResult)
{
case ERROR_SUCCESS:
return Status::LaunchedSuccessfully;
case ERROR_ALREADY_EXISTS:
{
if (_force)
{
// Try to stop another event tracing session to force launch self session.
if (_step == 0)
{
/*
According to https://msdn.microsoft.com/en-us/library/windows/desktop/aa363696(v=vs.85).aspx
SessionHandle is ignored (and could be NULL) if SessionName is not NULL,
and you only need to set the Wnode.BufferSize, Wnode.Guid, LoggerNameOffset, and LogFileNameOffset
in EVENT_TRACE_PROPERTIES structure if ControlCode is EVENT_TRACE_CONTROL_STOP.
All data is already set for m_properties to the moment. Simply copy m_properties and use the copy.
This method supposed to be faster than launching console window and executing shell command,
but if that would not work, return to using shell command "logman stop".
*/
// static is safe because we are guarded by spin-lock m_spin
static Properties p;
p.base = m_properties.base; // Use copy of m_properties to make sure m_properties will not be changed
// Stop another session
ControlTrace((TRACEHANDLE)NULL, KERNEL_LOGGER_NAME, reinterpret_cast<EVENT_TRACE_PROPERTIES*>(&p), EVENT_TRACE_CONTROL_STOP);
// Console window variant:
//if (32 >= (int)ShellExecute(NULL, NULL, "logman", "stop \"" KERNEL_LOGGER_NAME "\" -ets", NULL, SW_HIDE))
// return Status::AlreadyLaunched;
}
if (_step < 4)
{
// Command executed successfully. Wait for a few time until tracing session finish.
std::this_thread::sleep_for(std::chrono::milliseconds(500));
return startTrace(true, ++_step);
}
}
EASY_ERROR("Event tracing not launched: ERROR_ALREADY_EXISTS. To stop another session execute cmd: logman stop \"" << KERNEL_LOGGER_NAME << "\" -ets\n");
return Status::AlreadyLaunched;
}
case ERROR_ACCESS_DENIED:
EASY_ERROR("Event tracing not launched: ERROR_ACCESS_DENIED. Try to launch your application as Administrator.\n");
return Status::PermissionDenied;
case ERROR_BAD_LENGTH:
EASY_ERROR("Event tracing not launched: ERROR_BAD_LENGTH. It seems that your KERNEL_LOGGER_NAME differs from \"" << m_properties.sessionName << "\". Try to re-compile easy_profiler or contact EasyProfiler developers.\n");
return Status::BadPropertiesSize;
}
EASY_ERROR("Event tracing not launched: StartTrace() returned " << startTraceResult << std::endl);
return Status::UnknownError;
}
EventTracingEnableStatus EasyEventTracer::enable(bool _force)
{
using Status = EventTracingEnableStatus;
profiler::guard_lock<profiler::spin_lock> lock(m_spin);
if (m_bEnabled)
return Status::LaunchedSuccessfully;
/*
Trying to set debug privilege for current process
to be able to get other process information (process name).
*/
EasyEventTracer::setProcessPrivileges();
// Clear properties
memset(&m_properties, 0, sizeof(m_properties));
m_properties.base.Wnode.BufferSize = sizeof(m_properties);
m_properties.base.Wnode.Flags = WNODE_FLAG_TRACED_GUID;
m_properties.base.Wnode.ClientContext = RAW_TIMESTAMP_TIME_TYPE;
m_properties.base.Wnode.Guid = SystemTraceControlGuid;
m_properties.base.LoggerNameOffset = sizeof(m_properties.base);
m_properties.base.EnableFlags = EVENT_TRACE_FLAG_CSWITCH;
m_properties.base.LogFileMode = EVENT_TRACE_REAL_TIME_MODE;
// Start event tracing
const auto res = startTrace(_force);
if (res != Status::LaunchedSuccessfully)
return res;
memset(&m_trace, 0, sizeof(m_trace));
#ifdef __MINGW32__
m_trace.LoggerName = KERNEL_LOGGER;
#else
m_trace.LoggerName = KERNEL_LOGGER_NAME;
#endif
m_trace.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_EVENT_RECORD | PROCESS_TRACE_MODE_RAW_TIMESTAMP;
m_trace.EventRecordCallback = easyProcessTraceEvent;
m_openedHandle = OpenTrace(&m_trace);
if (m_openedHandle == INVALID_PROCESSTRACE_HANDLE)
{
EASY_ERROR("Event tracing not launched: OpenTrace() returned invalid handle.\n");
return Status::OpenTraceFailed;
}
/*
Have to launch a thread to process events because according to MSDN documentation:
The ProcessTrace function blocks the thread until it delivers all events, the BufferCallback function returns FALSE,
or you call CloseTrace. If the consumer is consuming events in real time, the ProcessTrace function returns after
the controller stops the trace session. (Note that there may be a several-second delay before the function returns.)
https://msdn.microsoft.com/en-us/library/windows/desktop/aa364093(v=vs.85).aspx
*/
m_processThread = std::thread([this](bool _lowPriority)
{
if (_lowPriority) // Set low priority for event tracing thread
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST);
EASY_THREAD_SCOPE("EasyProfiler.ETW");
ProcessTrace(&m_openedHandle, 1, 0, 0);
}, m_lowPriority.load(std::memory_order_acquire));
m_bEnabled = true;
EASY_LOGMSG("Event tracing launched\n");
return Status::LaunchedSuccessfully;
}
void EasyEventTracer::disable()
{
profiler::guard_lock<profiler::spin_lock> lock(m_spin);
if (!m_bEnabled)
return;
EASY_LOGMSG("Event tracing is stopping...\n");
TRACING_END_TIME.store(profiler::clock::now(), std::memory_order_release);
ControlTrace(m_openedHandle, KERNEL_LOGGER_NAME, props(), EVENT_TRACE_CONTROL_STOP);
CloseTrace(m_openedHandle);
// Wait for ProcessTrace to finish to make sure no processTraceEvent() will be called later.
if (m_processThread.joinable())
m_processThread.join();
m_bEnabled = false;
// processTraceEvent() is not called anymore. Clean static maps is safe.
PROCESS_INFO_TABLE.clear();
THREAD_PROCESS_INFO_TABLE.clear();
THREAD_PROCESS_INFO_TABLE[0U] = nullptr;
TRACING_END_TIME.store(~0ULL, std::memory_order_release);
EASY_LOGMSG("Event tracing stopped\n");
}
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
#endif // _WIN32
+127
View File
@@ -0,0 +1,127 @@
/************************************************************************
* file name : event_trace_win.h
* ----------------- :
* creation time : 2016/09/04
* author : Victor Zarubkin
* email : v.s.zarubkin@gmail.com
* ----------------- :
* description : The file contains declaration of EasyEventTracer class used for tracing
* : Windows system events to get context switches.
* ----------------- :
* change log : * 2016/09/04 Victor Zarubkin: initial commit.
* :
* : *
* ----------------- :
* license : Lightweight profiler library for c++
* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin
* :
* : Licensed under either of
* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
* : at your option.
* :
* : The MIT License
* :
* : 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.
* :
* : 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 EASY_PROFILER_EVENT_TRACE_WINDOWS_H
#define EASY_PROFILER_EVENT_TRACE_WINDOWS_H
#ifdef _WIN32
#define INITGUID // This is to enable using SystemTraceControlGuid in evntrace.h.
#include <Windows.h>
#include <Strsafe.h>
#include <wmistr.h>
#include <evntrace.h>
#include <evntcons.h>
#include <thread>
#include <atomic>
#include "event_trace_status.h"
#include "spin_lock.h"
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
class EasyEventTracer EASY_FINAL
{
#ifndef EASY_MAGIC_STATIC_AVAILABLE
friend class EasyEventTracerInstance;
#endif
#pragma pack(push, 1)
struct Properties {
Properties();
EVENT_TRACE_PROPERTIES base;
char sessionName[sizeof(KERNEL_LOGGER_NAME)];
};
#pragma pack(pop)
std::thread m_processThread;
Properties m_properties;
EVENT_TRACE_LOGFILE m_trace;
profiler::spin_lock m_spin;
std::atomic_bool m_lowPriority;
TRACEHANDLE m_sessionHandle = INVALID_PROCESSTRACE_HANDLE;
TRACEHANDLE m_openedHandle = INVALID_PROCESSTRACE_HANDLE;
bool m_bEnabled = false;
public:
static EasyEventTracer& instance();
~EasyEventTracer();
bool isLowPriority() const;
EventTracingEnableStatus enable(bool _force = false);
void disable();
void setLowPriority(bool _value);
static void setProcessPrivileges();
private:
EasyEventTracer();
inline EVENT_TRACE_PROPERTIES* props()
{
return reinterpret_cast<EVENT_TRACE_PROPERTIES*>(&m_properties);
}
EventTracingEnableStatus startTrace(bool _force, int _step = 0);
}; // END of class EasyEventTracer.
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
#endif // _WIN32
#endif // EASY_PROFILER_EVENT_TRACE_WINDOWS_H
+307
View File
@@ -0,0 +1,307 @@
/************************************************************************
* file name : hashed_str.h
* ----------------- :
* creation time : 2016/09/11
* author : Victor Zarubkin
* email : v.s.zarubkin@gmail.com
* ----------------- :
* description : The file contains definition of C-strings with calculated hash-code.
* : These strings may be used as optimized keys for std::unordered_map.
* ----------------- :
* change log : * 2016/09/11 Victor Zarubkin: Initial commit. Moved sources from reader.cpp
* :
* : *
* ----------------- :
* license : Lightweight profiler library for c++
* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin
* :
* : Licensed under either of
* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
* : at your option.
* :
* : The MIT License
* :
* : 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.
* :
* : 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 EASY_PROFILER__HASHED_CSTR__H_
#define EASY_PROFILER__HASHED_CSTR__H_
#include <functional>
#include <string.h>
#include <string>
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
namespace profiler {
class hashed_stdstring
{
::std::string m_str;
size_t m_hash;
public:
hashed_stdstring(const char* _str) : m_str(_str), m_hash(::std::hash<::std::string>()(m_str))
{
}
hashed_stdstring(const ::std::string& _str) : m_str(_str), m_hash(::std::hash<::std::string>()(m_str))
{
}
hashed_stdstring(::std::string&& _str) : m_str(::std::forward<::std::string&&>(_str)), m_hash(::std::hash<::std::string>()(m_str))
{
}
hashed_stdstring(hashed_stdstring&& _other) : m_str(::std::move(_other.m_str)), m_hash(_other.m_hash)
{
}
hashed_stdstring(const char* _str, size_t _hash_code) : m_str(_str), m_hash(_hash_code)
{
}
hashed_stdstring(const ::std::string& _str, size_t _hash_code) : m_str(_str), m_hash(_hash_code)
{
}
hashed_stdstring(::std::string&& _str, size_t _hash_code) : m_str(::std::forward<::std::string&&>(_str)), m_hash(_hash_code)
{
}
hashed_stdstring(const hashed_stdstring&) = default;
hashed_stdstring& operator = (const hashed_stdstring&) = default;
hashed_stdstring& operator = (hashed_stdstring&& _other)
{
m_str = ::std::move(_other.m_str);
m_hash = _other.m_hash;
return *this;
}
inline bool operator == (const hashed_stdstring& _other) const
{
return m_hash == _other.m_hash && m_str == _other.m_str;
}
inline bool operator != (const hashed_stdstring& _other) const
{
return !operator == (_other);
}
inline size_t hcode() const
{
return m_hash;
}
inline const char* c_str() const
{
return m_str.c_str();
}
inline size_t size() const
{
return m_str.size();
}
}; // END of class hashed_stdstring.
} // END of namespace profiler.
namespace std {
/** \brief Simply returns precalculated hash of a std::string. */
template <> struct hash<::profiler::hashed_stdstring> {
typedef ::profiler::hashed_stdstring argument_type;
typedef size_t result_type;
inline size_t operator () (const ::profiler::hashed_stdstring& _str) const {
return _str.hcode();
}
};
} // END of namespace std.
#if 0 == 1 //defined(_MSC_VER)// && _MSC_VER >= 1800
# define EASY_PROFILER_HASHED_CSTR_DEFINED
namespace profiler {
/** \brief Simple C-string pointer with length.
It is used as base class for a key in std::unordered_map.
It is used to get better performance than std::string.
It simply stores a pointer and a length, there is no
any memory allocation and copy.
\warning Make sure you know what you are doing. You have to be sure that
pointed C-string will exist until you finish using this cstring.
\ingroup profiler
*/
class cstring
{
protected:
const char* m_str;
size_t m_len;
public:
cstring(const char* _str) : m_str(_str), m_len(strlen(_str))
{
}
cstring(const char* _str, size_t _len) : m_str(_str), m_len(_len)
{
}
cstring(const cstring&) = default;
cstring& operator = (const cstring&) = default;
inline bool operator == (const cstring& _other) const
{
return m_len == _other.m_len && !strncmp(m_str, _other.m_str, m_len);
}
inline bool operator != (const cstring& _other) const
{
return !operator == (_other);
}
inline bool operator < (const cstring& _other) const
{
if (m_len == _other.m_len)
{
return strncmp(m_str, _other.m_str, m_len) < 0;
}
return m_len < _other.m_len;
}
inline const char* c_str() const
{
return m_str;
}
inline size_t size() const
{
return m_len;
}
}; // END of class cstring.
/** \brief cstring with precalculated hash.
This is used to calculate hash for C-string and to cache it
to be used in the future without recurring hash calculatoin.
\note This class is used as a key in std::unordered_map.
\ingroup profiler
*/
class hashed_cstr : public cstring
{
using Parent = cstring;
size_t m_hash;
public:
hashed_cstr(const char* _str) : Parent(_str), m_hash(0)
{
m_hash = ::std::_Hash_seq((const unsigned char *)m_str, m_len);
}
hashed_cstr(const char* _str, size_t _hash_code) : Parent(_str), m_hash(_hash_code)
{
}
hashed_cstr(const char* _str, size_t _len, size_t _hash_code) : Parent(_str, _len), m_hash(_hash_code)
{
}
hashed_cstr(const hashed_cstr&) = default;
hashed_cstr& operator = (const hashed_cstr&) = default;
inline bool operator == (const hashed_cstr& _other) const
{
return m_hash == _other.m_hash && Parent::operator == (_other);
}
inline bool operator != (const hashed_cstr& _other) const
{
return !operator == (_other);
}
inline size_t hcode() const
{
return m_hash;
}
}; // END of class hashed_cstr.
using string_with_hash = hashed_cstr;
} // END of namespace profiler.
namespace std {
/** \brief Simply returns precalculated hash of a C-string. */
template <> struct hash<::profiler::hashed_cstr>
{
using argument_type = ::profiler::hashed_cstr;
using result_type = size_t;
inline size_t operator () (const ::profiler::hashed_cstr& _str) const
{
return _str.hcode();
}
};
} // END of namespace std.
#else ////////////////////////////////////////////////////////////////////
// TODO: Create hashed_cstr for Linux (need to use Linux version of std::_Hash_seq)
namespace profiler {
using string_with_hash = hashed_stdstring;
}
#endif
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
#endif // EASY_PROFILER__HASHED_CSTR__H_
+92
View File
@@ -0,0 +1,92 @@
/**
Lightweight profiler library for c++
Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin
Licensed under either of
* MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
* Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
at your option.
The MIT License
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.
The Apache License, Version 2.0 (the "License");
You may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
**/
#include "nonscoped_block.h"
#include <cstring>
#include <cstdlib>
NonscopedBlock::NonscopedBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, bool)
: profiler::Block(_desc, _runtimeName, false), m_runtimeName(nullptr)
{
}
NonscopedBlock::~NonscopedBlock()
{
// Actually destructor should not be invoked because StackBuffer do manual memory management
m_end = m_begin; // to restrict profiler::Block to invoke profiler::endBlock() on destructor.
free(m_runtimeName);
}
void NonscopedBlock::copyname()
{
// Here we need to copy m_name to m_runtimeName to ensure that
// it would be alive to the moment we will serialize the block
if ((m_status & profiler::ON) == 0)
return;
if (*m_name != 0)
{
auto len = strlen(m_name);
m_runtimeName = static_cast<char*>(malloc(len + 1));
// memcpy should be faster than strncpy because we know
// actual bytes number and both strings have the same size
memcpy(m_runtimeName, m_name, len);
m_runtimeName[len] = 0;
m_name = m_runtimeName;
}
else
{
m_name = "";
}
}
void NonscopedBlock::destroy()
{
// free memory used by m_runtimeName
free(m_runtimeName);
m_name = "";
}
+73
View File
@@ -0,0 +1,73 @@
/**
Lightweight profiler library for c++
Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin
Licensed under either of
* MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
* Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
at your option.
The MIT License
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.
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 EASY_PROFILER_NONSCOPED_BLOCK_H
#define EASY_PROFILER_NONSCOPED_BLOCK_H
#include <easy/details/profiler_public_types.h>
class NonscopedBlock : public profiler::Block
{
char* m_runtimeName; ///< A copy of _runtimeName to make it safe to begin block in one function and end it in another
public:
NonscopedBlock() = delete;
NonscopedBlock(const NonscopedBlock&) = delete;
NonscopedBlock(NonscopedBlock&&) = delete;
NonscopedBlock& operator = (const NonscopedBlock&) = delete;
NonscopedBlock& operator = (NonscopedBlock&&) = delete;
NonscopedBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, bool = false);
~NonscopedBlock();
/** Copy string from m_name to m_runtimeName to make it safe to end block in another function.
Performs any work if block is ON and m_name != ""
*/
void copyname();
void destroy();
}; // END of class NonscopedBlock.
#endif // EASY_PROFILER_NONSCOPED_BLOCK_H
File diff suppressed because it is too large Load Diff
+226
View File
@@ -0,0 +1,226 @@
/**
Lightweight profiler library for c++
Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin
Licensed under either of
* MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
* Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
at your option.
The MIT License
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.
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 EASY_PROFILER_MANAGER_H
#define EASY_PROFILER_MANAGER_H
#include <easy/details/profiler_public_types.h>
#ifdef _WIN32
// Do not move this include to other place!
// It should be included before Windows.h which is included in spin_lock.h
# include <easy/easy_socket.h>
#endif // _WIN32
#include "spin_lock.h"
#include "hashed_cstr.h"
#include "thread_storage.h"
#include <atomic>
#include <map>
#include <ostream>
#include <unordered_map>
#include <thread>
#include <type_traits>
#include <vector>
//////////////////////////////////////////////////////////////////////////
using processid_t = uint64_t;
class BlockDescriptor;
namespace profiler {
class ValueId;
}
class ProfileManager
{
#ifndef EASY_MAGIC_STATIC_AVAILABLE
friend class ProfileManagerInstance;
#endif
ProfileManager();
using atomic_timestamp_t = std::atomic<profiler::timestamp_t>;
using guard_lock_t = profiler::guard_lock<profiler::spin_lock>;
using map_of_threads_stacks = std::map<profiler::thread_id_t, ThreadStorage>;
using block_descriptors_t = std::vector<BlockDescriptor*>;
using descriptors_map_t = std::unordered_map<profiler::string_with_hash, profiler::block_id_t>;
const processid_t m_processId;
#if defined(EASY_CHRONO_CLOCK) || defined(_WIN32)
const int64_t m_cpuFrequency;
#endif
map_of_threads_stacks m_threads;
block_descriptors_t m_descriptors;
descriptors_map_t m_descriptorsMap;
uint64_t m_descriptorsMemorySize;
#if !defined(EASY_CHRONO_CLOCK) && !defined(_WIN32)
std::atomic<int64_t> m_cpuFrequency;
#endif
profiler::timestamp_t m_beginTime;
profiler::timestamp_t m_endTime;
atomic_timestamp_t m_frameMax;
atomic_timestamp_t m_frameAvg;
atomic_timestamp_t m_frameCur;
profiler::spin_lock m_spin;
profiler::spin_lock m_storedSpin;
profiler::spin_lock m_dumpSpin;
std::atomic<profiler::thread_id_t> m_mainThreadId;
std::atomic_bool m_profilerStatus;
std::atomic_bool m_isEventTracingEnabled;
std::atomic_bool m_isAlreadyListening;
std::atomic_bool m_frameMaxReset;
std::atomic_bool m_frameAvgReset;
std::atomic_bool m_stopDumping;
std::string m_csInfoFilename = "/tmp/cs_profiling_info.log";
std::thread m_listenThread;
std::atomic_bool m_stopListen;
public:
ProfileManager(const ProfileManager&) = delete;
ProfileManager(ProfileManager&&) = delete;
ProfileManager& operator = (const ProfileManager&) = delete;
ProfileManager& operator = (ProfileManager&&) = delete;
static ProfileManager& instance();
~ProfileManager();
const profiler::BaseBlockDescriptor* addBlockDescriptor(profiler::EasyBlockStatus _defaultStatus,
const char* _autogenUniqueId,
const char* _name,
const char* _filename,
int _line,
profiler::block_type_t _block_type,
profiler::color_t _color,
bool _copyName = false);
void storeValue(const profiler::BaseBlockDescriptor* _desc, profiler::DataType _type, const void* _data, uint16_t _size, bool _isArray, profiler::ValueId _vin);
bool storeBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName);
bool storeBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, profiler::timestamp_t _beginTime, profiler::timestamp_t _endTime);
void beginBlock(profiler::Block& _block);
void beginNonScopedBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName);
void endBlock();
profiler::timestamp_t maxFrameDuration();
profiler::timestamp_t avgFrameDuration();
profiler::timestamp_t curFrameDuration() const;
void setEnabled(bool isEnable);
EASY_FORCE_INLINE bool isEnabled() const {
return m_profilerStatus.load(std::memory_order_acquire);
}
void setEventTracingEnabled(bool _isEnable);
bool isEventTracingEnabled() const;
uint32_t dumpBlocksToFile(const char* filename);
const char* registerThread(const char* name, profiler::ThreadGuard& threadGuard);
const char* registerThread(const char* name);
void setContextSwitchLogFilename(const char* name);
const char* getContextSwitchLogFilename() const;
void beginContextSwitch(profiler::thread_id_t _thread_id, profiler::timestamp_t _time, profiler::thread_id_t _target_thread_id, const char* _target_process, bool _lockSpin = true);
void endContextSwitch(profiler::thread_id_t _thread_id, processid_t _process_id, profiler::timestamp_t _endtime, bool _lockSpin = true);
void startListen(uint16_t _port);
void stopListen();
bool isListening() const;
profiler::timestamp_t ticks2ns(profiler::timestamp_t ticks) const;
profiler::timestamp_t ticks2us(profiler::timestamp_t ticks) const;
static bool isMainThread();
static profiler::timestamp_t this_thread_frameTime(profiler::Duration _durationCast);
static profiler::timestamp_t this_thread_frameTimeLocalMax(profiler::Duration _durationCast);
static profiler::timestamp_t this_thread_frameTimeLocalAvg(profiler::Duration _durationCast);
static profiler::timestamp_t main_thread_frameTime(profiler::Duration _durationCast);
static profiler::timestamp_t main_thread_frameTimeLocalMax(profiler::Duration _durationCast);
static profiler::timestamp_t main_thread_frameTimeLocalAvg(profiler::Duration _durationCast);
private:
void listen(uint16_t _port);
uint32_t dumpBlocksToStream(std::ostream& _outputStream, bool _lockSpin, bool _async);
void setBlockStatus(profiler::block_id_t _id, profiler::EasyBlockStatus _status);
void registerThread();
void beginFrame();
void endFrame();
void enableEventTracer();
void disableEventTracer();
static char checkThreadExpired(ThreadStorage& _registeredThread);
void storeBlockForce(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, ::profiler::timestamp_t& _timestamp);
void storeBlockForce2(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName, ::profiler::timestamp_t _timestamp);
ThreadStorage& _threadStorage(profiler::thread_id_t _thread_id);
ThreadStorage* _findThreadStorage(profiler::thread_id_t _thread_id);
ThreadStorage& threadStorage(profiler::thread_id_t _thread_id)
{
guard_lock_t lock(m_spin);
return _threadStorage(_thread_id);
}
ThreadStorage* findThreadStorage(profiler::thread_id_t _thread_id)
{
guard_lock_t lock(m_spin);
return _findThreadStorage(_thread_id);
}
}; // END of class ProfileManager.
//////////////////////////////////////////////////////////////////////////
#endif // EASY_PROFILER_MANAGER_H
+349
View File
@@ -0,0 +1,349 @@
/************************************************************************
* file name : profiler.cpp
* ----------------- :
* creation time : 2018/05/06
* authors : Sergey Yagovtsev, Victor Zarubkin
* emails : yse.sey@gmail.com, v.s.zarubkin@gmail.com
* ----------------- :
* description : The file contains implementation of Profile manager and implement access c-function
* :
* license : Lightweight profiler library for c++
* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin
* :
* : Licensed under either of
* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
* : at your option.
* :
* : The MIT License
* :
* : 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.
* :
* : The Apache License, Version 2.0 (the "License")
* :
* : You may not use this file except in compliance with the License.
* : You may obtain a copy of the License at
* :
* : http://www.apache.org/licenses/LICENSE-2.0
* :
* : Unless required by applicable law or agreed to in writing, software
* : distributed under the License is distributed on an "AS IS" BASIS,
* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* : See the License for the specific language governing permissions and
* : limitations under the License.
************************************************************************/
#include <easy/profiler.h>
#include <easy/arbitrary_value.h>
#include "profile_manager.h"
#include "event_trace_win.h"
#include "current_time.h"
//////////////////////////////////////////////////////////////////////////
#ifndef BUILD_WITH_EASY_PROFILER
# ifndef EASY_PROFILER_API_DISABLED
# define EASY_PROFILER_API_DISABLED
# endif
#endif
#if !defined(EASY_PROFILER_VERSION_MAJOR) || !defined(EASY_PROFILER_VERSION_MINOR) || !defined(EASY_PROFILER_VERSION_PATCH)
# ifdef _WIN32
# error EASY_PROFILER_VERSION_MAJOR and EASY_PROFILER_VERSION_MINOR and EASY_PROFILER_VERSION_PATCH macros must be defined
# else
# error "EASY_PROFILER_VERSION_MAJOR and EASY_PROFILER_VERSION_MINOR and EASY_PROFILER_VERSION_PATCH macros must be defined"
# endif
#endif
#define EASY_PROFILER_PRODUCT_VERSION "v" EASY_STRINGIFICATION(EASY_PROFILER_VERSION_MAJOR) "." \
EASY_STRINGIFICATION(EASY_PROFILER_VERSION_MINOR) "." \
EASY_STRINGIFICATION(EASY_PROFILER_VERSION_PATCH)
extern const uint32_t EASY_PROFILER_SIGNATURE = ('E' << 24) | ('a' << 16) | ('s' << 8) | 'y';
extern const uint32_t EASY_PROFILER_VERSION = (static_cast<uint32_t>(EASY_PROFILER_VERSION_MAJOR) << 24) |
(static_cast<uint32_t>(EASY_PROFILER_VERSION_MINOR) << 16) |
static_cast<uint32_t>(EASY_PROFILER_VERSION_PATCH);
#undef EASY_VERSION_INT
//////////////////////////////////////////////////////////////////////////
extern "C" {
PROFILER_API uint8_t versionMajor()
{
static_assert(0 <= EASY_PROFILER_VERSION_MAJOR && EASY_PROFILER_VERSION_MAJOR <= 255,
"EASY_PROFILER_VERSION_MAJOR must be defined in range [0, 255]");
return EASY_PROFILER_VERSION_MAJOR;
}
PROFILER_API uint8_t versionMinor()
{
static_assert(0 <= EASY_PROFILER_VERSION_MINOR && EASY_PROFILER_VERSION_MINOR <= 255,
"EASY_PROFILER_VERSION_MINOR must be defined in range [0, 255]");
return EASY_PROFILER_VERSION_MINOR;
}
PROFILER_API uint16_t versionPatch()
{
static_assert(0 <= EASY_PROFILER_VERSION_PATCH && EASY_PROFILER_VERSION_PATCH <= 65535,
"EASY_PROFILER_VERSION_PATCH must be defined in range [0, 65535]");
return EASY_PROFILER_VERSION_PATCH;
}
PROFILER_API uint32_t version()
{
return EASY_PROFILER_VERSION;
}
PROFILER_API const char* versionName()
{
#ifdef EASY_PROFILER_API_DISABLED
return EASY_PROFILER_PRODUCT_VERSION "_disabled";
#else
return EASY_PROFILER_PRODUCT_VERSION;
#endif
}
//////////////////////////////////////////////////////////////////////////
#if !defined(EASY_PROFILER_API_DISABLED)
PROFILER_API profiler::timestamp_t now()
{
return profiler::clock::now();
}
PROFILER_API profiler::timestamp_t toNanoseconds(profiler::timestamp_t _ticks)
{
return ProfileManager::instance().ticks2ns(_ticks);
}
PROFILER_API profiler::timestamp_t toMicroseconds(profiler::timestamp_t _ticks)
{
return ProfileManager::instance().ticks2us(_ticks);
}
PROFILER_API const profiler::BaseBlockDescriptor*
registerDescription(profiler::EasyBlockStatus _status, const char* _autogenUniqueId, const char* _name,
const char* _filename, int _line, profiler::block_type_t _block_type, profiler::color_t _color,
bool _copyName)
{
return ProfileManager::instance().addBlockDescriptor(_status, _autogenUniqueId, _name, _filename, _line,
_block_type, _color, _copyName);
}
PROFILER_API void endBlock()
{
ProfileManager::instance().endBlock();
}
PROFILER_API void setEnabled(bool isEnable)
{
ProfileManager::instance().setEnabled(isEnable);
}
PROFILER_API bool isEnabled()
{
return ProfileManager::instance().isEnabled();
}
PROFILER_API void storeValue(const profiler::BaseBlockDescriptor* _desc, profiler::DataType _type, const void* _data,
uint16_t _size, bool _isArray, profiler::ValueId _vin)
{
ProfileManager::instance().storeValue(_desc, _type, _data, _size, _isArray, _vin);
}
PROFILER_API void storeEvent(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName)
{
ProfileManager::instance().storeBlock(_desc, _runtimeName);
}
PROFILER_API void storeBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName,
profiler::timestamp_t _beginTime, profiler::timestamp_t _endTime)
{
ProfileManager::instance().storeBlock(_desc, _runtimeName, _beginTime, _endTime);
}
PROFILER_API void beginBlock(profiler::Block& _block)
{
ProfileManager::instance().beginBlock(_block);
}
PROFILER_API void beginNonScopedBlock(const profiler::BaseBlockDescriptor* _desc, const char* _runtimeName)
{
ProfileManager::instance().beginNonScopedBlock(_desc, _runtimeName);
}
PROFILER_API uint32_t dumpBlocksToFile(const char* filename)
{
return ProfileManager::instance().dumpBlocksToFile(filename);
}
PROFILER_API const char* registerThreadScoped(const char* name, profiler::ThreadGuard& threadGuard)
{
return ProfileManager::instance().registerThread(name, threadGuard);
}
PROFILER_API const char* registerThread(const char* name)
{
return ProfileManager::instance().registerThread(name);
}
PROFILER_API void setEventTracingEnabled(bool _isEnable)
{
ProfileManager::instance().setEventTracingEnabled(_isEnable);
}
PROFILER_API bool isEventTracingEnabled()
{
return ProfileManager::instance().isEventTracingEnabled();
}
# ifdef _WIN32
PROFILER_API void setLowPriorityEventTracing(bool _isLowPriority)
{
EasyEventTracer::instance().setLowPriority(_isLowPriority);
}
PROFILER_API bool isLowPriorityEventTracing()
{
return EasyEventTracer::instance().isLowPriority();
}
# else
PROFILER_API void setLowPriorityEventTracing(bool) { }
PROFILER_API bool isLowPriorityEventTracing() { return false; }
# endif
PROFILER_API void setContextSwitchLogFilename(const char* name)
{
return ProfileManager::instance().setContextSwitchLogFilename(name);
}
PROFILER_API const char* getContextSwitchLogFilename()
{
return ProfileManager::instance().getContextSwitchLogFilename();
}
PROFILER_API void startListen(uint16_t _port)
{
return ProfileManager::instance().startListen(_port);
}
PROFILER_API void stopListen()
{
return ProfileManager::instance().stopListen();
}
PROFILER_API bool isListening()
{
return ProfileManager::instance().isListening();
}
PROFILER_API bool isMainThread()
{
return ProfileManager::isMainThread();
}
PROFILER_API profiler::timestamp_t this_thread_frameTime(profiler::Duration _durationCast)
{
return ProfileManager::this_thread_frameTime(_durationCast);
}
PROFILER_API profiler::timestamp_t this_thread_frameTimeLocalMax(profiler::Duration _durationCast)
{
return ProfileManager::this_thread_frameTimeLocalMax(_durationCast);
}
PROFILER_API profiler::timestamp_t this_thread_frameTimeLocalAvg(profiler::Duration _durationCast)
{
return ProfileManager::this_thread_frameTimeLocalAvg(_durationCast);
}
PROFILER_API profiler::timestamp_t main_thread_frameTime(profiler::Duration _durationCast)
{
return ProfileManager::main_thread_frameTime(_durationCast);
}
PROFILER_API profiler::timestamp_t main_thread_frameTimeLocalMax(profiler::Duration _durationCast)
{
return ProfileManager::main_thread_frameTimeLocalMax(_durationCast);
}
PROFILER_API profiler::timestamp_t main_thread_frameTimeLocalAvg(profiler::Duration _durationCast)
{
return ProfileManager::main_thread_frameTimeLocalAvg(_durationCast);
}
#else // EASY_PROFILER_API_DISABLED
PROFILER_API profiler::timestamp_t now() { return 0; }
PROFILER_API profiler::timestamp_t toNanoseconds(profiler::timestamp_t) { return 0; }
PROFILER_API profiler::timestamp_t toMicroseconds(profiler::timestamp_t) { return 0; }
PROFILER_API const profiler::BaseBlockDescriptor* registerDescription(profiler::EasyBlockStatus, const char*,
const char*, const char*, int,
profiler::block_type_t, profiler::color_t, bool)
{
return reinterpret_cast<const profiler::BaseBlockDescriptor*>(0xbad);
}
PROFILER_API void endBlock() { }
PROFILER_API void setEnabled(bool) { }
PROFILER_API bool isEnabled() { return false; }
PROFILER_API void storeValue(const profiler::BaseBlockDescriptor*, profiler::DataType, const void*, uint16_t, bool,
profiler::ValueId)
{
}
PROFILER_API void storeEvent(const profiler::BaseBlockDescriptor*, const char*) { }
PROFILER_API void storeBlock(const profiler::BaseBlockDescriptor*, const char*, profiler::timestamp_t,
profiler::timestamp_t)
{
}
PROFILER_API void beginBlock(profiler::Block&) { }
PROFILER_API void beginNonScopedBlock(const profiler::BaseBlockDescriptor*, const char*) { }
PROFILER_API uint32_t dumpBlocksToFile(const char*) { return 0; }
PROFILER_API const char* registerThreadScoped(const char*, profiler::ThreadGuard&) { return ""; }
PROFILER_API const char* registerThread(const char*) { return ""; }
PROFILER_API void setEventTracingEnabled(bool) { }
PROFILER_API bool isEventTracingEnabled() { return false; }
PROFILER_API void setLowPriorityEventTracing(bool) { }
PROFILER_API bool isLowPriorityEventTracing(bool) { return false; }
PROFILER_API void setContextSwitchLogFilename(const char*) { }
PROFILER_API const char* getContextSwitchLogFilename() { return ""; }
PROFILER_API void startListen(uint16_t) { }
PROFILER_API void stopListen() { }
PROFILER_API bool isListening() { return false; }
PROFILER_API bool isMainThread() { return false; }
PROFILER_API profiler::timestamp_t this_thread_frameTime(profiler::Duration) { return 0; }
PROFILER_API profiler::timestamp_t this_thread_frameTimeLocalMax(profiler::Duration) { return 0; }
PROFILER_API profiler::timestamp_t this_thread_frameTimeLocalAvg(profiler::Duration) { return 0; }
PROFILER_API profiler::timestamp_t main_thread_frameTime(profiler::Duration) { return 0; }
PROFILER_API profiler::timestamp_t main_thread_frameTimeLocalMax(profiler::Duration) { return 0; }
PROFILER_API profiler::timestamp_t main_thread_frameTimeLocalAvg(profiler::Duration) { return 0; }
#endif // EASY_PROFILER_API_DISABLED
} // end extern "C".
File diff suppressed because it is too large Load Diff
+68
View File
@@ -0,0 +1,68 @@
/**
Lightweight profiler library for c++
Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin
Licensed under either of
* MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
* Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
at your option.
The MIT License
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.
The Apache License, Version 2.0 (the "License");
You may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
**/
#include <string.h>
#include <easy/serialized_block.h>
#include "thread_storage.h"
namespace profiler
{
SerializedBlock::SerializedBlock(const Block& block, uint16_t name_length)
: BaseBlockData(block)
{
char* pName = const_cast<char*>(name());
if (name_length != 0)
strncpy(pName, block.name(), name_length);
pName[name_length] = 0;
}
SerializedCSwitch::SerializedCSwitch(const CSwitchBlock& block, uint16_t name_length)
: CSwitchEvent(block)
{
char* pName = const_cast<char*>(name());
if (name_length != 0)
strncpy(pName, block.name(), name_length);
pName[name_length] = 0;
}
} // end of namespace profiler.
+126
View File
@@ -0,0 +1,126 @@
/**
Lightweight profiler library for c++
Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin
Licensed under either of
* MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
* Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
at your option.
The MIT License
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.
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 EASY_PROFILER_SPIN_LOCK_H
#define EASY_PROFILER_SPIN_LOCK_H
#define EASY_USE_CRITICAL_SECTION // Use CRITICAL_SECTION instead of std::atomic_flag
#if defined(_WIN32) && defined(EASY_USE_CRITICAL_SECTION)
#include <Windows.h>
#else
#include <atomic>
#endif
namespace profiler {
#if defined(_WIN32) && defined(EASY_USE_CRITICAL_SECTION)
// std::atomic_flag on Windows works slower than critical section, so we will use it instead of std::atomic_flag...
// By the way, Windows critical sections are slower than std::atomic_flag on Unix.
class spin_lock { CRITICAL_SECTION m_lock; public:
void lock() {
EnterCriticalSection(&m_lock);
}
void unlock() {
LeaveCriticalSection(&m_lock);
}
spin_lock() {
InitializeCriticalSection(&m_lock);
}
~spin_lock() {
DeleteCriticalSection(&m_lock);
}
};
#else
// std::atomic_flag on Unix works fine and very fast (almost instant!)
class spin_lock { ::std::atomic_flag m_lock; public:
void lock() {
while (m_lock.test_and_set(::std::memory_order_acquire));
}
void unlock() {
m_lock.clear(::std::memory_order_release);
}
spin_lock() {
m_lock.clear();
}
};
#endif
template <class T>
class guard_lock
{
T& m_lock;
bool m_isLocked = false;
public:
explicit guard_lock(T& m) : m_lock(m) {
m_lock.lock();
m_isLocked = true;
}
~guard_lock() {
unlock();
}
inline void unlock() {
if (m_isLocked) {
m_lock.unlock();
m_isLocked = false;
}
}
};
} // END of namespace profiler.
#ifdef EASY_USE_CRITICAL_SECTION
# undef EASY_USE_CRITICAL_SECTION
#endif
#endif // EASY_PROFILER_SPIN_LOCK_H
+141
View File
@@ -0,0 +1,141 @@
/**
Lightweight profiler library for c++
Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin
Licensed under either of
* MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
* Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
at your option.
The MIT License
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.
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 EASY_PROFILER_STACK_BUFFER_H
#define EASY_PROFILER_STACK_BUFFER_H
#include <algorithm>
#include <cstdlib>
#include <list>
#include "nonscoped_block.h"
#ifdef max
#undef max
#endif
template <class T>
inline void destroy_elem(T*)
{
}
inline void destroy_elem(NonscopedBlock* _elem)
{
_elem->destroy();
}
template <class T>
class StackBuffer
{
struct chunk { int8_t data[sizeof(T)]; };
std::list<chunk> m_overflow; ///< List of additional stack elements if current capacity of buffer is not enough
T* m_buffer; ///< Contiguous buffer used for stack
uint32_t m_size; ///< Current size of stack
uint32_t m_capacity; ///< Current capacity of m_buffer
uint32_t m_maxcapacity; ///< Maximum used capacity including m_buffer and m_overflow
public:
StackBuffer() = delete;
StackBuffer(const StackBuffer&) = delete;
StackBuffer(StackBuffer&&) = delete;
explicit StackBuffer(uint32_t N)
: m_buffer(static_cast<T*>(malloc(N * sizeof(T))))
, m_size(0)
, m_capacity(N)
, m_maxcapacity(N)
{
}
~StackBuffer()
{
for (uint32_t i = 0; i < m_size; ++i)
destroy_elem(m_buffer + i);
free(m_buffer);
for (auto& elem : m_overflow)
destroy_elem(reinterpret_cast<T*>(elem.data + 0));
}
template <class ... TArgs>
T& push(TArgs ... _args)
{
if (m_size < m_capacity)
return *(::new (m_buffer + m_size++) T(_args...));
m_overflow.emplace_back();
const uint32_t cap = m_capacity + static_cast<uint32_t>(m_overflow.size());
if (m_maxcapacity < cap)
m_maxcapacity = cap;
return *(::new (m_overflow.back().data + 0) T(_args...));
}
void pop()
{
if (m_overflow.empty())
{
// m_size should not be equal to 0 here because ProfileManager behavior does not allow such situation
destroy_elem(m_buffer + --m_size);
if (m_size == 0 && m_maxcapacity > m_capacity)
{
// When stack gone empty we can resize buffer to use enough space in the future
free(m_buffer);
m_maxcapacity = m_capacity = std::max(m_maxcapacity, m_capacity << 1);
m_buffer = static_cast<T*>(malloc(m_capacity * sizeof(T)));
}
return;
}
destroy_elem(reinterpret_cast<T*>(m_overflow.back().data + 0));
m_overflow.pop_back();
}
}; // END of class StackBuffer.
#endif // EASY_PROFILER_STACK_BUFFER_H
+237
View File
@@ -0,0 +1,237 @@
/**
Lightweight profiler library for c++
Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin
Licensed under either of
* MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
* Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
at your option.
The MIT License
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.
The Apache License, Version 2.0 (the "License");
You may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
**/
#include <algorithm>
#include "thread_storage.h"
#include "current_thread.h"
#include "current_time.h"
#ifdef min
#undef min
#endif
#ifdef max
#undef max
#endif
namespace {
EASY_CONSTEXPR uint16_t BASE_SIZE = static_cast<uint16_t>(sizeof(profiler::BaseBlockData) + 1U);
#if EASY_OPTION_TRUNCATE_LONG_RUNTIME_NAMES != 0
EASY_CONSTEXPR uint16_t MAX_BLOCK_NAME_LENGTH = BLOCK_CHUNK_SIZE - BASE_SIZE;
#endif
#if EASY_OPTION_CHECK_MAX_VALUE_DATA_SIZE != 0
EASY_CONSTEXPR uint16_t MAX_VALUE_DATA_SIZE = BLOCK_CHUNK_SIZE - static_cast<uint16_t>(sizeof(profiler::ArbitraryValue));
#endif
profiler::vin_t ptr2vin(const void* ptr)
{
static_assert(sizeof(uintptr_t) == sizeof(void*),
"Can not cast void* to uintptr_t. Different sizes.");
static_assert(sizeof(profiler::vin_t) >= sizeof(uintptr_t),
"Can not cast uintptr_t to vin_t. Possible truncation.");
return static_cast<profiler::vin_t>(reinterpret_cast<uintptr_t>(ptr));
}
} // end of namespace <noname>.
ThreadStorage::ThreadStorage()
: nonscopedBlocks(16)
, frameStartTime(0)
, id(getCurrentThreadId())
, stackSize(0)
, allowChildren(true)
, named(false)
, guarded(false)
, frameOpened(false)
{
expired = ATOMIC_VAR_INIT(0);
}
void ThreadStorage::storeValue(
profiler::timestamp_t _timestamp,
profiler::block_id_t _id,
profiler::DataType _type,
const void* _data,
uint16_t _size,
bool _isArray,
profiler::ValueId _vin
) {
#if EASY_OPTION_CHECK_MAX_VALUE_DATA_SIZE != 0
if (_size > MAX_VALUE_DATA_SIZE)
{
return;
}
#endif
const uint16_t serializedDataSize = _size + static_cast<uint16_t>(sizeof(profiler::ArbitraryValue));
void* data = blocks.closedList.allocate(serializedDataSize);
::new (data) profiler::ArbitraryValue(_timestamp, ptr2vin(_vin.m_id), _id, _size, _type, _isArray);
char* cdata = reinterpret_cast<char*>(data);
memcpy(cdata + sizeof(profiler::ArbitraryValue), _data, _size);
blocks.frameMemorySize += serializedDataSize;
putMarkIfEmpty();
}
void ThreadStorage::storeBlock(const profiler::Block& block)
{
#if EASY_OPTION_MEASURE_STORAGE_EXPAND != 0
EASY_LOCAL_STATIC_PTR(const BaseBlockDescriptor*, desc, \
MANAGER.addBlockDescriptor(EASY_OPTION_STORAGE_EXPAND_BLOCKS_ON ? profiler::ON : profiler::OFF, EASY_UNIQUE_LINE_ID, "EasyProfiler.ExpandStorage", \
__FILE__, __LINE__, profiler::BlockType::Block, EASY_COLOR_INTERNAL_EVENT));
EASY_THREAD_LOCAL static profiler::timestamp_t beginTime = 0ULL;
EASY_THREAD_LOCAL static profiler::timestamp_t endTime = 0ULL;
#endif
#if EASY_OPTION_TRUNCATE_LONG_RUNTIME_NAMES != 0
const uint16_t nameLength = std::min(static_cast<uint16_t>(strlen(block.name())), MAX_BLOCK_NAME_LENGTH);
#else
const uint16_t nameLength = static_cast<uint16_t>(strlen(block.name()));
#endif
#if EASY_OPTION_MEASURE_STORAGE_EXPAND == 0
const
#endif
auto serializedDataSize = static_cast<uint16_t>(BASE_SIZE + nameLength);
#if EASY_OPTION_MEASURE_STORAGE_EXPAND != 0
const bool expanded = (desc->m_status & profiler::ON) && blocks.closedList.need_expand(serializedDataSize);
if (expanded) beginTime = profiler::clock::now();
#endif
void* data = blocks.closedList.allocate(serializedDataSize);
#if EASY_OPTION_MEASURE_STORAGE_EXPAND != 0
if (expanded) endTime = profiler::clock::now();
#endif
::new (data) profiler::SerializedBlock(block, nameLength);
blocks.frameMemorySize += serializedDataSize;
#if EASY_OPTION_MEASURE_STORAGE_EXPAND != 0
if (expanded)
{
profiler::Block b(beginTime, desc->id(), "");
b.finish(endTime);
serializedDataSize = static_cast<uint16_t>(sizeof(profiler::BaseBlockData) + 1);
data = blocks.closedList.allocate(serializedDataSize);
::new (data) profiler::SerializedBlock(b, 0);
blocks.frameMemorySize += serializedDataSize;
}
#endif
}
void ThreadStorage::storeBlockForce(const profiler::Block& block)
{
const auto nameLength = static_cast<uint16_t>(strlen(block.name()));
const auto serializedDataSize = static_cast<uint16_t>(sizeof(profiler::BaseBlockData) + nameLength + 1);
void* data = blocks.closedList.marked_allocate(serializedDataSize);
::new (data) profiler::SerializedBlock(block, nameLength);
blocks.usedMemorySize += serializedDataSize;
}
void ThreadStorage::storeCSwitch(const CSwitchBlock& block)
{
const auto nameLength = static_cast<uint16_t>(strlen(block.name()));
const auto serializedDataSize = static_cast<uint16_t>(sizeof(profiler::CSwitchEvent) + nameLength + 1);
void* data = sync.closedList.allocate(serializedDataSize);
::new (data) profiler::SerializedCSwitch(block, nameLength);
sync.usedMemorySize += serializedDataSize;
}
void ThreadStorage::clearClosed()
{
blocks.clearClosed();
sync.clearClosed();
}
void ThreadStorage::popSilent()
{
if (!blocks.openedList.empty())
{
profiler::Block& top = blocks.openedList.back();
top.m_end = top.m_begin;
if (!top.m_isScoped)
nonscopedBlocks.pop();
blocks.openedList.pop_back();
}
}
void ThreadStorage::beginFrame()
{
if (!frameOpened)
{
frameStartTime = profiler::clock::now();
frameOpened = true;
}
}
profiler::timestamp_t ThreadStorage::endFrame()
{
frameOpened = false;
return profiler::clock::now() - frameStartTime;
}
void ThreadStorage::putMark()
{
blocks.closedList.put_mark();
blocks.usedMemorySize += blocks.frameMemorySize;
blocks.frameMemorySize = 0;
}
void ThreadStorage::putMarkIfEmpty()
{
if (!frameOpened)
putMark();
}
+150
View File
@@ -0,0 +1,150 @@
/**
Lightweight profiler library for c++
Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin
Licensed under either of
* MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
* Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
at your option.
The MIT License
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.
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 EASY_PROFILER_THREAD_STORAGE_H
#define EASY_PROFILER_THREAD_STORAGE_H
#include <atomic>
#include <functional>
#include <string>
#include <vector>
#include <easy/details/profiler_public_types.h>
#include <easy/details/arbitrary_value_public_types.h>
#include <easy/serialized_block.h>
#include "chunk_allocator.h"
#include "stack_buffer.h"
//////////////////////////////////////////////////////////////////////////
template <class T, const uint16_t N>
struct BlocksList
{
BlocksList(const BlocksList&) = delete;
BlocksList(BlocksList&&) = delete;
BlocksList() = default;
std::vector<T> openedList;
chunk_allocator<N> closedList;
uint64_t usedMemorySize = 0;
uint64_t frameMemorySize = 0;
void clearClosed()
{
//closedList.clear();
usedMemorySize = 0;
frameMemorySize = 0;
}
}; // END of struct BlocksList.
//////////////////////////////////////////////////////////////////////////
class CSwitchBlock : public profiler::CSwitchEvent
{
const char* m_name;
public:
CSwitchBlock(profiler::timestamp_t _begin_time, profiler::thread_id_t _tid, const char* _runtimeName) EASY_NOEXCEPT;
inline const char* name() const EASY_NOEXCEPT { return m_name; }
};
//////////////////////////////////////////////////////////////////////////
EASY_CONSTEXPR uint16_t BLOCKS_IN_CHUNK = 128U;
EASY_CONSTEXPR uint16_t SIZEOF_BLOCK = sizeof(profiler::BaseBlockData) + 1U + sizeof(uint16_t); // SerializedBlock stores BaseBlockData + at least 1 character for name ('\0') + 2 bytes for size of serialized data
EASY_CONSTEXPR uint16_t SIZEOF_CSWITCH = sizeof(profiler::CSwitchEvent) + 1U + sizeof(uint16_t); // SerializedCSwitch also stores additional 4 bytes to be able to save 64-bit thread_id
static_assert(((int)SIZEOF_BLOCK * (int)BLOCKS_IN_CHUNK) < 65536, "Chunk size for profiler::Block must be less than 65536");
static_assert(((int)SIZEOF_CSWITCH * (int)BLOCKS_IN_CHUNK) < 65536, "Chunk size for CSwitchBlock must be less than 65536");
EASY_CONSTEXPR uint16_t BLOCK_CHUNK_SIZE = get_aligned_size<SIZEOF_BLOCK * BLOCKS_IN_CHUNK>::Size;
EASY_CONSTEXPR uint16_t CSWITCH_CHUNK_SIZE = get_aligned_size<SIZEOF_BLOCK * BLOCKS_IN_CHUNK>::Size;
static_assert((BLOCK_CHUNK_SIZE % EASY_ALIGN_SIZE) == 0, "BLOCK_CHUNK_SIZE not aligned");
static_assert((CSWITCH_CHUNK_SIZE % EASY_ALIGN_SIZE) == 0, "CSWITCH_CHUNK_SIZE not aligned");
static_assert(BLOCK_CHUNK_SIZE > 2048, "wrong BLOCK_CHUNK_SIZE");
static_assert(CSWITCH_CHUNK_SIZE > 2048, "wrong CSWITCH_CHUNK_SIZE");
struct ThreadStorage EASY_FINAL
{
using BlocksStorage = BlocksList<std::reference_wrapper<profiler::Block>, BLOCK_CHUNK_SIZE>;
using ContextSwitchStorage = BlocksList<CSwitchBlock, CSWITCH_CHUNK_SIZE>;
StackBuffer<NonscopedBlock> nonscopedBlocks;
BlocksStorage blocks;
ContextSwitchStorage sync;
std::string name; ///< Thread name
profiler::timestamp_t frameStartTime; ///< Current frame start time. Used to calculate FPS.
const profiler::thread_id_t id; ///< Thread ID
std::atomic<char> expired; ///< Is thread expired
int32_t stackSize; ///< Current thread stack depth. Used when switching profiler state to begin collecting blocks only when new frame would be opened.
bool allowChildren; ///< False if one of previously opened blocks has OFF_RECURSIVE or ON_WITHOUT_CHILDREN status
bool named; ///< True if thread name was set
bool guarded; ///< True if thread has been registered using ThreadGuard
bool frameOpened; ///< Is new frame opened (this does not depend on profiling status) \sa profiledFrameOpened
void storeValue(profiler::timestamp_t _timestamp, profiler::block_id_t _id, profiler::DataType _type, const void* _data, uint16_t _size, bool _isArray, profiler::ValueId _vin);
void storeBlock(const profiler::Block& _block);
void storeBlockForce(const profiler::Block& _block);
void storeCSwitch(const CSwitchBlock& _block);
void clearClosed();
void popSilent();
void beginFrame();
profiler::timestamp_t endFrame();
void putMark();
void putMarkIfEmpty();
ThreadStorage();
ThreadStorage(const ThreadStorage&) = delete;
ThreadStorage(ThreadStorage&&) = delete;
}; // END of struct ThreadStorage.
//////////////////////////////////////////////////////////////////////////
#endif // EASY_PROFILER_THREAD_STORAGE_H
+564
View File
@@ -0,0 +1,564 @@
/************************************************************************
* file name : writer.cpp
* ----------------- :
* creation time : 2018/05/08
* authors : Sergey Yagovtsev, Victor Zarubkin
* emails : yse.sey@gmail.com, v.s.zarubkin@gmail.com
* ----------------- :
* description : The file contains implementation of fillTreesFromFile function
* : which reads profiler file and fill profiler blocks tree.
* ----------------- :
* license : Lightweight profiler library for c++
* : Copyright(C) 2016-2019 Sergey Yagovtsev, Victor Zarubkin
* :
* : Licensed under either of
* : * MIT license (LICENSE.MIT or http://opensource.org/licenses/MIT)
* : * Apache License, Version 2.0, (LICENSE.APACHE or http://www.apache.org/licenses/LICENSE-2.0)
* : at your option.
* :
* : The MIT License
* :
* : 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.
* :
* : The Apache License, Version 2.0 (the "License")
* :
* : You may not use this file except in compliance with the License.
* : You may obtain a copy of the License at
* :
* : http://www.apache.org/licenses/LICENSE-2.0
* :
* : Unless required by applicable law or agreed to in writing, software
* : distributed under the License is distributed on an "AS IS" BASIS,
* : WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* : See the License for the specific language governing permissions and
* : limitations under the License.
************************************************************************/
#include <fstream>
#include <iterator>
#include <algorithm>
#include <easy/writer.h>
#include <easy/profiler.h>
#include "alignment_helpers.h"
//////////////////////////////////////////////////////////////////////////
extern const uint32_t EASY_PROFILER_SIGNATURE;
extern const uint32_t EASY_PROFILER_VERSION;
EASY_CONSTEXPR auto BaseCSwitchSize = sizeof(profiler::SerializedCSwitch) + 1;
//////////////////////////////////////////////////////////////////////////
struct BlocksRange
{
profiler::block_index_t begin;
profiler::block_index_t end;
BlocksRange(profiler::block_index_t size = 0)
: begin(0), end(size)
{
}
BlocksRange(profiler::block_index_t beginIndex, profiler::block_index_t endIndex)
: begin(beginIndex), end(endIndex)
{
}
};
struct BlocksMemoryAndCount
{
uint64_t usedMemorySize = 0; // memory size used by profiler blocks
profiler::block_index_t blocksCount = 0;
BlocksMemoryAndCount() = default;
BlocksMemoryAndCount& operator += (const BlocksMemoryAndCount& another)
{
usedMemorySize += another.usedMemorySize;
blocksCount += another.blocksCount;
return *this;
}
};
struct BlocksAndCSwitchesRange
{
BlocksMemoryAndCount blocksMemoryAndCount;
BlocksMemoryAndCount cswitchesMemoryAndCount;
BlocksRange blocks;
BlocksRange cswitches;
};
//////////////////////////////////////////////////////////////////////////
template <typename T>
static void write(std::ostream& _stream, const char* _data, T _size)
{
_stream.write(_data, _size);
}
template <class T>
static void write(std::ostream& _stream, const T& _data)
{
_stream.write((const char*)&_data, sizeof(T));
}
static bool update_progress_write(std::atomic<int>& progress, int new_value, std::ostream& _log)
{
auto oldprogress = progress.exchange(new_value, std::memory_order_release);
if (oldprogress < 0)
{
_log << "Writing was interrupted";
return false;
}
return true;
}
//////////////////////////////////////////////////////////////////////////
static BlocksRange findRange(const profiler::BlocksTree::children_t& children, profiler::timestamp_t beginTime,
profiler::timestamp_t endTime, const profiler::block_getter_fn& getter)
{
const auto size = static_cast<profiler::block_index_t>(children.size());
BlocksRange range(size);
if (size == 0)
return range;
if (beginTime <= getter(children.front()).node->begin() && getter(children.back()).node->end() <= endTime)
return range; // All blocks inside range
auto first_it = std::lower_bound(children.begin(), children.end(), beginTime,
[&](profiler::block_index_t element, profiler::timestamp_t value)
{
return getter(element).node->end() < value;
});
for (; first_it != children.end(); ++first_it)
{
const auto& child = getter(*first_it);
if (child.node->begin() >= beginTime || child.node->end() > beginTime)
break;
}
if (first_it != children.end() && getter(*first_it).node->begin() <= endTime)
{
auto last_it = std::lower_bound(children.begin(), children.end(), endTime,
[&](profiler::block_index_t element, profiler::timestamp_t value)
{
return getter(element).node->begin() <= value;
});
if (last_it != children.end() && getter(*last_it).node->end() >= beginTime)
{
const auto begin = static_cast<profiler::block_index_t>(std::distance(children.begin(), first_it));
const auto end = static_cast<profiler::block_index_t>(std::distance(children.begin(), last_it));
if (begin <= end)
{
range.begin = begin;
range.end = end;
}
}
}
return range;
}
static BlocksRange findRange(const profiler::bookmarks_t& bookmarks, profiler::timestamp_t beginTime, profiler::timestamp_t endTime)
{
const auto size = static_cast<profiler::block_index_t>(bookmarks.size());
BlocksRange range(size);
if (size == 0)
return range;
if (beginTime <= bookmarks.front().pos && bookmarks.back().pos <= endTime)
return range; // All blocks inside range
auto first_it = std::lower_bound(bookmarks.begin(), bookmarks.end(), beginTime,
[](const profiler::Bookmark& element, profiler::timestamp_t value)
{
return element.pos < value;
});
for (; first_it != bookmarks.end(); ++first_it)
{
const auto& bookmark = *first_it;
if (bookmark.pos >= beginTime)
break;
}
if (first_it != bookmarks.end() && first_it->pos <= endTime)
{
auto last_it = std::lower_bound(bookmarks.begin(), bookmarks.end(), endTime,
[](const profiler::Bookmark& element, profiler::timestamp_t value)
{
return element.pos <= value;
});
if (last_it != bookmarks.end() && last_it->pos >= beginTime)
{
const auto begin = static_cast<profiler::block_index_t>(std::distance(bookmarks.begin(), first_it));
const auto end = static_cast<profiler::block_index_t>(std::distance(bookmarks.begin(), last_it));
if (begin <= end)
{
range.begin = begin;
range.end = end;
}
}
}
return range;
}
static BlocksMemoryAndCount calculateUsedMemoryAndBlocksCount(const profiler::BlocksTree::children_t& children,
const BlocksRange& range,
const profiler::block_getter_fn& getter,
const profiler::descriptors_list_t& descriptors,
bool contextSwitches)
{
BlocksMemoryAndCount memoryAndCount;
if (!contextSwitches)
{
for (auto i = range.begin; i < range.end; ++i)
{
const auto& child = getter(children[i]);
// Calculate self memory consumption
const auto& desc = *descriptors[child.node->id()];
uint64_t usedMemorySize = 0;
if (desc.type() == profiler::BlockType::Value)
usedMemorySize = sizeof(profiler::ArbitraryValue) + child.value->data_size();
else
usedMemorySize = sizeof(profiler::SerializedBlock) + strlen(child.node->name()) + 1;
// Calculate children memory consumption
const BlocksRange childRange(0, static_cast<profiler::block_index_t>(child.children.size()));
const auto childrenMemoryAndCount = calculateUsedMemoryAndBlocksCount(child.children, childRange,
getter, descriptors,
false);
// Accumulate memory and count
memoryAndCount += childrenMemoryAndCount;
memoryAndCount.usedMemorySize += usedMemorySize;
++memoryAndCount.blocksCount;
}
}
else
{
for (auto i = range.begin; i < range.end; ++i)
{
const auto& child = getter(children[i]);
const uint64_t usedMemorySize = BaseCSwitchSize + strlen(child.cs->name());
memoryAndCount.usedMemorySize += usedMemorySize;
++memoryAndCount.blocksCount;
}
}
return memoryAndCount;
}
//////////////////////////////////////////////////////////////////////////
static void serializeBlocks(std::ostream& output, std::vector<char>& buffer,
const profiler::BlocksTree::children_t& children, const BlocksRange& range,
const profiler::block_getter_fn& getter, const profiler::descriptors_list_t& descriptors)
{
for (auto i = range.begin; i < range.end; ++i)
{
const auto& child = getter(children[i]);
// Serialize children
const BlocksRange childRange(0, static_cast<profiler::block_index_t>(child.children.size()));
serializeBlocks(output, buffer, child.children, childRange, getter, descriptors);
// Serialize self
const auto& desc = *descriptors[child.node->id()];
uint16_t usedMemorySize = 0;
if (desc.type() == profiler::BlockType::Value)
{
usedMemorySize = static_cast<uint16_t>(sizeof(profiler::ArbitraryValue)) + child.value->data_size();
buffer.resize(usedMemorySize + sizeof(uint16_t));
unaligned_store16(buffer.data(), usedMemorySize);
memcpy(buffer.data() + sizeof(uint16_t), child.value, static_cast<size_t>(usedMemorySize));
}
else
{
usedMemorySize = static_cast<uint16_t>(sizeof(profiler::SerializedBlock)
+ strlen(child.node->name()) + 1);
buffer.resize(usedMemorySize + sizeof(uint16_t));
unaligned_store16(buffer.data(), usedMemorySize);
memcpy(buffer.data() + sizeof(uint16_t), child.node, static_cast<size_t>(usedMemorySize));
if (child.node->id() != desc.id())
{
// This block id is dynamic. Restore it's value like it was before in the input .prof file
auto block = reinterpret_cast<profiler::SerializedBlock*>(buffer.data() + sizeof(uint16_t));
block->setId(desc.id());
}
}
write(output, buffer.data(), buffer.size());
}
}
static void serializeContextSwitches(std::ostream& output, std::vector<char>& buffer,
const profiler::BlocksTree::children_t& children, const BlocksRange& range,
const profiler::block_getter_fn& getter)
{
for (auto i = range.begin; i < range.end; ++i)
{
const auto& child = getter(children[i]);
const auto usedMemorySize = static_cast<uint16_t>(BaseCSwitchSize + strlen(child.cs->name()));
buffer.resize(usedMemorySize + sizeof(uint16_t));
unaligned_store16(buffer.data(), usedMemorySize);
memcpy(buffer.data() + sizeof(uint16_t), child.cs, static_cast<size_t>(usedMemorySize));
write(output, buffer.data(), buffer.size());
}
}
static void serializeDescriptors(std::ostream& output, std::vector<char>& buffer,
const profiler::descriptors_list_t& descriptors,
profiler::block_id_t descriptors_count)
{
const size_t size = std::min(descriptors.size(), static_cast<size_t>(descriptors_count));
for (size_t i = 0; i < size; ++i)
{
const auto& desc = *descriptors[i];
if (desc.id() != i)
break;
const auto usedMemorySize = static_cast<uint16_t>(sizeof(profiler::SerializedBlockDescriptor)
+ strlen(desc.name()) + strlen(desc.file()) + 2);
buffer.resize(usedMemorySize + sizeof(uint16_t));
unaligned_store16(buffer.data(), usedMemorySize);
memcpy(buffer.data() + sizeof(uint16_t), &desc, static_cast<size_t>(usedMemorySize));
write(output, buffer.data(), buffer.size());
}
}
static void serializeBookmarks(std::ostream& output, const profiler::bookmarks_t& bookmarks, const BlocksRange& range)
{
for (auto i = range.begin; i < range.end; ++i)
{
const auto& bookmark = bookmarks[i];
const auto usedMemorySize = static_cast<uint16_t>(profiler::Bookmark::BaseSize + bookmark.text.size());
write(output, usedMemorySize);
write(output, bookmark.pos);
write(output, bookmark.color);
write(output, bookmark.text.c_str(), bookmark.text.size() + 1);
}
}
//////////////////////////////////////////////////////////////////////////
extern "C" PROFILER_API profiler::block_index_t writeTreesToFile(std::atomic<int>& progress, const char* filename,
const profiler::SerializedData& serialized_descriptors,
const profiler::descriptors_list_t& descriptors,
profiler::block_id_t descriptors_count,
const profiler::thread_blocks_tree_t& trees,
const profiler::bookmarks_t& bookmarks,
profiler::block_getter_fn block_getter,
profiler::timestamp_t begin_time,
profiler::timestamp_t end_time,
profiler::processid_t pid,
std::ostream& log)
{
if (!update_progress_write(progress, 0, log))
return 0;
std::ofstream outFile(filename, std::fstream::binary);
if (!outFile.is_open())
{
log << "Can not open file " << filename;
return 0;
}
// Write data to file
auto result = writeTreesToStream(progress, outFile, serialized_descriptors, descriptors, descriptors_count, trees,
bookmarks, std::move(block_getter), begin_time, end_time, pid, log);
return result;
}
//////////////////////////////////////////////////////////////////////////
extern "C" PROFILER_API profiler::block_index_t writeTreesToStream(std::atomic<int>& progress, std::ostream& str,
const profiler::SerializedData& serialized_descriptors,
const profiler::descriptors_list_t& descriptors,
profiler::block_id_t descriptors_count,
const profiler::thread_blocks_tree_t& trees,
const profiler::bookmarks_t& bookmarks,
profiler::block_getter_fn block_getter,
profiler::timestamp_t begin_time,
profiler::timestamp_t end_time,
profiler::processid_t pid,
std::ostream& log)
{
if (trees.empty() || serialized_descriptors.empty() || descriptors_count == 0)
{
log << "Nothing to save";
return 0;
}
BlocksMemoryAndCount total;
using ranges_t = std::unordered_map<profiler::thread_id_t, BlocksAndCSwitchesRange, estd::hash<profiler::thread_id_t> >;
ranges_t block_ranges;
// Calculate block ranges and used memory (for serialization)
profiler::timestamp_t beginTime = begin_time, endTime = end_time;
size_t i = 0;
for (const auto& kv : trees)
{
const auto id = kv.first;
const auto& tree = kv.second;
BlocksAndCSwitchesRange range;
range.blocks = findRange(tree.children, begin_time, end_time, block_getter);
range.cswitches = findRange(tree.sync, begin_time, end_time, block_getter);
range.blocksMemoryAndCount = calculateUsedMemoryAndBlocksCount(tree.children, range.blocks, block_getter,
descriptors, false);
total += range.blocksMemoryAndCount;
if (range.blocksMemoryAndCount.blocksCount != 0)
{
beginTime = std::min(beginTime, block_getter(tree.children[range.blocks.begin]).node->begin());
endTime = std::max(endTime, block_getter(tree.children[range.blocks.end - 1]).node->end());
}
range.cswitchesMemoryAndCount = calculateUsedMemoryAndBlocksCount(tree.sync, range.cswitches, block_getter,
descriptors, true);
total += range.cswitchesMemoryAndCount;
if (range.cswitchesMemoryAndCount.blocksCount != 0)
{
beginTime = std::min(beginTime, block_getter(tree.children[range.cswitches.begin]).cs->begin());
endTime = std::max(endTime, block_getter(tree.children[range.cswitches.end - 1]).cs->end());
}
block_ranges[id] = range;
if (!update_progress_write(progress, 15 / static_cast<int>(trees.size() - i), log))
return 0;
++i;
}
BlocksRange bookmarksRange;
uint16_t bookmarksCount = 0;
if (!bookmarks.empty())
{
bookmarksRange = findRange(bookmarks, begin_time, end_time);
bookmarksCount = static_cast<uint16_t>(bookmarksRange.end - bookmarksRange.begin);
if (bookmarksCount != 0)
{
beginTime = std::min(beginTime, bookmarks[bookmarksRange.begin].pos);
endTime = std::max(endTime, bookmarks[bookmarksRange.end - 1].pos);
}
}
if (total.blocksCount == 0)
{
log << "Nothing to save";
return 0;
}
const uint64_t usedMemorySizeDescriptors = serialized_descriptors.size() + descriptors_count * sizeof(uint16_t);
// Write data to stream
write(str, EASY_PROFILER_SIGNATURE);
write(str, EASY_PROFILER_VERSION);
write(str, pid);
// write 0 because we do not need to convert time from ticks to nanoseconds (it's already converted)
write<int64_t>(str, 0LL); // CPU frequency
write(str, beginTime);
write(str, endTime);
write(str, total.usedMemorySize);
write(str, usedMemorySizeDescriptors);
write(str, total.blocksCount);
write(str, descriptors_count);
write(str, static_cast<uint32_t>(trees.size()));
write(str, bookmarksCount);
write(str, static_cast<uint16_t>(0)); // padding
std::vector<char> buffer;
// Serialize all descriptors
serializeDescriptors(str, buffer, descriptors, descriptors_count);
// Serialize all blocks
i = 0;
for (const auto& kv : trees)
{
const auto id = kv.first;
const auto& tree = kv.second;
const auto& range = block_ranges.at(id);
const auto nameSize = static_cast<uint16_t>(tree.thread_name.size() + 1);
write(str, id);
write(str, nameSize);
write(str, tree.name(), nameSize);
// Serialize context switches
write(str, range.cswitchesMemoryAndCount.blocksCount);
if (range.cswitchesMemoryAndCount.blocksCount != 0)
serializeContextSwitches(str, buffer, tree.sync, range.cswitches, block_getter);
// Serialize blocks
write(str, range.blocksMemoryAndCount.blocksCount);
if (range.blocksMemoryAndCount.blocksCount != 0)
serializeBlocks(str, buffer, tree.children, range.blocks, block_getter, descriptors);
if (!update_progress_write(progress, 40 + 57 / static_cast<int>(trees.size() - i), log))
return 0;
}
write(str, EASY_PROFILER_SIGNATURE);
// Serialize bookmarks
if (bookmarksCount != 0)
{
serializeBookmarks(str, bookmarks, bookmarksRange);
write(str, EASY_PROFILER_SIGNATURE);
}
return total.blocksCount;
}
//////////////////////////////////////////////////////////////////////////