1 /** 2 * Copyright (c) 2021-2025 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef PANDA_RUNTIME_TOOLING_SAMPLER_SAMPLE_WRITER_H 17 #define PANDA_RUNTIME_TOOLING_SAMPLER_SAMPLE_WRITER_H 18 19 #include <iostream> 20 #include <string> 21 #include <fstream> 22 23 #include "libpandabase/os/thread.h" 24 25 #include "runtime/tooling/sampler/sample_info.h" 26 #include "runtime/tooling/sampler/samples_record.h" 27 28 #include <unordered_set> 29 30 namespace ark::tooling::sampler { 31 32 class StreamWriter { 33 public: 34 StreamWriter() = default; 35 virtual ~StreamWriter() = default; 36 NO_COPY_SEMANTIC(StreamWriter); 37 NO_MOVE_SEMANTIC(StreamWriter); 38 39 virtual void WriteModule(const FileInfo &moduleInfo) = 0; 40 virtual void WriteSample(const SampleInfo &sample) const = 0; 41 virtual bool IsModuleWritten(const FileInfo &moduleInfo) const = 0; 42 }; 43 44 /* 45 * ======================================================= 46 * ============= Sampler binary format ================== 47 * ======================================================= 48 * 49 * Writing with the fasters and more convenient format .aspt 50 * Then it should be converted to flamegraph 51 * 52 * .aspt - ark sampling profiler trace file, binary format 53 * 54 * .aspt consists of 2 type information: 55 * - module row (panda file and its pointer) 56 * - sample row (sample information) 57 * 58 * module row for 64-bits: 59 * first 8 byte is 0xFFFFFFFF (to recognize that it's not a sample row) 60 * next 8 byte is pointer module 61 * next 8 byte is size of panda file name 62 * next bytes is panda file name in ASCII symbols 63 * 64 * sample row for 64-bits: 65 * first 4 bytes is thread id of thread from sample was obtained 66 * next 4 bytes is thread status of thread from sample was obtained 67 * next 8 bytes is stack size 68 * next bytes is stack frame 69 * one stack frame is panda file ptr and file id 70 * 71 * Example for 64-bit architecture: 72 * 73 * Thread id Thread status Stack Size Managed stack frame id 74 * Sample row |___________|___________|________________|_____________------___________| 75 * 32 bits 32 bits 64 bits (128 * <stack size>) bits 76 * 77 * 0xFF..FF pointer checksum name size module path (ASCII str) 78 * Module row |__________|__________|__________|___________|_____________------___________| 79 * 64 bits 64 bits 32 bits 64 bits (8 * <name size>) bits 80 */ 81 class FileStreamWriter final : public StreamWriter { 82 public: FileStreamWriter(const std::string & filename)83 explicit FileStreamWriter(const std::string &filename) 84 { 85 /* 86 * This class instance should be used only from one thread 87 * It may lead to format invalidation 88 * This class wasn't made thread safe for performance reason 89 */ 90 std::string finalFileName; 91 if (filename.empty()) { 92 std::time_t currentTime = std::time(nullptr); 93 std::tm *localTime = std::localtime(¤tTime); 94 finalFileName = std::to_string(localTime->tm_hour) + "-" + std::to_string(localTime->tm_min) + "-" + 95 std::to_string(localTime->tm_sec) + ".aspt"; 96 } else { 97 finalFileName = filename; 98 } 99 writeStreamPtr_ = std::make_unique<std::ofstream>(finalFileName.c_str(), std::ios::binary); 100 ASSERT(writeStreamPtr_ != nullptr); 101 } 102 ~FileStreamWriter()103 ~FileStreamWriter() override 104 { 105 if (writeStreamPtr_ && writeStreamPtr_->is_open()) { 106 writeStreamPtr_->flush(); 107 } 108 }; 109 110 PANDA_PUBLIC_API void WriteModule(const FileInfo &moduleInfo) override; 111 PANDA_PUBLIC_API void WriteSample(const SampleInfo &sample) const override; 112 IsModuleWritten(const FileInfo & moduleInfo)113 bool IsModuleWritten(const FileInfo &moduleInfo) const override 114 { 115 return writtenModules_.find(moduleInfo) != writtenModules_.end(); 116 } 117 118 NO_COPY_SEMANTIC(FileStreamWriter); 119 NO_MOVE_SEMANTIC(FileStreamWriter); 120 121 static constexpr uintptr_t MODULE_INDICATOR_VALUE = 0xFFFFFFFF; 122 123 private: 124 std::unique_ptr<std::ofstream> writeStreamPtr_; 125 std::unordered_set<FileInfo> writtenModules_; 126 }; 127 128 class InspectorStreamWriter final : public StreamWriter { 129 public: InspectorStreamWriter(std::shared_ptr<SamplesRecord> samplesRecord)130 explicit InspectorStreamWriter(std::shared_ptr<SamplesRecord> samplesRecord) 131 : samplesRecord_(std::move(samplesRecord)) 132 { 133 } ~InspectorStreamWriter()134 ~InspectorStreamWriter() override 135 { 136 samplesRecord_.reset(); 137 } WriteModule(const FileInfo & moduleInfo)138 PANDA_PUBLIC_API void WriteModule(const FileInfo &moduleInfo) override 139 { 140 UNUSED_VAR(moduleInfo); 141 } WriteSample(const SampleInfo & sample)142 PANDA_PUBLIC_API void WriteSample(const SampleInfo &sample) const override 143 { 144 samplesRecord_->AddSampleInfo(sample.threadInfo.threadId, std::make_unique<SampleInfo>(sample)); 145 } IsModuleWritten(const FileInfo & moduleInfo)146 bool IsModuleWritten(const FileInfo &moduleInfo) const override 147 { 148 UNUSED_VAR(moduleInfo); 149 return true; 150 } 151 NO_COPY_SEMANTIC(InspectorStreamWriter); 152 NO_MOVE_SEMANTIC(InspectorStreamWriter); 153 154 private: 155 std::shared_ptr<SamplesRecord> samplesRecord_ = nullptr; 156 }; 157 158 } // namespace ark::tooling::sampler 159 160 #endif // PANDA_RUNTIME_TOOLING_SAMPLER_SAMPLE_WRITER_H 161