diff --git a/runtime/tests/tooling/sampler/CMakeLists.txt b/runtime/tests/tooling/sampler/CMakeLists.txt index 299fa09d39d8cd1f6e7f2c7a77e162b2c33bc7db..aa226041c3a4b651247d84bbf8cdeb3545dfd508 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 307552ca451b58c854d0371a7ff88804d96ea8b7..471d3e834a2086c87972eab29bb53a3d2a39720f 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 4503582705de692938dce2e269bb8c466d4d2c8a..df5c3a3e9f6c8121191795e3d04c0a49a0d82dcd 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 0000000000000000000000000000000000000000..06bdaa548c261a6de641753e9addc3f3ead27725 --- /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 8c83c73c5b136bca109c3aea23750f3fa5850762..de9827c5d65d21678d7b023b462a06f009ad56c9 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 dd956ac40b7d6ec76358b241d704d68dc319ccb2..eea5d144a6639d2e37859e37cfc934715e04285d 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 16af2c6075b37acffe7ac057e23b5a3e2dbed7a4..de6dce47f418e407446f81192aa63e3016bca5e4 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 0000000000000000000000000000000000000000..e704111775558a56946a450e1411d4042767621f --- /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 82a60095c451b63d699157d2c9f764c95e3b63c2..7d1a2f2d5960ac1a250b6e7a558be0320111cb76 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_;