mirror of
https://github.com/yse/easy_profiler.git
synced 2024-12-26 16:11:02 +08:00
Merge remote-tracking branch 'origin/develop' into saving_file_develop
This commit is contained in:
commit
0b9ee4e6d0
@ -11,3 +11,13 @@ include_directories(./include)
|
|||||||
|
|
||||||
add_executable(profiler_converter ${HEADER_FILES} ${CPP_FILES} main.cpp)
|
add_executable(profiler_converter ${HEADER_FILES} ${CPP_FILES} main.cpp)
|
||||||
target_link_libraries(profiler_converter easy_profiler)
|
target_link_libraries(profiler_converter easy_profiler)
|
||||||
|
|
||||||
|
install(
|
||||||
|
TARGETS
|
||||||
|
profiler_converter
|
||||||
|
RUNTIME
|
||||||
|
DESTINATION
|
||||||
|
bin
|
||||||
|
)
|
||||||
|
|
||||||
|
set_property(TARGET profiler_converter PROPERTY INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||||
|
@ -1,70 +1,138 @@
|
|||||||
///this
|
/**
|
||||||
#include "converter.h"
|
Lightweight profiler library for c++
|
||||||
/// reader
|
Copyright(C) 2016-2018 Sergey Yagovtsev, Victor Zarubkin
|
||||||
#include "reader.h"
|
|
||||||
|
|
||||||
|
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 "converter.h"
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
void JSONConverter::readThreadBlocks(const profiler::reader::BlocksTreeNode &node,nlohmann::json& json)
|
void JsonExporter::convert(const profiler::reader::BlocksTreeNode& node, nlohmann::json& json) const
|
||||||
{
|
{
|
||||||
auto j_local = nlohmann::json::object();
|
if (node.info.descriptor != nullptr)
|
||||||
|
|
||||||
if(node.current_block != nullptr){
|
|
||||||
json = {{"id",static_cast<int>(node.current_block->blockId)}};
|
|
||||||
json["start"] = (node.current_block->beginTime);
|
|
||||||
json["stop"] = (node.current_block->endTime);
|
|
||||||
///read data from block desciptor
|
|
||||||
if(node.current_block->descriptor)
|
|
||||||
{
|
|
||||||
json["compileTimeName"] = node.current_block->descriptor->compileTimeName;
|
|
||||||
|
|
||||||
std::stringstream stream;
|
|
||||||
stream << "0x"
|
|
||||||
<< std::hex << node.current_block->descriptor->argbColor;
|
|
||||||
std::string result( stream.str() );
|
|
||||||
|
|
||||||
json["color"] = result;
|
|
||||||
json["blockType"] = node.current_block->descriptor->blockType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto jsonObjects = nlohmann::json::array();
|
|
||||||
|
|
||||||
for(const auto &value : node.children)
|
|
||||||
{
|
{
|
||||||
jsonObjects.push_back(nlohmann::json::object());
|
json = {{"id", node.info.blockIndex},
|
||||||
readThreadBlocks(*value.get(),jsonObjects.back());
|
{"name", node.info.descriptor->blockName},
|
||||||
|
{"start", node.info.beginTime},
|
||||||
|
{"stop", node.info.endTime},
|
||||||
|
{"descriptor", node.info.descriptor->id}};
|
||||||
}
|
}
|
||||||
|
|
||||||
json["children"] = jsonObjects;
|
convertChildren(node, json);
|
||||||
}
|
}
|
||||||
|
|
||||||
void JSONConverter::convert()
|
void JsonExporter::convertChildren(const profiler::reader::BlocksTreeNode& node, nlohmann::json& json) const
|
||||||
|
{
|
||||||
|
if (node.children.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto children = nlohmann::json::array();
|
||||||
|
for (const auto& child : node.children)
|
||||||
|
{
|
||||||
|
children.emplace_back();
|
||||||
|
convert(child, children.back());
|
||||||
|
}
|
||||||
|
|
||||||
|
json["children"] = children;
|
||||||
|
}
|
||||||
|
|
||||||
|
void JsonExporter::convert(const ::std::string& inputFile, const ::std::string& outputFile) const
|
||||||
{
|
{
|
||||||
profiler::reader::FileReader fr;
|
profiler::reader::FileReader fr;
|
||||||
fr.readFile(m_file_in);
|
if (fr.readFile(inputFile) == 0)
|
||||||
const profiler::reader::thread_blocks_tree_t &blocks_tree = fr.getBlocksTreeData();
|
return;
|
||||||
nlohmann::json json;
|
|
||||||
json["version"] = fr.getVersion();
|
|
||||||
json["timeUnit"] = "ns";
|
|
||||||
auto jsonObjects = nlohmann::json::array();
|
|
||||||
for(const auto &value : blocks_tree)
|
|
||||||
{
|
|
||||||
jsonObjects.push_back(nlohmann::json::object());
|
|
||||||
jsonObjects.back()["threadId"] = value.first;
|
|
||||||
jsonObjects.back()["name"] = fr.getThreadName(value.first);
|
|
||||||
readThreadBlocks(value.second,jsonObjects.back());
|
|
||||||
|
|
||||||
}
|
nlohmann::json json = {{"version", fr.getVersionString()}, {"timeUnits", "ns"}};
|
||||||
json["threads"] = jsonObjects;
|
|
||||||
|
|
||||||
if(!m_file_out.empty())
|
auto descriptors = nlohmann::json::array();
|
||||||
|
const auto& block_descriptors = fr.getBlockDescriptors();
|
||||||
|
for (const auto& descriptor : block_descriptors)
|
||||||
{
|
{
|
||||||
std::ofstream file(m_file_out);
|
descriptors.emplace_back();
|
||||||
file << json;
|
|
||||||
|
std::stringstream stream;
|
||||||
|
stream << "0x" << std::hex << descriptor.argbColor;
|
||||||
|
|
||||||
|
auto& desc = descriptors.back();
|
||||||
|
|
||||||
|
desc["id"] = descriptor.id;
|
||||||
|
if (descriptor.parentId != descriptor.id)
|
||||||
|
desc["parentId"] = descriptor.parentId;
|
||||||
|
|
||||||
|
desc["name"] = descriptor.blockName;
|
||||||
|
desc["type"] = descriptor.blockType;
|
||||||
|
desc["color"] = stream.str();
|
||||||
|
desc["sourceFile"] = descriptor.fileName;
|
||||||
|
desc["sourceLine"] = descriptor.lineNumber;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
json["blockDescriptors"] = descriptors;
|
||||||
|
|
||||||
|
auto threads = nlohmann::json::array();
|
||||||
|
const auto& blocks_tree = fr.getBlocksTree();
|
||||||
|
for (const auto& kv : blocks_tree)
|
||||||
{
|
{
|
||||||
::std::cout << nlohmann::json(json).dump(2);
|
threads.emplace_back();
|
||||||
|
|
||||||
|
auto& thread = threads.back();
|
||||||
|
thread["threadId"] = kv.first;
|
||||||
|
thread["threadName"] = fr.getThreadName(kv.first);
|
||||||
|
|
||||||
|
convertChildren(kv.second, thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
json["threads"] = threads;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!outputFile.empty())
|
||||||
|
{
|
||||||
|
::std::ofstream file(outputFile);
|
||||||
|
json.dump(file, true, 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
json.dump(::std::cout, true, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
::std::cout << "json.dump() error...";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,34 +1,72 @@
|
|||||||
|
/**
|
||||||
|
Lightweight profiler library for c++
|
||||||
|
Copyright(C) 2016-2018 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_CONVERTER_H
|
#ifndef EASY_PROFILER_CONVERTER_H
|
||||||
#define EASY_PROFILER_CONVERTER_H
|
#define EASY_PROFILER_CONVERTER_H
|
||||||
|
|
||||||
///std
|
|
||||||
#include<string>
|
|
||||||
|
|
||||||
///this
|
|
||||||
#include "reader.h"
|
#include "reader.h"
|
||||||
|
|
||||||
///nlohmann json
|
|
||||||
#include "include/json.hpp"
|
#include "include/json.hpp"
|
||||||
|
|
||||||
class JSONConverter EASY_FINAL
|
class EasyProfilerExporter
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
JSONConverter(const ::std::string &file_in,
|
|
||||||
const ::std::string &file_out):
|
|
||||||
m_file_in(file_in),
|
|
||||||
m_file_out(file_out)
|
|
||||||
{}
|
|
||||||
|
|
||||||
~JSONConverter()
|
virtual ~EasyProfilerExporter() {}
|
||||||
{
|
virtual void convert(const ::std::string& inputFile, const ::std::string& outputFile) const = 0;
|
||||||
}
|
|
||||||
void convert();
|
|
||||||
private:
|
|
||||||
void readThreadBlocks(const profiler::reader::BlocksTreeNode &node, nlohmann::json &json);
|
|
||||||
|
|
||||||
::std::string m_file_in;
|
|
||||||
::std::string m_file_out;
|
|
||||||
nlohmann::json json;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class JsonExporter EASY_FINAL : public EasyProfilerExporter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
~JsonExporter() override {}
|
||||||
|
void convert(const ::std::string& inputFile, const ::std::string& outputFile) const override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void convert(const profiler::reader::BlocksTreeNode& node, nlohmann::json& json) const;
|
||||||
|
void convertChildren(const profiler::reader::BlocksTreeNode& node, nlohmann::json& json) const;
|
||||||
|
|
||||||
|
}; // end of class JsonExporter.
|
||||||
|
|
||||||
#endif //EASY_PROFILER_CONVERTER_H
|
#endif //EASY_PROFILER_CONVERTER_H
|
||||||
|
@ -8435,6 +8435,7 @@ class basic_json
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
/*!
|
/*!
|
||||||
@brief internal implementation of the serialization function
|
@brief internal implementation of the serialization function
|
||||||
|
|
||||||
|
@ -1,17 +1,14 @@
|
|||||||
///std
|
///std
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
///this
|
|
||||||
//#include "reader.h"
|
|
||||||
#include "converter.h"
|
#include "converter.h"
|
||||||
|
|
||||||
using namespace profiler::reader;
|
using namespace profiler::reader;
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
std::string filename;
|
std::string filename, output_json_filename;
|
||||||
std::string output_json_filename = "";
|
|
||||||
if (argc > 1 && argv[1])
|
if (argc > 1 && argv[1])
|
||||||
{
|
{
|
||||||
filename = argv[1];
|
filename = argv[1];
|
||||||
@ -20,17 +17,18 @@ int main(int argc, char* argv[])
|
|||||||
{
|
{
|
||||||
std::cout << "Usage: " << argv[0] << " INPUT_PROF_FILE [OUTPUT_JSON_FILE]\n"
|
std::cout << "Usage: " << argv[0] << " INPUT_PROF_FILE [OUTPUT_JSON_FILE]\n"
|
||||||
"where:\n"
|
"where:\n"
|
||||||
"INPUT_PROF_FILE required\n"
|
"INPUT_PROF_FILE // Required\n"
|
||||||
"OUTPUT_JSON_FILE optional (if not specified output will be print in stdout)\n";
|
"OUTPUT_JSON_FILE (if not specified output will be print in stdout) // Optional\n";
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (argc > 2 && argv[2])
|
if (argc > 2 && argv[2])
|
||||||
{
|
{
|
||||||
output_json_filename = argv[2];
|
output_json_filename = argv[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
JSONConverter js(filename, output_json_filename);
|
JsonExporter js;
|
||||||
js.convert();
|
js.convert(filename, output_json_filename);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1,473 +1,198 @@
|
|||||||
///std
|
/**
|
||||||
#include <memory>
|
Lightweight profiler library for c++
|
||||||
|
Copyright(C) 2016-2018 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 <algorithm>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <sstream>
|
#include <deque>
|
||||||
#include <iterator>
|
#include <functional>
|
||||||
|
|
||||||
///this
|
|
||||||
#include "reader.h"
|
#include "reader.h"
|
||||||
|
|
||||||
#include "hashed_cstr.h"
|
|
||||||
|
|
||||||
////from easy_profiler_core/reader.cpp/////
|
|
||||||
|
|
||||||
typedef uint64_t processid_t;
|
|
||||||
|
|
||||||
extern const uint32_t PROFILER_SIGNATURE = ('E' << 24) | ('a' << 16) | ('s' << 8) | 'y';
|
|
||||||
|
|
||||||
# define EASY_VERSION_INT(v_major, v_minor, v_patch) ((static_cast<uint32_t>(v_major) << 24) | (static_cast<uint32_t>(v_minor) << 16) | static_cast<uint32_t>(v_patch))
|
|
||||||
const uint32_t MIN_COMPATIBLE_VERSION = EASY_VERSION_INT(0, 1, 0); ///< minimal compatible version (.prof file format was not changed seriously since this version)
|
|
||||||
const uint32_t EASY_V_100 = EASY_VERSION_INT(1, 0, 0); ///< in v1.0.0 some additional data were added into .prof file
|
|
||||||
const uint32_t EASY_V_130 = EASY_VERSION_INT(1, 3, 0); ///< in v1.3.0 changed sizeof(thread_id_t) uint32_t -> uint64_t
|
|
||||||
const uint32_t EASY_V_200 = EASY_VERSION_INT(2, 0, 0); ///< in v2.0.0 file header was slightly rearranged
|
|
||||||
# undef EASY_VERSION_INT
|
|
||||||
|
|
||||||
const uint64_t TIME_FACTOR = 1000000000ULL;
|
|
||||||
|
|
||||||
// TODO: use 128 bit integer operations for better accuracy
|
|
||||||
#define EASY_USE_FLOATING_POINT_CONVERSION
|
|
||||||
|
|
||||||
#ifdef EASY_USE_FLOATING_POINT_CONVERSION
|
|
||||||
|
|
||||||
// Suppress warnings about double to uint64 conversion
|
|
||||||
# ifdef _MSC_VER
|
|
||||||
# pragma warning(disable:4244)
|
|
||||||
# elif defined(__GNUC__)
|
|
||||||
# pragma GCC diagnostic push
|
|
||||||
# pragma GCC diagnostic ignored "-Wconversion"
|
|
||||||
# pragma GCC diagnostic ignored "-Wsign-conversion"
|
|
||||||
# elif defined(__clang__)
|
|
||||||
# pragma clang diagnostic push
|
|
||||||
# pragma clang diagnostic ignored "-Wconversion"
|
|
||||||
# pragma clang diagnostic ignored "-Wsign-conversion"
|
|
||||||
# endif
|
|
||||||
|
|
||||||
# define EASY_CONVERT_TO_NANO(t, freq, factor) t *= factor
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
# define EASY_CONVERT_TO_NANO(t, freq, factor) t *= TIME_FACTOR; t /= freq
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
inline bool isCompatibleVersion(uint32_t _version)
|
|
||||||
{
|
|
||||||
return _version >= MIN_COMPATIBLE_VERSION;
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef EASY_PROFILER_HASHED_CSTR_DEFINED
|
|
||||||
|
|
||||||
using StatsMap = ::std::unordered_map<::profiler::block_id_t, ::profiler::BlockStatistics*, ::estd::hash<::profiler::block_id_t> >;
|
|
||||||
|
|
||||||
/** \note It is absolutely safe to use hashed_cstr (which simply stores pointer) because std::unordered_map,
|
|
||||||
which uses it as a key, exists only inside fillTreesFromFile function. */
|
|
||||||
using IdMap = ::std::unordered_map<::profiler::hashed_cstr, ::profiler::block_id_t>;
|
|
||||||
|
|
||||||
using CsStatsMap = ::std::unordered_map<::profiler::hashed_cstr, ::profiler::BlockStatistics*>;
|
|
||||||
|
|
||||||
#else
|
|
||||||
|
|
||||||
// TODO: Create optimized version of profiler::hashed_cstr for Linux too.
|
|
||||||
using StatsMap = ::std::unordered_map<::profiler::block_id_t, ::profiler::BlockStatistics*, ::estd::hash<::profiler::block_id_t> >;
|
|
||||||
using IdMap = ::std::unordered_map<::profiler::hashed_stdstring, ::profiler::block_id_t>;
|
|
||||||
using CsStatsMap = ::std::unordered_map<::profiler::hashed_stdstring, ::profiler::BlockStatistics*>;
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/// end from easy_profiler_core/reader.cpp/////
|
|
||||||
|
|
||||||
using namespace profiler::reader;
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
struct EasyFileHeader
|
namespace profiler
|
||||||
{
|
{
|
||||||
uint32_t signature = 0;
|
|
||||||
uint32_t version = 0;
|
namespace reader
|
||||||
processid_t pid = 0;
|
{
|
||||||
int64_t cpu_frequency = 0;
|
|
||||||
::profiler::timestamp_t begin_time = 0;
|
profiler::block_index_t FileReader::readFile(const std::string& filename)
|
||||||
::profiler::timestamp_t end_time = 0;
|
{
|
||||||
uint64_t memory_size = 0;
|
::profiler::SerializedData serialized_blocks, serialized_descriptors;
|
||||||
uint64_t descriptors_memory_size = 0;
|
profiler::descriptors_list_t descriptors;
|
||||||
uint32_t total_blocks_number = 0;
|
profiler::blocks_t blocks;
|
||||||
|
profiler::thread_blocks_tree_t threaded_trees;
|
||||||
|
|
||||||
uint32_t total_descriptors_number = 0;
|
uint32_t total_descriptors_number = 0;
|
||||||
};
|
|
||||||
|
|
||||||
bool readHeader_v1(EasyFileHeader& _header, ::std::stringstream& inFile, ::std::stringstream& _log)
|
EASY_CONSTEXPR bool DoNotGatherStats = false;
|
||||||
{
|
const auto blocks_number = ::fillTreesFromFile(filename.c_str(), serialized_blocks, serialized_descriptors,
|
||||||
// File header before v2.0.0
|
descriptors, blocks, threaded_trees, total_descriptors_number, m_version, DoNotGatherStats, m_errorMessage);
|
||||||
|
|
||||||
if (_header.version > EASY_V_100)
|
if (blocks_number == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
m_blockDescriptors.reserve(descriptors.size());
|
||||||
|
uint32_t descId = 0;
|
||||||
|
for (auto descriptor : descriptors)
|
||||||
{
|
{
|
||||||
if (_header.version < EASY_V_130)
|
BlockDescriptor desc;
|
||||||
|
desc.parentId = descriptor->id();
|
||||||
|
desc.id = descId++;
|
||||||
|
desc.lineNumber = descriptor->line();
|
||||||
|
desc.argbColor = descriptor->color();
|
||||||
|
desc.blockType = static_cast<decltype(desc.blockType)>(descriptor->type());
|
||||||
|
desc.status = descriptor->status();
|
||||||
|
|
||||||
|
if (desc.parentId == desc.id) // compile time descriptor and name
|
||||||
|
desc.blockName = descriptor->name();
|
||||||
|
|
||||||
|
desc.fileName = descriptor->file();
|
||||||
|
m_blockDescriptors.push_back(std::move(desc));
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptors.clear();
|
||||||
|
serialized_descriptors.clear();
|
||||||
|
|
||||||
|
for (auto& kv : threaded_trees)
|
||||||
|
{
|
||||||
|
auto& thread = kv.second;
|
||||||
|
auto& root = m_blocksTree[kv.first];
|
||||||
|
auto& cswitches = m_contextSwitches[kv.first];
|
||||||
|
|
||||||
|
root.info.descriptor = nullptr;
|
||||||
|
if (!thread.children.empty())
|
||||||
{
|
{
|
||||||
uint32_t old_pid = 0;
|
root.info.beginTime = blocks[thread.children.front()].node->begin();
|
||||||
inFile.read((char*)&old_pid, sizeof(uint32_t));
|
root.info.endTime = blocks[thread.children.back()].node->end();
|
||||||
_header.pid = old_pid;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
inFile.read((char*)&_header.pid, sizeof(processid_t));
|
root.info.beginTime = 0;
|
||||||
|
root.info.endTime = 0;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
inFile.read((char*)&_header.cpu_frequency, sizeof(int64_t));
|
m_threadNames[kv.first] = std::move(thread.thread_name);
|
||||||
inFile.read((char*)&_header.begin_time, sizeof(::profiler::timestamp_t));
|
|
||||||
inFile.read((char*)&_header.end_time, sizeof(::profiler::timestamp_t));
|
|
||||||
|
|
||||||
inFile.read((char*)&_header.total_blocks_number, sizeof(uint32_t));
|
cswitches.reserve(thread.sync.size());
|
||||||
if (_header.total_blocks_number == 0)
|
for (auto i : thread.sync)
|
||||||
{
|
|
||||||
_log << "Profiled blocks number == 0";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
inFile.read((char*)&_header.memory_size, sizeof(decltype(_header.memory_size)));
|
|
||||||
if (_header.memory_size == 0)
|
|
||||||
{
|
|
||||||
_log << "Wrong memory size == 0 for " << _header.total_blocks_number << " blocks";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
inFile.read((char*)&_header.total_descriptors_number, sizeof(uint32_t));
|
|
||||||
if (_header.total_descriptors_number == 0)
|
|
||||||
{
|
|
||||||
_log << "Blocks description number == 0";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
inFile.read((char*)&_header.descriptors_memory_size, sizeof(decltype(_header.descriptors_memory_size)));
|
|
||||||
if (_header.descriptors_memory_size == 0)
|
|
||||||
{
|
|
||||||
_log << "Wrong memory size == 0 for " << _header.total_descriptors_number << " blocks descriptions";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool readHeader_v2(EasyFileHeader& _header, std::stringstream& inFile, std::stringstream& _log)
|
|
||||||
{
|
|
||||||
// File header after v2.0.0
|
|
||||||
|
|
||||||
inFile.read((char*)&_header.pid, sizeof(processid_t));
|
|
||||||
inFile.read((char*)&_header.cpu_frequency, sizeof(int64_t));
|
|
||||||
inFile.read((char*)&_header.begin_time, sizeof(::profiler::timestamp_t));
|
|
||||||
inFile.read((char*)&_header.end_time, sizeof(::profiler::timestamp_t));
|
|
||||||
|
|
||||||
inFile.read((char*)&_header.memory_size, sizeof(decltype(_header.memory_size)));
|
|
||||||
if (_header.memory_size == 0)
|
|
||||||
{
|
|
||||||
_log << "Wrong memory size == 0 for " << _header.total_blocks_number << " blocks";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
inFile.read((char*)&_header.descriptors_memory_size, sizeof(decltype(_header.descriptors_memory_size)));
|
|
||||||
if (_header.descriptors_memory_size == 0)
|
|
||||||
{
|
|
||||||
_log << "Wrong memory size == 0 for " << _header.total_descriptors_number << " blocks descriptions";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
inFile.read((char*)&_header.total_blocks_number, sizeof(uint32_t));
|
|
||||||
if (_header.total_blocks_number == 0)
|
|
||||||
{
|
|
||||||
_log << "Profiled blocks number == 0";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
inFile.read((char*)&_header.total_descriptors_number, sizeof(uint32_t));
|
|
||||||
if (_header.total_descriptors_number == 0)
|
|
||||||
{
|
|
||||||
_log << "Blocks description number == 0";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
::profiler::block_index_t FileReader::readFile(const ::std::string &filename)
|
|
||||||
{
|
|
||||||
::std::ifstream file(filename, ::std::fstream::binary);
|
|
||||||
if (!file.is_open())
|
|
||||||
{
|
|
||||||
errorMessage << "Can not open file " << filename;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
::std::stringstream inFile;
|
|
||||||
|
|
||||||
inFile << file.rdbuf();
|
|
||||||
file.close();
|
|
||||||
|
|
||||||
uint32_t signature = 0;
|
|
||||||
inFile.read((char*)&signature, sizeof(uint32_t));
|
|
||||||
if (signature != PROFILER_SIGNATURE)
|
|
||||||
{
|
|
||||||
errorMessage << "Wrong signature " << signature << "\nThis is not EasyProfiler file/stream.";
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_version = 0;
|
|
||||||
inFile.read((char*)&m_version, sizeof(uint32_t));
|
|
||||||
if (!isCompatibleVersion(m_version))
|
|
||||||
{
|
|
||||||
errorMessage << "Incompatible version: v" << (m_version >> 24) << "." << ((m_version & 0x00ff0000) >> 16) << "." << (m_version & 0x0000ffff);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
EasyFileHeader header;
|
|
||||||
header.signature = signature;
|
|
||||||
header.version = m_version;
|
|
||||||
|
|
||||||
if (m_version < EASY_V_200)
|
|
||||||
{
|
|
||||||
if (!readHeader_v1(header, inFile, errorMessage))
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!readHeader_v2(header, inFile, errorMessage))
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint64_t cpu_frequency = header.cpu_frequency;
|
|
||||||
const double conversion_factor = (cpu_frequency != 0 ? static_cast<double>(TIME_FACTOR) / static_cast<double>(cpu_frequency) : 1.);
|
|
||||||
|
|
||||||
auto begin_time = header.begin_time;
|
|
||||||
auto end_time = header.end_time;
|
|
||||||
|
|
||||||
const auto memory_size = header.memory_size;
|
|
||||||
const auto descriptors_memory_size = header.descriptors_memory_size;
|
|
||||||
const auto total_blocks_number = header.total_blocks_number;
|
|
||||||
uint32_t total_descriptors_number = header.total_descriptors_number;
|
|
||||||
|
|
||||||
if (cpu_frequency != 0)
|
|
||||||
{
|
|
||||||
EASY_CONVERT_TO_NANO(begin_time, cpu_frequency, conversion_factor);
|
|
||||||
EASY_CONVERT_TO_NANO(end_time, cpu_frequency, conversion_factor);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_BlockDescriptors.reserve(total_descriptors_number);
|
|
||||||
serialized_descriptors.set(descriptors_memory_size);
|
|
||||||
|
|
||||||
///read descriptors data
|
|
||||||
uint64_t i = 0;
|
|
||||||
while (!inFile.eof() && m_BlockDescriptors.size() < total_descriptors_number)
|
|
||||||
{
|
|
||||||
uint16_t sz = 0;
|
|
||||||
inFile.read((char*)&sz, sizeof(sz));
|
|
||||||
if (sz == 0)
|
|
||||||
{
|
{
|
||||||
m_BlockDescriptors.push_back(nullptr);
|
auto baseData = blocks[i].cs;
|
||||||
continue;
|
cswitches.emplace_back(ContextSwitchEvent {baseData->begin(), baseData->end(), baseData->tid(), baseData->name()});
|
||||||
}
|
}
|
||||||
|
|
||||||
char* data = serialized_descriptors[i];
|
using child_t = std::pair<profiler::block_index_t, std::reference_wrapper<BlocksTreeNode> >;
|
||||||
inFile.read(data, sz);
|
using children_queue_t = std::deque<child_t>;
|
||||||
auto descriptor = reinterpret_cast<::profiler::SerializedBlockDescriptor*>(data);
|
children_queue_t queue;
|
||||||
|
|
||||||
auto desc = ::std::make_shared<BlockDescriptor>();
|
root.children.reserve(thread.children.size());
|
||||||
desc->lineNumber = descriptor->line();
|
for (auto i : thread.children)
|
||||||
desc->blockId = descriptor->id();
|
queue.emplace_back(i, std::ref(root));
|
||||||
desc->argbColor = descriptor->color();
|
|
||||||
desc->blockType = static_cast<decltype(desc->blockType)>(descriptor->type());
|
|
||||||
desc->status = descriptor->status();
|
|
||||||
desc->compileTimeName = descriptor->name();
|
|
||||||
desc->fileName = descriptor->file();
|
|
||||||
m_BlockDescriptors.push_back(::std::move(desc));
|
|
||||||
|
|
||||||
i += sz;
|
while (!queue.empty())
|
||||||
|
{
|
||||||
|
auto current = queue.front();
|
||||||
|
queue.pop_front();
|
||||||
|
|
||||||
|
const profiler::BlocksTree& block = blocks[current.first];
|
||||||
|
BlocksTreeNode& parent = current.second;
|
||||||
|
|
||||||
|
parent.children.emplace_back();
|
||||||
|
auto& child = parent.children.back();
|
||||||
|
|
||||||
|
child.children.reserve(block.children.size());
|
||||||
|
for (auto i : block.children)
|
||||||
|
queue.emplace_back(i, std::ref(child));
|
||||||
|
|
||||||
|
auto& descriptor = m_blockDescriptors[block.node->id()];
|
||||||
|
if (descriptor.parentId != descriptor.id && descriptor.blockName.empty())
|
||||||
|
descriptor.blockName = block.node->name(); // runtime name
|
||||||
|
|
||||||
|
auto& info = child.info;
|
||||||
|
info.beginTime = block.node->begin();
|
||||||
|
info.endTime = block.node->end();
|
||||||
|
info.descriptor = &descriptor;
|
||||||
|
info.blockIndex = current.first;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
serialized_blocks.set(memory_size);
|
return blocks_number;
|
||||||
|
|
||||||
i = 0;
|
|
||||||
uint32_t read_number = 0;
|
|
||||||
::profiler::block_index_t blocks_counter = 0;
|
|
||||||
::std::vector<char> name;
|
|
||||||
|
|
||||||
const size_t thread_id_t_size = m_version < EASY_V_130 ? sizeof(uint32_t) : sizeof(::profiler::thread_id_t);
|
|
||||||
///read blocks info for every thread
|
|
||||||
while (!inFile.eof() && read_number < total_blocks_number)
|
|
||||||
{
|
|
||||||
::profiler::thread_id_t thread_id = 0;
|
|
||||||
inFile.read((char*)&thread_id, thread_id_t_size);
|
|
||||||
auto& root = m_BlocksTree[thread_id];
|
|
||||||
|
|
||||||
uint16_t name_size = 0;
|
|
||||||
inFile.read((char*)&name_size, sizeof(uint16_t));
|
|
||||||
if (name_size != 0)
|
|
||||||
{
|
|
||||||
name.resize(name_size);
|
|
||||||
inFile.read(name.data(), name_size);
|
|
||||||
m_threadNames[thread_id] = name.data();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t blocks_number_in_thread = 0;
|
|
||||||
inFile.read((char*)&blocks_number_in_thread, sizeof(decltype(blocks_number_in_thread)));
|
|
||||||
auto threshold = read_number + blocks_number_in_thread;
|
|
||||||
while (!inFile.eof() && read_number < threshold)
|
|
||||||
{
|
|
||||||
++read_number;
|
|
||||||
|
|
||||||
uint16_t sz = 0;
|
|
||||||
inFile.read((char*)&sz, sizeof(sz));
|
|
||||||
if (sz == 0)
|
|
||||||
{
|
|
||||||
errorMessage << "Bad CSwitch block size == 0";
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* data = serialized_blocks[i];
|
|
||||||
inFile.read(data, sz);
|
|
||||||
i += sz;
|
|
||||||
auto baseData = reinterpret_cast<::profiler::SerializedCSwitch*>(data);
|
|
||||||
auto t_begin = reinterpret_cast<::profiler::timestamp_t*>(data);
|
|
||||||
auto t_end = t_begin + 1;
|
|
||||||
|
|
||||||
if (cpu_frequency != 0)
|
|
||||||
{
|
|
||||||
EASY_CONVERT_TO_NANO(*t_begin, cpu_frequency, conversion_factor);
|
|
||||||
EASY_CONVERT_TO_NANO(*t_end, cpu_frequency, conversion_factor);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*t_end > begin_time)
|
|
||||||
{
|
|
||||||
if (*t_begin < begin_time)
|
|
||||||
*t_begin = begin_time;
|
|
||||||
|
|
||||||
m_ContextSwitches.emplace_back();
|
|
||||||
::std::shared_ptr<ContextSwitchEvent>& cs = m_ContextSwitches.back();
|
|
||||||
cs->switchName = baseData->name();
|
|
||||||
cs->targetThreadId = baseData->tid();
|
|
||||||
cs->beginTime = baseData->begin();
|
|
||||||
cs->endTime = baseData->end();
|
|
||||||
|
|
||||||
const auto block_index = blocks_counter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (inFile.eof())
|
|
||||||
break;
|
|
||||||
|
|
||||||
blocks_number_in_thread = 0;
|
|
||||||
inFile.read((char*)&blocks_number_in_thread, sizeof(decltype(blocks_number_in_thread)));
|
|
||||||
threshold = read_number + blocks_number_in_thread;
|
|
||||||
::std::vector<::std::shared_ptr<BlocksTreeNode>> siblings;
|
|
||||||
::std::shared_ptr<BlocksTreeNode> prev_node = ::std::make_shared<BlocksTreeNode>();
|
|
||||||
prev_node->current_block = ::std::make_shared<BlockInfo>();
|
|
||||||
|
|
||||||
::std::shared_ptr<BlocksTreeNode> element;
|
|
||||||
uint32_t level = 0;
|
|
||||||
|
|
||||||
while (!inFile.eof() && read_number < threshold)
|
|
||||||
{
|
|
||||||
element = ::std::make_shared<BlocksTreeNode>();
|
|
||||||
element->current_block = ::std::make_shared<BlockInfo>();
|
|
||||||
|
|
||||||
++read_number;
|
|
||||||
|
|
||||||
uint16_t sz = 0;
|
|
||||||
inFile.read((char*)&sz, sizeof(sz));
|
|
||||||
if (sz == 0)
|
|
||||||
{
|
|
||||||
errorMessage << "Bad block size == 0";
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* data = serialized_blocks[i];
|
|
||||||
inFile.read(data, sz);
|
|
||||||
i += sz;
|
|
||||||
auto baseData = reinterpret_cast<::profiler::SerializedBlock*>(data);
|
|
||||||
if (baseData->id() >= total_descriptors_number)
|
|
||||||
{
|
|
||||||
errorMessage << "Bad block id == " << baseData->id();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
element->current_block->blockId = baseData->id();
|
|
||||||
|
|
||||||
auto desc = m_BlockDescriptors[baseData->id()];
|
|
||||||
if (desc == nullptr)
|
|
||||||
{
|
|
||||||
errorMessage << "Bad block id == " << baseData->id() << ". Description is null.";
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
element->current_block->descriptor = m_BlockDescriptors[baseData->id()];
|
|
||||||
|
|
||||||
auto t_begin = reinterpret_cast<::profiler::timestamp_t*>(data);
|
|
||||||
auto t_end = t_begin + 1;
|
|
||||||
|
|
||||||
if (cpu_frequency != 0)
|
|
||||||
{
|
|
||||||
EASY_CONVERT_TO_NANO(*t_begin, cpu_frequency, conversion_factor);
|
|
||||||
EASY_CONVERT_TO_NANO(*t_end, cpu_frequency, conversion_factor);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (*t_end >= begin_time)
|
|
||||||
{
|
|
||||||
if (*t_begin < begin_time)
|
|
||||||
*t_begin = begin_time;
|
|
||||||
|
|
||||||
element->current_block->beginTime = baseData->begin();
|
|
||||||
element->current_block->endTime = baseData->end();
|
|
||||||
|
|
||||||
///is sibling?
|
|
||||||
if(element->current_block->beginTime >= prev_node->current_block->endTime)
|
|
||||||
{
|
|
||||||
prev_node = element;
|
|
||||||
///all siblings
|
|
||||||
root.children.push_back(element);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto iter = root.children.begin();
|
|
||||||
for(;iter != root.children.end(); ++iter)
|
|
||||||
{
|
|
||||||
if(iter->get()->current_block->beginTime >= element->current_block->beginTime)
|
|
||||||
{
|
|
||||||
::std::move(iter,root.children.end(),::std::back_inserter(element->children));
|
|
||||||
root.children.erase(std::remove(begin(root.children), end(root.children), nullptr),
|
|
||||||
end(root.children));
|
|
||||||
root.children.emplace_back(element);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const auto block_index = blocks_counter++;
|
|
||||||
///TODO: make optimization BLOCK_TYPE_EVENT. leave it here commented.
|
|
||||||
// if (desc->blockType == ::profiler::BLOCK_TYPE_EVENT)
|
|
||||||
// {
|
|
||||||
// root.children.emplace_back(element);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return blocks_counter;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const thread_blocks_tree_t &FileReader::getBlocksTreeData()
|
const thread_blocks_tree_t& FileReader::getBlocksTree() const
|
||||||
{
|
{
|
||||||
return m_BlocksTree;
|
return m_blocksTree;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ::std::string &FileReader::getThreadName(uint64_t threadId)
|
const descriptors_list_t& FileReader::getBlockDescriptors() const
|
||||||
{
|
{
|
||||||
return m_threadNames[threadId];
|
return m_blockDescriptors;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t FileReader::getVersion()
|
const std::string& FileReader::getThreadName(uint64_t threadId) const
|
||||||
|
{
|
||||||
|
auto it = m_threadNames.find(threadId);
|
||||||
|
if (it == m_threadNames.end())
|
||||||
|
return m_emptyString;
|
||||||
|
return it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t FileReader::getVersion() const
|
||||||
{
|
{
|
||||||
return m_version;
|
return m_version;
|
||||||
}
|
}
|
||||||
|
|
||||||
const context_switches_t &FileReader::getContextSwitches()
|
::std::string FileReader::getVersionString() const
|
||||||
{
|
{
|
||||||
return m_ContextSwitches;
|
std::stringstream stream;
|
||||||
|
stream << ((m_version & 0xff000000) >> 24) << "." << ((m_version & 0x00ff0000) >> 16) << "." << (m_version & 0x0000ffff);
|
||||||
|
return stream.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const context_switches_t& FileReader::getContextSwitches() const
|
||||||
|
{
|
||||||
|
return m_contextSwitches;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // end of namespace reader.
|
||||||
|
|
||||||
|
} // end of namespace profiler.
|
||||||
|
@ -43,15 +43,11 @@ The Apache License, Version 2.0 (the "License");
|
|||||||
#ifndef EASY_PROFILER_READER_H
|
#ifndef EASY_PROFILER_READER_H
|
||||||
#define EASY_PROFILER_READER_H
|
#define EASY_PROFILER_READER_H
|
||||||
|
|
||||||
///std
|
|
||||||
#include <fstream>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <memory>
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
|
||||||
///this
|
|
||||||
#include <easy/easy_protocol.h>
|
#include <easy/easy_protocol.h>
|
||||||
#include <easy/reader.h>
|
#include <easy/reader.h>
|
||||||
#include <easy/utility.h>
|
#include <easy/utility.h>
|
||||||
@ -63,67 +59,80 @@ namespace reader {
|
|||||||
class BlocksTreeNode;
|
class BlocksTreeNode;
|
||||||
using thread_blocks_tree_t = ::std::unordered_map<::profiler::thread_id_t, BlocksTreeNode, ::estd::hash<::profiler::thread_id_t> >;
|
using thread_blocks_tree_t = ::std::unordered_map<::profiler::thread_id_t, BlocksTreeNode, ::estd::hash<::profiler::thread_id_t> >;
|
||||||
using thread_names_t = ::std::unordered_map<::profiler::thread_id_t, ::std::string>;
|
using thread_names_t = ::std::unordered_map<::profiler::thread_id_t, ::std::string>;
|
||||||
using context_switches_t = ::std::vector<::std::shared_ptr<ContextSwitchEvent> >;
|
using context_switches_list_t = ::std::vector<ContextSwitchEvent>;
|
||||||
|
using context_switches_t = ::std::unordered_map<::profiler::thread_id_t, context_switches_list_t, ::estd::hash<::profiler::thread_id_t> >;
|
||||||
|
using descriptors_list_t = ::std::vector<BlockDescriptor>;
|
||||||
|
|
||||||
class BlocksTreeNode
|
class BlocksTreeNode EASY_FINAL
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
::std::vector<::std::shared_ptr<BlocksTreeNode>> children;
|
|
||||||
::std::shared_ptr<BlockInfo> current_block;
|
|
||||||
BlocksTreeNode* parent;
|
|
||||||
|
|
||||||
BlocksTreeNode(BlocksTreeNode&& other) : children(::std::move(other.children))
|
using children_t = ::std::vector<BlocksTreeNode>;
|
||||||
, current_block(::std::move(other.current_block))
|
|
||||||
, parent(other.parent)
|
BlockInfo info;
|
||||||
|
children_t children;
|
||||||
|
BlocksTreeNode* parent = nullptr;
|
||||||
|
|
||||||
|
BlocksTreeNode(BlocksTreeNode&& other) EASY_NOEXCEPT
|
||||||
|
: info(other.info)
|
||||||
|
, children(::std::move(other.children))
|
||||||
|
, parent(other.parent)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
BlocksTreeNode() : parent(nullptr)
|
BlocksTreeNode& operator = (BlocksTreeNode&& other) EASY_NOEXCEPT
|
||||||
{
|
{
|
||||||
|
info = other.info;
|
||||||
|
children = ::std::move(other.children);
|
||||||
|
parent = other.parent;
|
||||||
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BlocksTreeNode() = default;
|
||||||
|
~BlocksTreeNode() = default;
|
||||||
|
|
||||||
|
BlocksTreeNode(const BlocksTreeNode&) = delete;
|
||||||
|
BlocksTreeNode& operator = (const BlocksTreeNode&) = delete;
|
||||||
|
|
||||||
}; // end of class BlocksTreeNode.
|
}; // end of class BlocksTreeNode.
|
||||||
|
|
||||||
class FileReader EASY_FINAL
|
class FileReader EASY_FINAL
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/*-----------------------------------------------------------------*/
|
///< initial read file with RAW data
|
||||||
///initial read file with RAW data
|
|
||||||
::profiler::block_index_t readFile(const ::std::string& filename);
|
::profiler::block_index_t readFile(const ::std::string& filename);
|
||||||
/*-----------------------------------------------------------------*/
|
|
||||||
///get blocks tree
|
///< get blocks tree
|
||||||
const thread_blocks_tree_t& getBlocksTreeData();
|
const thread_blocks_tree_t& getBlocksTree() const;
|
||||||
/*-----------------------------------------------------------------*/
|
|
||||||
|
const descriptors_list_t& getBlockDescriptors() const;
|
||||||
|
|
||||||
/*! get thread name by Id
|
/*! get thread name by Id
|
||||||
\param threadId thread Id
|
\param threadId thread Id
|
||||||
\return Name of thread
|
\return Name of thread
|
||||||
*/
|
*/
|
||||||
const std::string &getThreadName(uint64_t threadId);
|
const std::string& getThreadName(uint64_t threadId) const;
|
||||||
/*-----------------------------------------------------------------*/
|
|
||||||
/*! get file version
|
/*! get file version
|
||||||
\return data file version
|
\return data file version
|
||||||
*/
|
*/
|
||||||
uint32_t getVersion();
|
uint32_t getVersion() const;
|
||||||
/*-----------------------------------------------------------------*/
|
|
||||||
///get sontext switches
|
::std::string getVersionString() const;
|
||||||
const context_switches_t& getContextSwitches();
|
|
||||||
/*-----------------------------------------------------------------*/
|
///< get context switches
|
||||||
|
const context_switches_t& getContextSwitches() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
///serialized raw data
|
::std::string m_emptyString;
|
||||||
::profiler::SerializedData serialized_blocks, serialized_descriptors;
|
::std::stringstream m_errorMessage; ///< error log stream
|
||||||
///error log stream
|
thread_blocks_tree_t m_blocksTree; ///< thread's blocks hierarchy
|
||||||
::std::stringstream errorMessage;
|
thread_names_t m_threadNames; ///< [thread_id, thread_name]
|
||||||
///thread's blocks
|
context_switches_t m_contextSwitches; ///< context switches info
|
||||||
thread_blocks_tree_t m_BlocksTree;
|
descriptors_list_t m_blockDescriptors; ///< block descriptors
|
||||||
///[thread_id, thread_name]
|
uint32_t m_version; ///< .prof file version
|
||||||
thread_names_t m_threadNames;
|
|
||||||
///context switches info
|
|
||||||
context_switches_t m_ContextSwitches;
|
|
||||||
std::vector<std::shared_ptr<BlockDescriptor>> m_BlockDescriptors;
|
|
||||||
///data file version
|
|
||||||
uint32_t m_version;
|
|
||||||
|
|
||||||
}; // end of class FileReader.
|
}; // end of class FileReader.
|
||||||
|
|
||||||
|
@ -43,35 +43,34 @@ struct FileHeader //64
|
|||||||
|
|
||||||
struct BlockInfo
|
struct BlockInfo
|
||||||
{
|
{
|
||||||
uint64_t beginTime;
|
uint64_t beginTime;
|
||||||
uint64_t endTime;
|
uint64_t endTime;
|
||||||
uint32_t blockId;
|
const BlockDescriptor* descriptor;
|
||||||
uint32_t parentBlockId;
|
uint32_t blockIndex;
|
||||||
std::string runTimeBlockName;
|
|
||||||
std::string thread_name; ///< Name of parent thread
|
|
||||||
std::shared_ptr<BlockDescriptor> descriptor;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ContextSwitchEvent{
|
struct ContextSwitchEvent
|
||||||
uint64_t beginTime;
|
{
|
||||||
uint64_t endTime;
|
uint64_t beginTime;
|
||||||
uint64_t targetThreadId;
|
uint64_t endTime;
|
||||||
std::string switchName;
|
uint64_t targetThreadId;
|
||||||
|
std::string targetProcess; ///< Contains process id and process name
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BlockDescriptor
|
struct BlockDescriptor
|
||||||
{
|
{
|
||||||
uint32_t blockId;
|
uint32_t parentId; ///< This will differ from id if this descriptor was created from runtime named block
|
||||||
int lineNumber;
|
uint32_t id;
|
||||||
uint32_t argbColor;
|
int lineNumber;
|
||||||
uint8_t blockType;
|
uint32_t argbColor;
|
||||||
uint8_t status;
|
uint8_t blockType;
|
||||||
std::string compileTimeName;
|
uint8_t status;
|
||||||
std::string fileName;
|
std::string blockName;
|
||||||
|
std::string fileName;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} //namespace reader
|
} //namespace reader
|
||||||
|
|
||||||
} //namespace profiler
|
} //namespace profiler
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user