diff --git a/easy_profiler_converter/converter.cpp b/easy_profiler_converter/converter.cpp index f3d6de9..0c8838f 100644 --- a/easy_profiler_converter/converter.cpp +++ b/easy_profiler_converter/converter.cpp @@ -1,70 +1,138 @@ -///this -#include "converter.h" -/// reader -#include "reader.h" +/** +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 "converter.h" #include -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.current_block != nullptr){ - json = {{"id",static_cast(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) + if (node.info.descriptor != nullptr) { - jsonObjects.push_back(nlohmann::json::object()); - readThreadBlocks(*value.get(),jsonObjects.back()); + json = {{"id", node.info.blockIndex}, + {"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; - fr.readFile(m_file_in); - const profiler::reader::thread_blocks_tree_t &blocks_tree = fr.getBlocksTreeData(); - 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()); + if (fr.readFile(inputFile) == 0) + return; - } - json["threads"] = jsonObjects; + nlohmann::json json = {{"version", fr.getVersionString()}, {"timeUnits", "ns"}}; - 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); - file << json; + descriptors.emplace_back(); + + 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..."; } } diff --git a/easy_profiler_converter/converter.h b/easy_profiler_converter/converter.h index db7cad6..6c987cc 100644 --- a/easy_profiler_converter/converter.h +++ b/easy_profiler_converter/converter.h @@ -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 #define EASY_PROFILER_CONVERTER_H -///std -#include - -///this #include "reader.h" - -///nlohmann json #include "include/json.hpp" -class JSONConverter EASY_FINAL +class EasyProfilerExporter { public: - JSONConverter(const ::std::string &file_in, - const ::std::string &file_out): - m_file_in(file_in), - m_file_out(file_out) - {} - ~JSONConverter() - { - } - 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; + virtual ~EasyProfilerExporter() {} + virtual void convert(const ::std::string& inputFile, const ::std::string& outputFile) const = 0; }; + +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 diff --git a/easy_profiler_converter/include/json.hpp b/easy_profiler_converter/include/json.hpp index 6eed114..7af408a 100644 --- a/easy_profiler_converter/include/json.hpp +++ b/easy_profiler_converter/include/json.hpp @@ -8435,6 +8435,7 @@ class basic_json }; +public: /*! @brief internal implementation of the serialization function diff --git a/easy_profiler_converter/main.cpp b/easy_profiler_converter/main.cpp index 0c5ac59..bec1d31 100644 --- a/easy_profiler_converter/main.cpp +++ b/easy_profiler_converter/main.cpp @@ -1,17 +1,14 @@ ///std #include #include - -///this -//#include "reader.h" #include "converter.h" using namespace profiler::reader; int main(int argc, char* argv[]) { - std::string filename; - std::string output_json_filename = ""; + std::string filename, output_json_filename; + if (argc > 1 && 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" "where:\n" - "INPUT_PROF_FILE required\n" - "OUTPUT_JSON_FILE optional (if not specified output will be print in stdout)\n"; + "INPUT_PROF_FILE // Required\n" + "OUTPUT_JSON_FILE (if not specified output will be print in stdout) // Optional\n"; return 1; } + if (argc > 2 && argv[2]) { output_json_filename = argv[2]; } - JSONConverter js(filename, output_json_filename); - js.convert(); + JsonExporter js; + js.convert(filename, output_json_filename); return 0; } diff --git a/easy_profiler_converter/reader.cpp b/easy_profiler_converter/reader.cpp index 6c0699c..e5bd10d 100644 --- a/easy_profiler_converter/reader.cpp +++ b/easy_profiler_converter/reader.cpp @@ -1,473 +1,198 @@ -///std -#include +/** +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 #include -#include -#include +#include +#include -///this #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(v_major) << 24) | (static_cast(v_minor) << 16) | static_cast(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; - processid_t pid = 0; - int64_t cpu_frequency = 0; - ::profiler::timestamp_t begin_time = 0; - ::profiler::timestamp_t end_time = 0; - uint64_t memory_size = 0; - uint64_t descriptors_memory_size = 0; - uint32_t total_blocks_number = 0; + +namespace reader +{ + +profiler::block_index_t FileReader::readFile(const std::string& filename) +{ + ::profiler::SerializedData serialized_blocks, serialized_descriptors; + profiler::descriptors_list_t descriptors; + profiler::blocks_t blocks; + profiler::thread_blocks_tree_t threaded_trees; + uint32_t total_descriptors_number = 0; -}; -bool readHeader_v1(EasyFileHeader& _header, ::std::stringstream& inFile, ::std::stringstream& _log) -{ - // File header before v2.0.0 + EASY_CONSTEXPR bool DoNotGatherStats = false; + const auto blocks_number = ::fillTreesFromFile(filename.c_str(), serialized_blocks, serialized_descriptors, + 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(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; - inFile.read((char*)&old_pid, sizeof(uint32_t)); - _header.pid = old_pid; + root.info.beginTime = blocks[thread.children.front()].node->begin(); + root.info.endTime = blocks[thread.children.back()].node->end(); } 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)); - inFile.read((char*)&_header.begin_time, sizeof(::profiler::timestamp_t)); - inFile.read((char*)&_header.end_time, sizeof(::profiler::timestamp_t)); + m_threadNames[kv.first] = std::move(thread.thread_name); - 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.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(TIME_FACTOR) / static_cast(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) + cswitches.reserve(thread.sync.size()); + for (auto i : thread.sync) { - m_BlockDescriptors.push_back(nullptr); - continue; + auto baseData = blocks[i].cs; + cswitches.emplace_back(ContextSwitchEvent {baseData->begin(), baseData->end(), baseData->tid(), baseData->name()}); } - char* data = serialized_descriptors[i]; - inFile.read(data, sz); - auto descriptor = reinterpret_cast<::profiler::SerializedBlockDescriptor*>(data); + using child_t = std::pair >; + using children_queue_t = std::deque; + children_queue_t queue; - auto desc = ::std::make_shared(); - desc->lineNumber = descriptor->line(); - desc->blockId = descriptor->id(); - desc->argbColor = descriptor->color(); - desc->blockType = static_castblockType)>(descriptor->type()); - desc->status = descriptor->status(); - desc->compileTimeName = descriptor->name(); - desc->fileName = descriptor->file(); - m_BlockDescriptors.push_back(::std::move(desc)); + root.children.reserve(thread.children.size()); + for (auto i : thread.children) + queue.emplace_back(i, std::ref(root)); - 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); - - i = 0; - uint32_t read_number = 0; - ::profiler::block_index_t blocks_counter = 0; - ::std::vector 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& 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> siblings; - ::std::shared_ptr prev_node = ::std::make_shared(); - prev_node->current_block = ::std::make_shared(); - - ::std::shared_ptr element; - uint32_t level = 0; - - while (!inFile.eof() && read_number < threshold) - { - element = ::std::make_shared(); - element->current_block = ::std::make_shared(); - - ++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; + return blocks_number; } -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; } -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. diff --git a/easy_profiler_converter/reader.h b/easy_profiler_converter/reader.h index fe7706b..28ee357 100644 --- a/easy_profiler_converter/reader.h +++ b/easy_profiler_converter/reader.h @@ -43,15 +43,11 @@ The Apache License, Version 2.0 (the "License"); #ifndef EASY_PROFILER_READER_H #define EASY_PROFILER_READER_H -///std -#include #include -#include #include #include #include -///this #include #include #include @@ -63,67 +59,80 @@ namespace reader { class BlocksTreeNode; 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 context_switches_t = ::std::vector<::std::shared_ptr >; +using context_switches_list_t = ::std::vector; +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; -class BlocksTreeNode +class BlocksTreeNode EASY_FINAL { public: - ::std::vector<::std::shared_ptr> children; - ::std::shared_ptr current_block; - BlocksTreeNode* parent; - BlocksTreeNode(BlocksTreeNode&& other) : children(::std::move(other.children)) - , current_block(::std::move(other.current_block)) - , parent(other.parent) + using children_t = ::std::vector; + + 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. class FileReader EASY_FINAL { public: - /*-----------------------------------------------------------------*/ - ///initial read file with RAW data + ///< initial read file with RAW data ::profiler::block_index_t readFile(const ::std::string& filename); - /*-----------------------------------------------------------------*/ - ///get blocks tree - const thread_blocks_tree_t& getBlocksTreeData(); - /*-----------------------------------------------------------------*/ + + ///< get blocks tree + const thread_blocks_tree_t& getBlocksTree() const; + + const descriptors_list_t& getBlockDescriptors() const; + /*! get thread name by Id \param threadId thread Id \return Name of thread */ - const std::string &getThreadName(uint64_t threadId); - /*-----------------------------------------------------------------*/ + const std::string& getThreadName(uint64_t threadId) const; + /*! get file version \return data file version */ - uint32_t getVersion(); - /*-----------------------------------------------------------------*/ - ///get sontext switches - const context_switches_t& getContextSwitches(); - /*-----------------------------------------------------------------*/ + uint32_t getVersion() const; + + ::std::string getVersionString() const; + + ///< get context switches + const context_switches_t& getContextSwitches() const; private: - ///serialized raw data - ::profiler::SerializedData serialized_blocks, serialized_descriptors; - ///error log stream - ::std::stringstream errorMessage; - ///thread's blocks - thread_blocks_tree_t m_BlocksTree; - ///[thread_id, thread_name] - thread_names_t m_threadNames; - ///context switches info - context_switches_t m_ContextSwitches; - std::vector> m_BlockDescriptors; - ///data file version - uint32_t m_version; + ::std::string m_emptyString; + ::std::stringstream m_errorMessage; ///< error log stream + thread_blocks_tree_t m_blocksTree; ///< thread's blocks hierarchy + thread_names_t m_threadNames; ///< [thread_id, thread_name] + context_switches_t m_contextSwitches; ///< context switches info + descriptors_list_t m_blockDescriptors; ///< block descriptors + uint32_t m_version; ///< .prof file version }; // end of class FileReader. diff --git a/easy_profiler_core/include/easy/easy_protocol.h b/easy_profiler_core/include/easy/easy_protocol.h index 7340d5f..0f2a38e 100644 --- a/easy_profiler_core/include/easy/easy_protocol.h +++ b/easy_profiler_core/include/easy/easy_protocol.h @@ -43,35 +43,34 @@ struct FileHeader //64 struct BlockInfo { - uint64_t beginTime; - uint64_t endTime; - uint32_t blockId; - uint32_t parentBlockId; - std::string runTimeBlockName; - std::string thread_name; ///< Name of parent thread - std::shared_ptr descriptor; + uint64_t beginTime; + uint64_t endTime; + const BlockDescriptor* descriptor; + uint32_t blockIndex; }; -struct ContextSwitchEvent{ - uint64_t beginTime; - uint64_t endTime; - uint64_t targetThreadId; - std::string switchName; +struct ContextSwitchEvent +{ + uint64_t beginTime; + uint64_t endTime; + uint64_t targetThreadId; + std::string targetProcess; ///< Contains process id and process name }; struct BlockDescriptor { - uint32_t blockId; - int lineNumber; - uint32_t argbColor; - uint8_t blockType; - uint8_t status; - std::string compileTimeName; - std::string fileName; + uint32_t parentId; ///< This will differ from id if this descriptor was created from runtime named block + uint32_t id; + int lineNumber; + uint32_t argbColor; + uint8_t blockType; + uint8_t status; + std::string blockName; + std::string fileName; }; - } //namespace reader + } //namespace profiler #endif