mirror of
https://github.com/InoriRus/Kyty.git
synced 2026-08-28 05:06:40 +00:00
Initial commit
This commit is contained in:
@@ -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
@@ -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)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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_
|
||||
@@ -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
@@ -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
@@ -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_
|
||||
@@ -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 = "";
|
||||
}
|
||||
@@ -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
|
||||
+1698
File diff suppressed because it is too large
Load Diff
+226
@@ -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
@@ -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".
|
||||
+1561
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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
@@ -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
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
Reference in New Issue
Block a user