updated profiler

This commit is contained in:
Igor Korsukov 2023-10-27 20:18:01 +03:00
parent 204f25b1f1
commit 24a27b48bd
19 changed files with 604 additions and 197 deletions

View File

@ -293,7 +293,7 @@ if (WIN_PORTABLE)
add_definitions(-DWIN_PORTABLE)
endif()
add_definitions(-DHAW_PROFILER_ENABLED)
add_definitions(-DKORS_PROFILER_ENABLED)
if (MUE_ENABLE_LOAD_QML_FROM_SOURCE)
add_definitions(-DMUE_ENABLE_LOAD_QML_FROM_SOURCE)

View File

@ -21,10 +21,12 @@
*/
#include "profilerviewmodel.h"
#include "global/profiler.h"
#include "log.h"
using namespace mu::diagnostics;
using namespace haw::profiler;
using namespace mu::profiler;
ProfilerViewModel::ProfilerViewModel(QObject* parent)
: QAbstractListModel(parent)

View File

@ -23,13 +23,13 @@ set(MODULE global)
include(${CMAKE_CURRENT_LIST_DIR}/modularity/modularity.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/async/async.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/thirdparty/kors_logger/logger.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/thirdparty/haw_profiler/src/profiler.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/thirdparty/kors_profiler/profiler.cmake)
set(MODULE_SRC
${MODULARITY_SRC}
${ASYNC_SRC}
${KORS_LOGGER_SRC}
${HAW_PROFILER_SRC}
${KORS_PROFILER_SRC}
${CMAKE_CURRENT_LIST_DIR}/globaltypes.h
${CMAKE_CURRENT_LIST_DIR}/iapplication.h
${CMAKE_CURRENT_LIST_DIR}/iinteractive.h
@ -39,6 +39,7 @@ set(MODULE_SRC
${CMAKE_CURRENT_LIST_DIR}/logger.h
${CMAKE_CURRENT_LIST_DIR}/logremover.cpp
${CMAKE_CURRENT_LIST_DIR}/logremover.h
${CMAKE_CURRENT_LIST_DIR}/profiler.h
${CMAKE_CURRENT_LIST_DIR}/dataformatter.cpp
${CMAKE_CURRENT_LIST_DIR}/dataformatter.h
${CMAKE_CURRENT_LIST_DIR}/muversion.cpp

View File

@ -24,9 +24,10 @@
#include "modularity/ioc.h"
#include "internal/globalconfiguration.h"
#include "log.h"
#include "logger.h"
#include "logremover.h"
#include "thirdparty/kors_logger/src/logdefdest.h"
#include "profiler.h"
#include "muversion.h"
#include "internal/application.h"
@ -44,6 +45,8 @@
#include "diagnostics/idiagnosticspathsregister.h"
#include "log.h"
using namespace mu::framework;
using namespace mu::modularity;
using namespace mu::io;
@ -78,7 +81,7 @@ void GlobalModule::onPreInit(const IApplication::RunMode& mode)
settings()->load();
//! --- Setup logger ---
using namespace kors::logger;
using namespace mu::logger;
Logger* logger = Logger::instance();
logger->clearDests();
@ -131,7 +134,7 @@ void GlobalModule::onPreInit(const IApplication::RunMode& mode)
LOGI() << "=== Started MuseScore " << framework::MUVersion::fullVersion() << ", build number " << MUSESCORE_BUILD_NUMBER << " ===";
//! --- Setup profiler ---
using namespace haw::profiler;
using namespace mu::profiler;
struct MyPrinter : public Profiler::Printer
{
void printDebug(const std::string& str) override { LOG_STREAM(Logger::DEBG, "Profiler", Color::Magenta)() << str; }
@ -143,7 +146,7 @@ void GlobalModule::onPreInit(const IApplication::RunMode& mode)
profOpt.funcsTimeEnabled = true;
profOpt.funcsTraceEnabled = false;
profOpt.funcsMaxThreadCount = 100;
profOpt.dataTopCount = 150;
profOpt.statTopCount = 150;
Profiler* profiler = Profiler::instance();
profiler->setup(profOpt, new MyPrinter());

View File

@ -26,7 +26,7 @@
#include <cstdlib>
#include <cassert>
#include "thirdparty/haw_profiler/src/profiler.h"
#include "profiler.h"
#include "logger.h"
#undef FALLTHROUGH

View File

@ -23,6 +23,7 @@
#define MU_LOGGER_H
#include "thirdparty/kors_logger/src/logger.h"
#include "thirdparty/kors_logger/src/logdefdest.h"
#include "thirdparty/kors_logger/src/log_base.h"
namespace mu::logger {
@ -30,6 +31,9 @@ using Logger = kors::logger::Logger;
using Type = kors::logger::Type;
using Level = kors::logger::Level;
using Color = kors::logger::Color;
using LogLayout = kors::logger::LogLayout;
using ConsoleLogDest = kors::logger::ConsoleLogDest;
using FileLogDest = kors::logger::FileLogDest;
}
#endif // MU_LOGGER_H

View File

@ -0,0 +1,31 @@
/*
* SPDX-License-Identifier: GPL-3.0-only
* MuseScore-CLA-applies
*
* MuseScore
* Music Composition & Notation
*
* Copyright (C) 2021 MuseScore BVBA and others
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
#ifndef MU_PROFILER_H
#define MU_PROFILER_H
#include "thirdparty/kors_profiler/src/profiler.h"
namespace mu::profiler {
using Profiler = kors::profiler::Profiler;
}
#endif // MU_PROFILER_H

View File

@ -1,2 +0,0 @@
*.user
build-*

View File

@ -1,22 +0,0 @@
Simple profiler
---------------
Simple, embedded profiler with very small overhead
(ported from https://github.com/igorkorsukov/qzebradev)
Features:
* Embedded profiler (can run anywhere and anytime)
* Function duration measure
* Steps duration measure
* Very small overhead
* Enabled / disabled on compile time and run time
* Thread safe (without use mutex)
* Custom data printer
[Example](tests/main.cpp)
To use Profiler within your software project include the Profiler source into your project
Source:
* profiler.h/cpp - profiler and macros
or see and include `src/profiler.cmake` in the cmake project

View File

@ -1,13 +0,0 @@
set(HAW_PROFILER_SRC
${CMAKE_CURRENT_LIST_DIR}/profiler.cpp
${CMAKE_CURRENT_LIST_DIR}/profiler.h
)
set(HAW_PROFILER_INC
${CMAKE_CURRENT_LIST_DIR}
)
if (MSVC)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4456")
endif (MSVC)

View File

@ -1,21 +0,0 @@
cmake_minimum_required(VERSION 3.14)
project(haw_profiler LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(${CMAKE_CURRENT_LIST_DIR}/../src/profiler.cmake)
include_directories(${HAW_PROFILER_INC})
add_executable(haw_profiler
main.cpp
${HAW_PROFILER_SRC}
)
target_link_libraries(haw_profiler
pthread
)

View File

@ -0,0 +1,35 @@
# profiler
Simple, embedded profiler with very small overhead
Requires C++17 and higher.
Features:
* Embedded profiler (can run anywhere and anytime)
* Function duration measure
* Steps duration measure
* Very small overhead
* Enabled / disabled on compile time and run time
* Thread safe (without use mutex)
* Custom data printer
[Example](example/main.cpp)
Used in at least two private commercial projects and one [open source](https://github.com/musescore/MuseScore).
## Integration
To use Profiler within your software project include the Profiler source into your project
Source:
* profiler.h/cpp - profiler and macros
* funcinfo.h - macros for parsing signatures
or see and include `profiler.cmake` in the cmake project
## ChangeLog
### v1.1
* Improved parsing of function signatures
* Fixed get threads data
### v1.0
* Ported from [https://github.com/igorkorsukov/qzebradev](https://github.com/igorkorsukov/qzebradev)

View File

@ -0,0 +1,22 @@
cmake_minimum_required(VERSION 3.14)
project(profiler_example LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
include(${CMAKE_CURRENT_LIST_DIR}/../profiler.cmake)
include_directories(${KORS_PROFILER_INC})
add_definitions(-DKORS_PROFILER_ENABLED)
add_executable(${PROJECT_NAME}
main.cpp
${KORS_PROFILER_SRC}
)
target_link_libraries(${PROJECT_NAME}
pthread
)

View File

@ -72,7 +72,7 @@ public:
int main(int argc, char* argv[])
{
std::clog << "Hello World, I am Profiler\n";
std::cout << "Hello World, I am Profiler" << std::endl;
Example t;
t.example();
@ -101,4 +101,25 @@ int main(int argc, char* argv[])
Example::th_func 2.212 ms 1 2.212 ms
*/
//! NOTE Custom setup
// custom printer
using namespace kors::profiler;
struct MyPrinter : public Profiler::Printer
{
void printDebug(const std::string& str) override { std::clog << str << std::endl; }
void printInfo(const std::string& str) override { std::clog << str << std::endl; }
};
// options
Profiler::Options profOpt;
profOpt.stepTimeEnabled = true; // enable measure of steps, macros: BEGIN_STEP_TIME, STEP_TIME
profOpt.funcsTimeEnabled = true; // enable measure of functions, macros: TRACEFUNC, TRACEFUNC_C
profOpt.funcsTraceEnabled = false; // enable trace (output by func call), macros: TRACEFUNC, TRACEFUNC_C
profOpt.funcsMaxThreadCount = 100; // max treads count
profOpt.statTopCount = 150; // statistic top count
Profiler* profiler = Profiler::instance();
profiler->setup(profOpt, new MyPrinter());
}

View File

@ -0,0 +1,9 @@
set(KORS_PROFILER_SRC
${CMAKE_CURRENT_LIST_DIR}/src/profiler.cpp
${CMAKE_CURRENT_LIST_DIR}/src/profiler.h
)
set(KORS_PROFILER_INC
${CMAKE_CURRENT_LIST_DIR}/src
)

View File

@ -0,0 +1,339 @@
/*
MIT License
Copyright (c) 2023 Igor Korsukov
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.
*/
#ifndef KORS__FI_H
#define KORS__FI_H
#include <string_view>
#include <algorithm>
#ifndef KORS_FUNC_SIG
#if defined(_MSC_VER)
#define FUNC_SIG __FUNCSIG__
#else
#define FUNC_SIG __PRETTY_FUNCTION__
#endif
#endif
#if defined(__GNUC__) && !defined(__clang__)
#if (__GNUC__ < 11)
#define _KORS_NO_STRINGVIEW_CONSTEXPR_METHODS
#endif
#endif
#define FUNCNAME kors::funcinfo::funcNameBySig(FUNC_SIG)
#define CLASSNAME kors::funcinfo::classNameBySig(FUNC_SIG)
#define CLASSFUNC kors::funcinfo::classFuncBySig(FUNC_SIG)
#define MODULENAME kors::funcinfo::moduleNameBySig(FUNC_SIG)
namespace kors::funcinfo {
#ifndef _KORS_NO_STRINGVIEW_CONSTEXPR_METHODS
//! NOTE Signature maybe like:
//! * ReturnType funcName(...)
//! * ReturnType some_ns::maybesub::funcName(...)
//! * ReturnType Class::funcName(...)
//! * ReturnType some_ns::maybesub::Class::funcName(...)
constexpr std::string_view funcNameBySig(const std::string_view& sig)
{
constexpr std::string_view Colon("::");
constexpr std::string_view ArgBegin("(");
constexpr std::string_view Space(" ");
std::size_t endFunc = sig.find_first_of(ArgBegin);
if (endFunc == std::string_view::npos) {
return sig;
}
std::size_t beginFunc = sig.find_last_of(Colon, endFunc);
if (beginFunc == std::string_view::npos) {
beginFunc = sig.find_last_of(Space, endFunc);
}
if (beginFunc == std::string_view::npos) {
beginFunc = 0;
} else {
beginFunc += 1;
}
return sig.substr(beginFunc, (endFunc - beginFunc));
}
//! NOTE Signature maybe like:
//! * ReturnType Class::funcName(...)
//! * ReturnType some_ns::maybesub::Class::funcName(...)
constexpr std::string_view classNameBySig(const std::string_view& sig)
{
constexpr std::string_view Colon("::");
constexpr std::string_view ArgBegin("(");
constexpr std::string_view Space(" ");
std::size_t endFunc = sig.find_first_of(ArgBegin);
if (endFunc == std::string_view::npos) {
return sig;
}
std::size_t beginFunc = sig.find_last_of(Colon, endFunc);
if (beginFunc == std::string_view::npos) {
return std::string_view();
}
std::size_t beginClassColon = sig.find_last_of(Colon, beginFunc - 2);
std::size_t beginClassSpace = sig.find_last_of(Space, beginFunc - 2);
std::size_t beginClass = std::string_view::npos;
if (beginClassColon == std::string_view::npos) {
beginClass = beginClassSpace;
} else if (beginClassSpace == std::string_view::npos) {
beginClass = beginClassColon;
} else {
beginClass = std::max(beginClassColon, beginClassSpace);
}
if (beginClass == std::string_view::npos) {
beginClass = 0;
} else {
beginClass += 1;
}
return sig.substr(beginClass, (beginFunc - 1 - beginClass));
}
//! NOTE Signature maybe like:
//! * ReturnType Class::funcName(...)
//! * ReturnType some_ns::maybesub::Class::funcName(...)
constexpr std::string_view classFuncBySig(const std::string_view& sig)
{
constexpr std::string_view Colon("::");
constexpr std::string_view ArgBegin("(");
constexpr std::string_view Space(" ");
std::size_t endFunc = sig.find_first_of(ArgBegin);
if (endFunc == std::string_view::npos) {
return sig;
}
std::size_t beginFunc = sig.find_last_of(Colon, endFunc);
if (beginFunc == std::string_view::npos) {
return funcNameBySig(sig);
}
std::size_t beginClassColon = sig.find_last_of(Colon, beginFunc - 2);
std::size_t beginClassSpace = sig.find_last_of(Space, beginFunc - 2);
std::size_t beginClass = std::string_view::npos;
if (beginClassColon == std::string_view::npos) {
beginClass = beginClassSpace;
} else if (beginClassSpace == std::string_view::npos) {
beginClass = beginClassColon;
} else {
beginClass = std::max(beginClassColon, beginClassSpace);
}
if (beginClass == std::string_view::npos) {
beginClass = 0;
} else {
beginClass += 1;
}
return sig.substr(beginClass, (endFunc - beginClass));
}
//! NOTE Signature should be like
//! ReturnType xxx::modulename::maybesub::ClassName::methodName()
constexpr std::string_view moduleNameBySig(const std::string_view& sig)
{
constexpr std::string_view ArgBegin("(");
constexpr std::string_view Space(" ");
constexpr std::string_view Colon("::");
std::size_t endFunc = sig.find_first_of(ArgBegin);
if (endFunc == std::string_view::npos) {
return sig;
}
std::size_t beginFunc = sig.find_last_of(Space, endFunc);
if (beginFunc == std::string_view::npos) {
return std::string_view();
}
size_t beginModule = sig.find_first_of(Colon, beginFunc) + 2;
if (beginModule == std::string_view::npos) {
return std::string_view();
}
size_t endModule = sig.find_first_of(Colon, beginModule);
if (endModule == std::string_view::npos) {
return std::string_view();
}
return sig.substr(beginModule, (endModule - beginModule));
}
#else
//! NOTE Signature maybe like:
//! * ReturnType funcName(...)
//! * ReturnType some_ns::maybesub::funcName(...)
//! * ReturnType Class::funcName(...)
//! * ReturnType some_ns::maybesub::Class::funcName(...)
inline std::string_view funcNameBySig(const std::string_view& sig)
{
static const std::string_view Colon("::");
static const std::string_view ArgBegin("(");
static const std::string_view Space(" ");
std::size_t endFunc = sig.find_first_of(ArgBegin);
if (endFunc == std::string_view::npos) {
return sig;
}
std::size_t beginFunc = sig.find_last_of(Colon, endFunc);
if (beginFunc == std::string_view::npos) {
beginFunc = sig.find_last_of(Space, endFunc);
}
if (beginFunc == std::string_view::npos) {
beginFunc = 0;
} else {
beginFunc += 1;
}
return sig.substr(beginFunc, (endFunc - beginFunc));
}
//! NOTE Signature maybe like:
//! * ReturnType Class::funcName(...)
//! * ReturnType some_ns::maybesub::Class::funcName(...)
inline std::string_view classNameBySig(const std::string_view& sig)
{
static const std::string_view Colon("::");
static const std::string_view ArgBegin("(");
static const std::string_view Space(" ");
std::size_t endFunc = sig.find_first_of(ArgBegin);
if (endFunc == std::string_view::npos) {
return sig;
}
std::size_t beginFunc = sig.find_last_of(Colon, endFunc);
if (beginFunc == std::string_view::npos) {
return std::string_view();
}
std::size_t beginClassColon = sig.find_last_of(Colon, beginFunc - 2);
std::size_t beginClassSpace = sig.find_last_of(Space, beginFunc - 2);
std::size_t beginClass = std::string_view::npos;
if (beginClassColon == std::string_view::npos) {
beginClass = beginClassSpace;
} else if (beginClassSpace == std::string_view::npos) {
beginClass = beginClassColon;
} else {
beginClass = std::max(beginClassColon, beginClassSpace);
}
if (beginClass == std::string_view::npos) {
beginClass = 0;
} else {
beginClass += 1;
}
return sig.substr(beginClass, (beginFunc - 1 - beginClass));
}
//! NOTE Signature maybe like:
//! * ReturnType Class::funcName(...)
//! * ReturnType some_ns::maybesub::Class::funcName(...)
inline std::string_view classFuncBySig(const std::string_view& sig)
{
static const std::string_view Colon("::");
static const std::string_view ArgBegin("(");
static const std::string_view Space(" ");
std::size_t endFunc = sig.find_first_of(ArgBegin);
if (endFunc == std::string_view::npos) {
return sig;
}
std::size_t beginFunc = sig.find_last_of(Colon, endFunc);
if (beginFunc == std::string_view::npos) {
return funcNameBySig(sig);
}
std::size_t beginClassColon = sig.find_last_of(Colon, beginFunc - 2);
std::size_t beginClassSpace = sig.find_last_of(Space, beginFunc - 2);
std::size_t beginClass = std::string_view::npos;
if (beginClassColon == std::string_view::npos) {
beginClass = beginClassSpace;
} else if (beginClassSpace == std::string_view::npos) {
beginClass = beginClassColon;
} else {
beginClass = std::max(beginClassColon, beginClassSpace);
}
if (beginClass == std::string_view::npos) {
beginClass = 0;
} else {
beginClass += 1;
}
return sig.substr(beginClass, (endFunc - beginClass));
}
//! NOTE Signature should be like
//! ReturnType xxx::modulename::maybesub::ClassName::methodName()
inline std::string_view moduleNameBySig(const std::string_view& sig)
{
static const std::string_view ArgBegin("(");
static const std::string_view Space(" ");
static const std::string_view Colon("::");
std::size_t endFunc = sig.find_first_of(ArgBegin);
if (endFunc == std::string_view::npos) {
return sig;
}
std::size_t beginFunc = sig.find_last_of(Space, endFunc);
if (beginFunc == std::string_view::npos) {
return std::string_view();
}
size_t beginModule = sig.find_first_of(Colon, beginFunc) + 2;
if (beginModule == std::string_view::npos) {
return std::string_view();
}
size_t endModule = sig.find_first_of(Colon, beginModule);
if (endModule == std::string_view::npos) {
return std::string_view();
}
return sig.substr(beginModule, (endModule - beginModule));
}
#endif // _KORS_NO_STRINGVIEW_CONSTEXPR_METHODS
}
#endif // KORS__FI_H

View File

@ -1,24 +1,41 @@
/*
MIT License
Copyright (c) 2020 Igor Korsukov
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.
*/
#include "profiler.h"
#include <iostream>
#include <iomanip>
#include <algorithm>
#include <iomanip>
#include <errno.h>
#include <stdio.h>
using namespace haw::profiler;
using namespace kors::profiler;
Profiler::Options Profiler::m_options;
constexpr int MAIN_THREAD_INDEX(0);
Profiler* Profiler::instance()
{
static Profiler p;
return &p;
}
Profiler::Profiler()
{
setup(Options(), new Printer());
@ -29,6 +46,12 @@ Profiler::~Profiler()
delete m_printer;
}
Profiler* Profiler::instance()
{
static Profiler p;
return &p;
}
void Profiler::setup(const Options& opt, Printer* printer)
{
m_options = opt;
@ -165,7 +188,7 @@ void Profiler::clear()
}
{
std::lock_guard<std::mutex> lock(m_funcs.mutex);
for (auto st : m_steps.timers) {
for (auto& st : m_steps.timers) {
delete st.second;
}
m_steps.timers.clear();
@ -187,7 +210,7 @@ Profiler::Data Profiler::threadsData(Data::Mode mode) const
std::thread::id empty;
for (size_t i = 0; i < funcsThreads.size(); ++i) {
std::thread::id th = funcsThreads[i];
std::thread::id th = funcsThreads.at(i);
if (th == empty) {
break;
}
@ -205,7 +228,7 @@ Profiler::Data Profiler::threadsData(Data::Mode mode) const
Data::Thread thdata;
thdata.thread = th;
const FuncTimers& timers = funcsTimers[i];
const FuncTimers& timers = funcsTimers.at(i);
for (auto it : timers) {
const FuncTimer* ft = it.second;
Data::Func f(
@ -232,13 +255,13 @@ Profiler::Data Profiler::threadsData(Data::Mode mode) const
std::string Profiler::threadsDataString(Data::Mode mode) const
{
Profiler::Data data = threadsData(mode);
return printer()->formatData(data, mode, m_options.dataTopCount);
return printer()->formatData(data, mode, m_options.statTopCount);
}
void Profiler::printThreadsData(Data::Mode mode) const
{
Profiler::Data data = threadsData(mode);
printer()->printData(data, mode, m_options.dataTopCount);
printer()->printData(data, mode, m_options.statTopCount);
}
void Profiler::print(const std::string& str)
@ -266,44 +289,6 @@ bool Profiler::save_file(const std::string& path, const std::string& content)
return count > 0;
}
std::string FuncMarker::formatSig(const std::string& sig)
{
static const std::string Coln("::");
static const std::string Spc(" ");
static const std::string ArgBeg("(");
std::size_t endFunc = sig.find_first_of(ArgBeg);
if (endFunc == std::string::npos) {
return sig;
}
std::size_t beginFunc = sig.find_last_of(Coln, endFunc);
if (beginFunc == std::string::npos) {
return sig;
}
std::size_t beginClassColon = sig.find_last_of(Coln, beginFunc - 2);
std::size_t beginClassSpace = sig.find_last_of(Spc, beginFunc - 2);
std::size_t beginClass = std::string::npos;
if (beginClassColon == std::string::npos) {
beginClass = beginClassSpace;
} else if (beginClassSpace == std::string::npos) {
beginClass = beginClassColon;
} else {
beginClass = std::max(beginClassColon, beginClassSpace);
}
if (beginClass == std::string::npos) {
beginClass = beginFunc;
} else {
beginClass += 1;
}
std::string str = sig.substr(beginClass, (endFunc - beginClass));
return str;
}
double Profiler::StepTimer::beginMs() const
{
return beginTime.mlsecsElapsed();
@ -373,7 +358,7 @@ using mclock = std::chrono::high_resolution_clock;
void Profiler::ElapsedTimer::start()
{
_start = mclock::now();
m_start = mclock::now();
}
void Profiler::ElapsedTimer::restart()
@ -384,45 +369,42 @@ void Profiler::ElapsedTimer::restart()
double Profiler::ElapsedTimer::mlsecsElapsed() const
{
auto end = mclock::now();
std::chrono::duration<double, std::milli> elapsed = end - _start;
std::chrono::duration<double, std::milli> elapsed = end - m_start;
return elapsed.count();
}
void Profiler::ElapsedTimer::invalidate()
{
_start = mclock::time_point();
m_start = mclock::time_point();
}
bool Profiler::ElapsedTimer::isValid() const
{
const mclock::duration since_epoch = _start.time_since_epoch();
const mclock::duration since_epoch = m_start.time_since_epoch();
return since_epoch.count() > 0;
}
Profiler::Printer::~Printer()
{}
void Profiler::Printer::printDebug(const std::string& str)
{
std::cout << str << '\n';
std::cout << str << std::endl;
}
void Profiler::Printer::printInfo(const std::string& str)
{
std::cout << str << '\n';
std::cout << str << std::endl;
}
static std::string formatDouble(double val, size_t prec)
std::string Profiler::Printer::formatDouble(double val, size_t prec)
{
std::stringstream ss;
ss << std::fixed << std::setprecision(static_cast<int>(prec)) << val;
ss << std::fixed << std::setprecision(prec) << val;
return ss.str();
}
void Profiler::Printer::printStep(const std::string& tag, double beginMs, double stepMs, const std::string& info)
{
static const std::string COLN(" : ");
static const std::string COLON(" : ");
static const std::string SEP("/");
static const std::string MS(" ms: ");
@ -431,7 +413,7 @@ void Profiler::Printer::printStep(const std::string& tag, double beginMs, double
str
.append(tag)
.append(COLN)
.append(COLON)
.append(formatDouble(beginMs, 3))
.append(SEP)
.append(formatDouble(stepMs, 3))
@ -480,15 +462,17 @@ void Profiler::Printer::printData(const Data& data, Data::Mode mode, int maxcoun
printInfo(formatData(data, mode, maxcount));
}
namespace {
struct IsLessBySum {
bool operator()(const Profiler::Data::Func& f, const Profiler::Data::Func& s) const
{
return f.sumtimeMs > s.sumtimeMs;
}
};
}
std::string Profiler::Printer::formatData(const Data& data, Data::Mode mode, int maxcount) const
{
struct IsLessBySum {
bool operator()(const Profiler::Data::Func& f, const Profiler::Data::Func& s) const
{
return f.sumtimeMs > s.sumtimeMs;
}
};
std::stringstream stream;
stream << "\n\n";
@ -543,27 +527,25 @@ std::string Profiler::Printer::formatData(const Data& data, Data::Mode mode, int
return stream.str();
}
std::string Profiler::Printer::leftJustified(const std::string& in, size_t width)
{
std::string out = in;
if (width > out.size()) {
out.resize(width, ' ');
}
return out;
}
#define FORMAT(str, width) leftJustified(str, width)
#define TITLE(str) FORMAT(std::string(str), 18)
#define VALUE(val, unit) FORMAT(std::to_string(val) + unit, 18)
#define VALUE_D(val, unit) FORMAT(formatDouble(val, 3) + unit, 18)
void Profiler::Printer::funcsToStream(std::stringstream& stream,
const std::string& title,
const std::list<Data::Func>& funcs,
int count) const
{
auto leftJustified = [](const std::string& val, size_t width) -> std::string
{
std::string str;
str.resize(width, ' ');
size_t lenght = width < val.size() ? width : val.size();
for (size_t i = 0; i < lenght; ++i) {
str[i] = val[i];
}
return str;
};
#define FORMAT(str, width) leftJustified(str, width)
#define TITLE(str) FORMAT(std::string(str), 18)
#define VALUE(val, unit) FORMAT(std::to_string(val) + unit, 18)
#define VALUE_D(val, unit) FORMAT(formatDouble(val, 3) + unit, 18)
stream << title << "\n";
stream << FORMAT("Function", 60) << TITLE("Call time") << TITLE("Call count") << TITLE("Sum time") << "\n";
@ -580,10 +562,5 @@ void Profiler::Printer::funcsToStream(std::stringstream& stream,
}
}
#undef FORMAT
#undef TITLE
#undef VALUE
#undef VALUE_D
stream << "\n\n";
}

View File

@ -1,7 +1,31 @@
#ifndef HAW_PROFILER_H
#define HAW_PROFILER_H
/*
MIT License
Copyright (c) 2020 Igor Korsukov
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.
*/
#ifndef KORS_PROFILER_H
#define KORS_PROFILER_H
#include <string>
#include <string_view>
#include <list>
#include <vector>
#include <set>
@ -11,46 +35,42 @@
#include <chrono>
#include <sstream>
#ifndef FUNC_INFO
#if defined(_MSC_VER)
#define FUNC_INFO __FUNCSIG__
#else
#define FUNC_INFO __PRETTY_FUNCTION__
#endif
#endif
#include "funcinfo.h"
#ifdef HAW_PROFILER_ENABLED
// #define KORS_PROFILER_ENABLED
#ifdef KORS_PROFILER_ENABLED
#ifndef TRACEFUNC
#define TRACEFUNC \
static std::string __func_info(haw::profiler::FuncMarker::formatSig(FUNC_INFO)); \
haw::profiler::FuncMarker __funcMarker(__func_info);
static std::string __func_info(CLASSFUNC); \
kors::profiler::FuncMarker __funcMarker(__func_info);
#endif
#ifndef TRACEFUNC_C
#define TRACEFUNC_C(info) \
static std::string __func_info(info); \
haw::profiler::FuncMarker __funcMarkerInfo(__func_info);
kors::profiler::FuncMarker __funcMarkerInfo(__func_info);
#endif
#ifndef BEGIN_STEP_TIME
#define BEGIN_STEP_TIME(tag) \
if (haw::profiler::Profiler::options().stepTimeEnabled) \
{ haw::profiler::Profiler::instance()->stepTime(tag, std::string("Begin"), true); }
if (kors::profiler::Profiler::options().stepTimeEnabled) \
{ kors::profiler::Profiler::instance()->stepTime(tag, std::string("Begin"), true); }
#endif
#ifndef STEP_TIME
#define STEP_TIME(tag, info) \
if (haw::profiler::Profiler::options().stepTimeEnabled) \
{ haw::profiler::Profiler::instance()->stepTime(tag, info); }
if (kors::profiler::Profiler::options().stepTimeEnabled) \
{ kors::profiler::Profiler::instance()->stepTime(tag, info); }
#endif
#ifndef PROFILER_CLEAR
#define PROFILER_CLEAR haw::profiler::Profiler::instance()->clear();
#define PROFILER_CLEAR kors::profiler::Profiler::instance()->clear();
#endif
#ifndef PROFILER_PRINT
#define PROFILER_PRINT haw::profiler::Profiler::instance()->printThreadsData();
#define PROFILER_PRINT kors::profiler::Profiler::instance()->printThreadsData();
#endif
#else
@ -64,7 +84,7 @@
#endif
namespace haw::profiler {
namespace kors::profiler {
class Profiler
{
public:
@ -72,12 +92,12 @@ public:
static Profiler* instance();
struct Options {
bool stepTimeEnabled{ true };
bool funcsTimeEnabled{ true };
bool funcsTraceEnabled{ false };
size_t funcsMaxThreadCount{ 100 };
int dataTopCount{ 150 };
Options() {}
bool stepTimeEnabled = true;
bool funcsTimeEnabled = true;
bool funcsTraceEnabled = false;
size_t funcsMaxThreadCount = 100;
int statTopCount = 150;
};
struct Data {
@ -89,8 +109,8 @@ public:
struct Func {
std::string func;
long callcount{ 0 };
double sumtimeMs{ 0. };
long callcount = 0;
double sumtimeMs = 0.0;
Func() {}
Func(const std::string& f, long cc, double st)
: func(f), callcount(cc), sumtimeMs(st) {}
@ -106,7 +126,7 @@ public:
};
struct Printer {
virtual ~Printer();
virtual ~Printer() = default;
virtual void printDebug(const std::string& str);
virtual void printInfo(const std::string& str);
virtual void printStep(const std::string& tag, double beginMs, double stepMs, const std::string& info);
@ -116,6 +136,9 @@ public:
virtual std::string formatData(const Data& data, Data::Mode mode, int maxcount) const;
virtual void funcsToStream(std::stringstream& stream, const std::string& title, const std::list<Data::Func>& funcs,
int count) const;
static std::string formatDouble(double val, size_t prec);
static std::string leftJustified(const std::string& in, size_t width);
};
struct ElapsedTimer {
@ -126,14 +149,14 @@ public:
bool isValid() const;
private:
std::chrono::high_resolution_clock::time_point _start;
std::chrono::high_resolution_clock::time_point m_start;
};
struct FuncTimer {
const std::string& func;
ElapsedTimer timer;
long callcount;
double sumtimeMs;
long callcount = 0;
double sumtimeMs = 0.0;
explicit FuncTimer(const std::string& f)
: func(f), callcount(0), sumtimeMs(0) {}
};
@ -199,12 +222,12 @@ private:
bool save_file(const std::string& path, const std::string& content);
Printer* m_printer{ nullptr };
Printer* m_printer = nullptr;
StepsData m_steps;
mutable FuncsData m_funcs;
size_t m_stackCounter{ 0 };
size_t m_stackCounter = 0;
};
struct FuncMarker
@ -224,11 +247,9 @@ struct FuncMarker
}
}
static std::string formatSig(const std::string& sig);
Profiler::FuncTimer* timer{ nullptr };
Profiler::FuncTimer* timer = nullptr;
const std::string& func;
};
}
#endif // XTZ_PROFILER_H
#endif // KORS_PROFILER_H