From c3c39df298a23174cd842d78f6051aa8215fb3b9 Mon Sep 17 00:00:00 2001 From: kurnevichstanislav Date: Thu, 13 Jul 2023 20:03:56 +0300 Subject: [PATCH] [Sampling profiler] Refactoring in aspt_converter * Rename aspt_converter.cpp -> main.cpp * Split aspt_converter.h into .cpp and .h * Fix naming and delimeters in options.yaml * Supplemented the README with information about the aspt_converter options Signed-off-by: kurnevichstanislav --- runtime/tests/tooling/sampler/CMakeLists.txt | 14 +- tools/sampler/CMakeLists.txt | 15 +- tools/sampler/README.md | 24 ++-- tools/sampler/aspt_converter.cpp | 128 +++++++++++++++++ tools/sampler/aspt_converter.h | 122 ++-------------- tools/sampler/{aspt_convert.cpp => main.cpp} | 12 +- tools/sampler/options.yaml | 14 +- tools/sampler/trace_dumper.cpp | 138 +++++++++++++++++++ tools/sampler/trace_dumper.h | 121 +++------------- 9 files changed, 347 insertions(+), 241 deletions(-) create mode 100644 tools/sampler/aspt_converter.cpp rename tools/sampler/{aspt_convert.cpp => main.cpp} (84%) create mode 100644 tools/sampler/trace_dumper.cpp diff --git a/runtime/tests/tooling/sampler/CMakeLists.txt b/runtime/tests/tooling/sampler/CMakeLists.txt index 299fa09d3..aa226041c 100644 --- a/runtime/tests/tooling/sampler/CMakeLists.txt +++ b/runtime/tests/tooling/sampler/CMakeLists.txt @@ -13,9 +13,17 @@ cmake_minimum_required(VERSION 3.10 FATAL_ERROR) -add_gtests( - sampling_profiler_test - sampling_profiler_test.cpp +panda_add_gtest( + NO_CORES + NAME sampling_profiler_test + SOURCES + sampling_profiler_test.cpp + LIBRARIES + arkruntime + arkassembler + aspt_converter_static + SANITIZERS + ${PANDA_SANITIZERS_LIST} ) add_panda_assembly(TARGET sampling_profiler_test_ark_asm SOURCE sampling_profiler_test.pa) diff --git a/tools/sampler/CMakeLists.txt b/tools/sampler/CMakeLists.txt index 307552ca4..471d3e834 100644 --- a/tools/sampler/CMakeLists.txt +++ b/tools/sampler/CMakeLists.txt @@ -14,16 +14,19 @@ cmake_minimum_required(VERSION 3.3.2 FATAL_ERROR) project(aspt_converter CXX) -panda_add_executable(aspt_converter aspt_convert.cpp) +panda_add_executable(aspt_converter main.cpp) -panda_target_link_libraries(aspt_converter - arkfile - arkbase +panda_add_library(aspt_converter_static STATIC + aspt_converter.cpp + trace_dumper.cpp ) - -panda_target_include_directories(aspt_converter PUBLIC ${PANDA_BINARY_ROOT}) +panda_target_link_libraries(aspt_converter_static arkfile arkbase) +panda_target_include_directories(aspt_converter_static PUBLIC ${PANDA_BINARY_ROOT}) panda_add_sanitizers(TARGET aspt_converter SANITIZERS ${PANDA_SANITIZERS_LIST}) +panda_add_sanitizers(TARGET aspt_converter_static SANITIZERS ${PANDA_SANITIZERS_LIST}) panda_gen_options(TARGET aspt_converter YAML_FILE options.yaml GENERATED_HEADER aspt_converter_options.h) + +panda_target_link_libraries(aspt_converter aspt_converter_static) diff --git a/tools/sampler/README.md b/tools/sampler/README.md index 450358270..df5c3a3e9 100644 --- a/tools/sampler/README.md +++ b/tools/sampler/README.md @@ -11,7 +11,7 @@ git clone https://github.com/brendangregg/FlameGraph.git ```bash # get abc bin -${BUILD_DIR}/bin/es2panda ${BUILD_SOURCE}/plugins/ets/tests/runtime/tooling.sampler/SamplerTest.ets ${BUILD_DIR}/sampling_app.abc +${BUILD_DIR}/bin/es2panda ${BUILD_SOURCE}/plugins/ets/tests/runtime/tooling/sampler/SamplerTest.ets ${BUILD_DIR}/sampling_app.abc # get sample dump ${BUILD_DIR}/bin/ark --load-runtimes=ets --boot-panda-files=${BUILD_DIR}/plugins/ets/etsstdlib.abc --sampling-profiler-enable --sampling-profiler-interval=200 --sampling-profiler-output-file=${BUILD_DIR}/outfile.aspt ${BUILD_DIR}/sampling_app.abc ETSGLOBAL::main @@ -23,14 +23,22 @@ ${BUILD_DIR}/bin/aspt_converter --input=${BUILD_DIR}/outfile.aspt --output=${BUI ${BUILD_DIR}/FlameGraph/flamegraph.pl ${BUILD_DIR}/traceout.csv > ${BUILD_DIR}/out.svg ``` -In current implementation of aspt_converter two vizual modes are supported: -```bash -# convert sample dump to single csv with separating samples by threads -${BUILD_DIR}/bin/aspt_converter --input=${BUILD_DIR}/outfile.aspt --output=${BUILD_DIR}/traceout.csv --thread-separation-by-tid +If option `--sampling-profiler-collect-stats` passed on ark launch it creates file at the end of vm's life with information about quantity of sent signal to every thread and created samples. + Signals can be ignored if threads are suspended to wait for cpu time(major part of ignored signals), or mutator threads are not executing bytecode (not started or finished). -# convert sample dump many csv files with one thread per file. Name of ouput file will concatinate with thread_id -${BUILD_DIR}/bin/aspt_converter --input=${BUILD_DIR}/outfile.aspt --output=${BUILD_DIR}/traceout.csv --thread-separation-by-csv -``` +## AsptConverter parameters + +| Parameter | Possible values | Description | +| ------------------------------- | ------------------------------- | ----------------------------------------------------------- | +| --csv-tid-separation | single-csv-single-tid | Doesn't distinguish threads, dump samples in single csv | +| | single-csv-multi-tid (by default) | Distinguish threads by thread_id in a single csv file | +| | multi-csv | Distinguish threads by creating csv files for each thread | +| --cold-graph-enable | true/false (by default: false) | Add information about thread status (running/suspended) | +| --substitute-module-dir | true/false (by default: false) | Enable substitution of panda files directories, e.g. run converter on host for .aspt got from device | +| --substitute-source-str | {dir1}, {dir2} | Substring that will be replaced with substitude-destination | +| --substitute-destination-str | {dir_target1}, {dir_target2} | Substring that will be places instead of substitude-source | + +Note: In substitution parameters (source and destination str) number of strings should be equal and i-th string from source changes only to i-th from destination. ## Limitations 1. Samples can be lost but not necessarily in case if signal was sent to interpreter thread while it was in the bridge and frame was not constructed yet. (Relevant to profiling with JIT or ArkTS NAPI) diff --git a/tools/sampler/aspt_converter.cpp b/tools/sampler/aspt_converter.cpp new file mode 100644 index 000000000..06bdaa548 --- /dev/null +++ b/tools/sampler/aspt_converter.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2021-2022 Huawei Device Co., Ltd. + * Licensed under 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 "tools/sampler/aspt_converter.h" + +namespace panda::tooling::sampler { + +size_t AsptConverter::CollectTracesStats() +{ + stack_traces_.clear(); + + size_t sample_counter = 0; + SampleInfo sample; + while (reader_.GetNextSample(&sample)) { + ++sample_counter; + + if (dump_type_ == DumpType::WITHOUT_THREAD_SEPARATION) { + // NOTE: zeroing thread_id to make samples indistinguishable in mode without thread separation + sample.thread_info.thread_id = 0; + } + + if (!build_cold_graph_) { + // NOTE: zeroing thread_status to make samples indistinguishable + // in mode without building cold flamegraph + sample.thread_info.thread_status = SampleInfo::ThreadStatus::UNDECLARED; + } + + auto it = stack_traces_.find(sample); + if (it == stack_traces_.end()) { + stack_traces_.insert({sample, 1}); + continue; + } + ++it->second; + } + return sample_counter; +} + +bool AsptConverter::CollectModules() +{ + FileInfo m_info; + while (reader_.GetNextModule(&m_info)) { + std::string filepath_str = m_info.pathname; + if (substitute_directories_.has_value()) { + for (size_t i = 0; i < substitute_directories_->source.size(); ++i) { + auto pos = filepath_str.find(substitute_directories_->source[i]); + if (pos != std::string::npos) { + filepath_str.replace(pos, substitute_directories_->source[i].size(), + substitute_directories_->destination[i]); + break; + } + } + } + const char *filepath = filepath_str.c_str(); + + if (!panda::os::IsFileExists(filepath)) { + LOG(ERROR, PROFILER) << "Module not found, path: " << filepath; + } + if (modules_map_.find(m_info.ptr) == modules_map_.end()) { + modules_map_.insert({m_info.ptr, panda_file::OpenPandaFileOrZip(filepath)}); + } + } + + return !modules_map_.empty(); +} + +bool AsptConverter::DumpResolvedTracesAsCSV(const char *filename) +{ + std::unique_ptr dumper; + + switch (dump_type_) { + case DumpType::WITHOUT_THREAD_SEPARATION: + case DumpType::THREAD_SEPARATION_BY_TID: + dumper = std::make_unique(filename, dump_type_, &modules_map_, &methods_map_, + build_cold_graph_); + break; + case DumpType::THREAD_SEPARATION_BY_CSV: + dumper = std::make_unique(filename, &modules_map_, &methods_map_, build_cold_graph_); + break; + default: + UNREACHABLE(); + } + for (auto &[sample, count] : stack_traces_) { + ASSERT(sample.stack_info.managed_stack_size <= SampleInfo::StackInfo::MAX_STACK_DEPTH); + dumper->DumpTraces(sample, count); + } + return true; +} + +void AsptConverter::BuildMethodsMap() +{ + for (const auto &pf_pair : modules_map_) { + const panda_file::File *pf = pf_pair.second.get(); + if (pf == nullptr) { + continue; + } + auto classes_span = pf->GetClasses(); + for (auto id : classes_span) { + if (pf->IsExternal(panda_file::File::EntityId(id))) { + continue; + } + panda_file::ClassDataAccessor cda(*pf, panda_file::File::EntityId(id)); + cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) { + std::string method_name = utf::Mutf8AsCString(mda.GetName().data); + std::string class_name = utf::Mutf8AsCString(cda.GetDescriptor()); + if (class_name[class_name.length() - 1] == ';') { + class_name.pop_back(); + } + std::string full_name = class_name + "::"; + full_name += method_name; + methods_map_[pf][mda.GetMethodId().GetOffset()] = std::move(full_name); + }); + } + } +} + +} // namespace panda::tooling::sampler diff --git a/tools/sampler/aspt_converter.h b/tools/sampler/aspt_converter.h index 8c83c73c5..de9827c5d 100644 --- a/tools/sampler/aspt_converter.h +++ b/tools/sampler/aspt_converter.h @@ -27,135 +27,35 @@ namespace panda::tooling::sampler { -struct SubstitudeModules { +struct SubstituteModules { std::vector source; std::vector destination; }; class AsptConverter { public: + NO_COPY_SEMANTIC(AsptConverter); + NO_MOVE_SEMANTIC(AsptConverter); + using StackTraceMap = std::unordered_map; explicit AsptConverter(const char *filename, DumpType dump_type, bool build_cold_graph, - std::optional substitude_directories) + std::optional substitute_directories) : dump_type_(dump_type), reader_(filename), build_cold_graph_(build_cold_graph), - substitude_directories_(std::move(substitude_directories)) + substitute_directories_(std::move(substitute_directories)) { } ~AsptConverter() = default; - size_t CollectTracesStats() - { - stack_traces_.clear(); - - size_t sample_counter = 0; - SampleInfo sample; - while (reader_.GetNextSample(&sample)) { - ++sample_counter; - - if (dump_type_ == DumpType::WITHOUT_THREAD_SEPARATION) { - // NOTE: zeroing thread_id to make samples indistinguishable in mode without thread separation - sample.thread_info.thread_id = 0; - } - - if (!build_cold_graph_) { - // NOTE: zeroing thread_status to make samples indistinguishable - // in mode without building cold flamegraph - sample.thread_info.thread_status = SampleInfo::ThreadStatus::UNDECLARED; - } - - auto it = stack_traces_.find(sample); - if (it == stack_traces_.end()) { - stack_traces_.insert({sample, 1}); - continue; - } - ++it->second; - } - return sample_counter; - } - - bool CollectModules() - { - FileInfo m_info; - while (reader_.GetNextModule(&m_info)) { - std::string filepath_str = m_info.pathname; - if (substitude_directories_.has_value()) { - for (size_t i = 0; i < substitude_directories_->source.size(); ++i) { - auto pos = filepath_str.find(substitude_directories_->source[i]); - if (pos != std::string::npos) { - filepath_str.replace(pos, substitude_directories_->source[i].size(), - substitude_directories_->destination[i]); - break; - } - } - } - const char *filepath = filepath_str.c_str(); - - if (!panda::os::IsFileExists(filepath)) { - LOG(ERROR, PROFILER) << "Module not found, path: " << filepath; - } - if (modules_map_.find(m_info.ptr) == modules_map_.end()) { - modules_map_.insert({m_info.ptr, panda_file::OpenPandaFileOrZip(filepath)}); - } - } - - return !modules_map_.empty(); - } + size_t CollectTracesStats(); - bool DumpResolvedTracesAsCSV(const char *filename) - { - std::unique_ptr dumper; - - switch (dump_type_) { - case DumpType::WITHOUT_THREAD_SEPARATION: - case DumpType::THREAD_SEPARATION_BY_TID: - dumper = std::make_unique(filename, dump_type_, &modules_map_, &methods_map_, - build_cold_graph_); - break; - case DumpType::THREAD_SEPARATION_BY_CSV: - dumper = std::make_unique(filename, &modules_map_, &methods_map_, build_cold_graph_); - break; - default: - UNREACHABLE(); - } - for (auto &[sample, count] : stack_traces_) { - ASSERT(sample.stack_info.managed_stack_size <= SampleInfo::StackInfo::MAX_STACK_DEPTH); - dumper->DumpTraces(sample, count); - } - return true; - } + bool CollectModules(); - void BuildMethodsMap() - { - for (const auto &pf_pair : modules_map_) { - const panda_file::File *pf = pf_pair.second.get(); - if (pf == nullptr) { - continue; - } - auto classes_span = pf->GetClasses(); - for (auto id : classes_span) { - if (pf->IsExternal(panda_file::File::EntityId(id))) { - continue; - } - panda_file::ClassDataAccessor cda(*pf, panda_file::File::EntityId(id)); - cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) { - std::string method_name = utf::Mutf8AsCString(mda.GetName().data); - std::string class_name = utf::Mutf8AsCString(cda.GetDescriptor()); - if (class_name[class_name.length() - 1] == ';') { - class_name.pop_back(); - } - std::string full_name = class_name + "::"; - full_name += method_name; - methods_map_[pf][mda.GetMethodId().GetOffset()] = std::move(full_name); - }); - } - } - } + void BuildMethodsMap(); - NO_COPY_SEMANTIC(AsptConverter); - NO_MOVE_SEMANTIC(AsptConverter); + bool DumpResolvedTracesAsCSV(const char *filename); private: DumpType dump_type_; @@ -164,7 +64,7 @@ private: MethodMap methods_map_; StackTraceMap stack_traces_; bool build_cold_graph_; - std::optional substitude_directories_; + std::optional substitute_directories_; }; } // namespace panda::tooling::sampler diff --git a/tools/sampler/aspt_convert.cpp b/tools/sampler/main.cpp similarity index 84% rename from tools/sampler/aspt_convert.cpp rename to tools/sampler/main.cpp index dd956ac40..eea5d144a 100644 --- a/tools/sampler/aspt_convert.cpp +++ b/tools/sampler/main.cpp @@ -36,17 +36,17 @@ int Main(int argc, const char **argv) std::string output_filename = cli_options.GetOutput(); bool build_cold_graph = cli_options.IsColdGraphEnable(); - std::optional substitude_directories; - if (cli_options.IsSubstitudeModuleDir()) { - substitude_directories = {cli_options.GetSubstitudeSourceStr(), cli_options.GetSubstitudeDestinationStr()}; - if (substitude_directories->source.size() != substitude_directories->destination.size()) { - LOG(FATAL, PROFILER) << "different number of strings in substitude option"; + std::optional substitute_directories; + if (cli_options.IsSubstituteModuleDir()) { + substitute_directories = {cli_options.GetSubstituteSourceStr(), cli_options.GetSubstituteDestinationStr()}; + if (substitute_directories->source.size() != substitute_directories->destination.size()) { + LOG(FATAL, PROFILER) << "different number of strings in substitute option"; } } DumpType dump_type = parser.GetDumpType(); - AsptConverter conv(input_filename.c_str(), dump_type, build_cold_graph, std::move(substitude_directories)); + AsptConverter conv(input_filename.c_str(), dump_type, build_cold_graph, std::move(substitute_directories)); if (conv.CollectTracesStats() == 0) { LOG(ERROR, PROFILER) << "No samples found in file"; return 1; diff --git a/tools/sampler/options.yaml b/tools/sampler/options.yaml index 16af2c607..de6dce47f 100644 --- a/tools/sampler/options.yaml +++ b/tools/sampler/options.yaml @@ -40,19 +40,19 @@ options: default: false description: Builds cold flame graph -- name: substitude-module-dir +- name: substitute-module-dir type: bool default: false description: Enable substitution of panda files directories e.g. run converter on host for .aspt got from device -- name: substitude-source-str +- name: substitute-source-str type: arg_list_t default: [] - description: Substring that will be replaced with substitude-destination - delimiter: ':' + description: Substrings that will be replaced with substitute-destination + delimiter: ',' -- name: substitude-destination-str +- name: substitute-destination-str type: arg_list_t default: [] - description: Substring that will be places instead of substitude-source - delimiter: ':' + description: Substrings that will be places instead of substitute-source + delimiter: ',' diff --git a/tools/sampler/trace_dumper.cpp b/tools/sampler/trace_dumper.cpp new file mode 100644 index 000000000..e70411177 --- /dev/null +++ b/tools/sampler/trace_dumper.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2021-2022 Huawei Device Co., Ltd. + * Licensed under 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 "trace_dumper.h" + +namespace panda::tooling::sampler { + +void TraceDumper::DumpTraces(const SampleInfo &sample, size_t count) +{ + std::ofstream &stream = ResolveStream(sample); + + for (size_t i = sample.stack_info.managed_stack_size; i-- > 0;) { + uintptr_t pf_id = sample.stack_info.managed_stack[i].panda_file_ptr; + uint64_t file_id = sample.stack_info.managed_stack[i].file_id; + + std::string full_method_name; + if (pf_id == helpers::ToUnderlying(FrameKind::BRIDGE)) { + full_method_name = "System_Frame"; + } else { + const panda_file::File *pf = nullptr; + auto it = modules_map_->find(pf_id); + if (it != modules_map_->end()) { + pf = it->second.get(); + } + + full_method_name = ResolveName(pf, file_id); + } + stream << full_method_name << "; "; + } + stream << count << "\n"; +} + +/* static */ +void TraceDumper::WriteThreadId(std::ofstream &stream, uint32_t thread_id) +{ + stream << "thread_id = " << thread_id << "; "; +} + +/* static */ +void TraceDumper::WriteThreadStatus(std::ofstream &stream, SampleInfo::ThreadStatus thread_status) +{ + stream << "status = "; + + switch (thread_status) { + case SampleInfo::ThreadStatus::RUNNING: { + stream << "active; "; + break; + } + case SampleInfo::ThreadStatus::SUSPENDED: { + stream << "suspended; "; + break; + } + default: { + UNREACHABLE(); + } + } +} + +std::string TraceDumper::ResolveName(const panda_file::File *pf, uint64_t file_id) const +{ + if (pf == nullptr) { + return std::string("__unknown_module::" + std::to_string(file_id)); + } + + auto it = methods_map_->find(pf); + if (it != methods_map_->end()) { + return it->second.at(file_id); + } + + return pf->GetFilename() + "::__unknown_" + std::to_string(file_id); +} + +/* override */ +std::ofstream &SingleCSVDumper::ResolveStream(const SampleInfo &sample) +{ + if (option_ == DumpType::THREAD_SEPARATION_BY_TID) { + WriteThreadId(stream_, sample.thread_info.thread_id); + } + if (build_cold_graph_) { + WriteThreadStatus(stream_, sample.thread_info.thread_status); + } + return stream_; +} + +/* override */ +std::ofstream &MultipleCSVDumper::ResolveStream(const SampleInfo &sample) +{ + auto it = thread_id_map_.find(sample.thread_info.thread_id); + if (it == thread_id_map_.end()) { + std::string filename_with_thread_id = AddThreadIdToFilename(filename_, sample.thread_info.thread_id); + + auto return_pair = + thread_id_map_.insert({sample.thread_info.thread_id, std::ofstream(filename_with_thread_id)}); + it = return_pair.first; + + auto is_success_insert = return_pair.second; + if (!is_success_insert) { + LOG(FATAL, PROFILER) << "Failed while insert in unordored_map"; + } + } + + WriteThreadId(it->second, sample.thread_info.thread_id); + + if (build_cold_graph_) { + WriteThreadStatus(it->second, sample.thread_info.thread_status); + } + + return it->second; +} + +/* static */ +std::string MultipleCSVDumper::AddThreadIdToFilename(const std::string &filename, uint32_t thread_id) +{ + std::string filename_with_thread_id(filename); + + std::size_t pos = filename_with_thread_id.find("csv"); + if (pos == std::string::npos) { + LOG(FATAL, PROFILER) << "Incorrect output filename, *.csv format expected"; + } + + filename_with_thread_id.insert(pos - 1, std::to_string(thread_id)); + + return filename_with_thread_id; +} + +} // namespace panda::tooling::sampler diff --git a/tools/sampler/trace_dumper.h b/tools/sampler/trace_dumper.h index 82a60095c..7d1a2f2d5 100644 --- a/tools/sampler/trace_dumper.h +++ b/tools/sampler/trace_dumper.h @@ -39,54 +39,25 @@ enum class DumpType { class TraceDumper { public: + NO_COPY_SEMANTIC(TraceDumper); + NO_MOVE_SEMANTIC(TraceDumper); + explicit TraceDumper(ModuleMap *modules_map, MethodMap *methods_map) : modules_map_(modules_map), methods_map_(methods_map) { } virtual ~TraceDumper() = default; - virtual std::ofstream &ResolveStream(const SampleInfo &sample) = 0; + void DumpTraces(const SampleInfo &sample, size_t count); - void DumpTraces(const SampleInfo &sample, size_t count) - { - std::ofstream &stream = ResolveStream(sample); - - for (size_t i = sample.stack_info.managed_stack_size; i-- > 0;) { - uintptr_t pf_id = sample.stack_info.managed_stack[i].panda_file_ptr; - uint64_t file_id = sample.stack_info.managed_stack[i].file_id; - - std::string full_method_name; - if (pf_id == helpers::ToUnderlying(FrameKind::BRIDGE)) { - full_method_name = "System_Frame"; - } else { - const panda_file::File *pf = nullptr; - auto it = modules_map_->find(pf_id); - if (it != modules_map_->end()) { - pf = it->second.get(); - } - - full_method_name = ResolveName(pf, file_id); - } - stream << full_method_name << "; "; - } - stream << count << "\n"; - } +protected: + static void WriteThreadId(std::ofstream &stream, uint32_t thread_id); + static void WriteThreadStatus(std::ofstream &stream, SampleInfo::ThreadStatus thread_status); - std::string ResolveName(const panda_file::File *pf, uint64_t file_id) const - { - if (pf == nullptr) { - return std::string("__unknown_module::" + std::to_string(file_id)); - } - - auto it = methods_map_->find(pf); - if (it != methods_map_->end()) { - return it->second.at(file_id); - } - return pf->GetFilename() + "::__unknown_" + std::to_string(file_id); - } +private: + virtual std::ofstream &ResolveStream(const SampleInfo &sample) = 0; - NO_COPY_SEMANTIC(TraceDumper); - NO_MOVE_SEMANTIC(TraceDumper); + std::string ResolveName(const panda_file::File *pf, uint64_t file_id) const; private: ModuleMap *modules_map_ {nullptr}; @@ -95,6 +66,9 @@ private: class SingleCSVDumper final : public TraceDumper { public: + NO_COPY_SEMANTIC(SingleCSVDumper); + NO_MOVE_SEMANTIC(SingleCSVDumper); + explicit SingleCSVDumper(const char *filename, DumpType option, ModuleMap *modules_map, MethodMap *methods_map, bool build_cold_graph) : TraceDumper(modules_map, methods_map), @@ -105,19 +79,8 @@ public: } ~SingleCSVDumper() override = default; - std::ofstream &ResolveStream(const SampleInfo &sample) override - { - if (option_ == DumpType::THREAD_SEPARATION_BY_TID) { - stream_ << "thread_id = " << sample.thread_info.thread_id << "; "; - } - if (build_cold_graph_) { - stream_ << "status = " << static_cast(sample.thread_info.thread_status) << "; "; - } - return stream_; - } - - NO_COPY_SEMANTIC(SingleCSVDumper); - NO_MOVE_SEMANTIC(SingleCSVDumper); +private: + std::ofstream &ResolveStream(const SampleInfo &sample) override; private: std::ofstream stream_; @@ -127,6 +90,9 @@ private: class MultipleCSVDumper final : public TraceDumper { public: + NO_COPY_SEMANTIC(MultipleCSVDumper); + NO_MOVE_SEMANTIC(MultipleCSVDumper); + explicit MultipleCSVDumper(const char *filename, ModuleMap *modules_map, MethodMap *methods_map, bool build_cold_graph) : TraceDumper(modules_map, methods_map), filename_(filename), build_cold_graph_(build_cold_graph) @@ -134,55 +100,10 @@ public: } ~MultipleCSVDumper() override = default; - static std::string AddThreadIdToFilename(const std::string &filename, const uint32_t thread_id) - { - std::string filename_with_thread_id(filename); - std::size_t pos = filename_with_thread_id.find("csv"); - if (pos == std::string::npos) { - LOG(FATAL, PROFILER) << "Incorrect output filename, *.csv format expected"; - } - filename_with_thread_id.insert(pos - 1, std::to_string(thread_id)); - return filename_with_thread_id; - } - - std::ofstream &ResolveStream(const SampleInfo &sample) override - { - auto it = thread_id_map_.find(sample.thread_info.thread_id); - if (it == thread_id_map_.end()) { - std::string filename_with_thread_id = AddThreadIdToFilename(filename_, sample.thread_info.thread_id); - - auto return_pair = - thread_id_map_.insert({sample.thread_info.thread_id, std::ofstream(filename_with_thread_id)}); - it = return_pair.first; - - auto is_success_insert = return_pair.second; - if (!is_success_insert) { - LOG(FATAL, PROFILER) << "Failed while insert in unordored_map"; - } - } - it->second << "thread_id = " << sample.thread_info.thread_id << "; "; - if (build_cold_graph_) { - it->second << "status = "; - switch (sample.thread_info.thread_status) { - case SampleInfo::ThreadStatus::RUNNING: { - it->second << "active; "; - break; - } - case SampleInfo::ThreadStatus::SUSPENDED: { - it->second << "suspended; "; - break; - } - default: { - UNREACHABLE(); - } - } - } - - return it->second; - } +private: + std::ofstream &ResolveStream(const SampleInfo &sample) override; - NO_COPY_SEMANTIC(MultipleCSVDumper); - NO_MOVE_SEMANTIC(MultipleCSVDumper); + static std::string AddThreadIdToFilename(const std::string &filename, uint32_t thread_id); private: std::string filename_; -- Gitee