1/* 2 * Copyright (c) 2021-2022 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#define HILOG_TAG "Protobuf" 16 17#include "report_protobuf_file.h" 18#include "utilities.h" 19 20using namespace Proto; 21namespace OHOS { 22namespace Developtools { 23namespace HiPerf { 24// output 25ReportProtobufFileWriter::~ReportProtobufFileWriter() 26{ 27 Close(); 28} 29 30void ReportProtobufFileWriter::BeforeClose() 31{ 32 HiperfRecord record; 33 SampleStatistic *message = record.mutable_statistic(); 34 35 message->set_count(recordCount_); 36 message->set_lost(recordLost_); 37 protpbufCodedOutputStream_->WriteLittleEndian32(record.ByteSizeLong()); 38 record.SerializeToCodedStream(protpbufCodedOutputStream_.get()); 39} 40 41void ReportProtobufFileWriter::Close() 42{ 43 if (protobufFileStream_->is_open()) { 44 BeforeClose(); 45 // write 0 as end 46 protpbufCodedOutputStream_->WriteLittleEndian32(0); 47 protpbufCodedOutputStream_.reset(nullptr); 48 protpbufOutputStream_.reset(nullptr); 49 protobufFileStream_->close(); 50 } 51} 52 53bool ReportProtobufFileWriter::Write(const void *buffer, int size) 54{ 55 if (protobufFileStream_->is_open()) { 56 try { 57 protobufFileStream_->write(static_cast<const char *>(buffer), size); 58 HLOGM("writed %d bytes", size); 59 return true; 60 } catch (std::ofstream::failure &writeErr) { 61 HLOGE("write file failed %s", fileName_.c_str()); 62 } 63 } else { 64 printf("no file open for write (request %d bytes).\n", size); 65 } 66 return false; 67} 68 69bool ReportProtobufFileWriter::Create(std::string fileName) 70{ 71 fileName_ = fileName; 72 try { 73 protobufFileStream_->exceptions(std::ofstream::failbit | std::ofstream::badbit | 74 std::ofstream::eofbit); 75 std::string resolvedPath = CanonicalizeSpecPath(fileName_.c_str()); 76 protobufFileStream_->open(resolvedPath.c_str(), 77 std::fstream::out | std::fstream::trunc | std::fstream::binary); 78 protpbufOutputStream_ = 79 std::make_unique<google::protobuf::io::CopyingOutputStreamAdaptor>(this); 80 protpbufCodedOutputStream_ = 81 std::make_unique<google::protobuf::io::CodedOutputStream>(protpbufOutputStream_.get()); 82 83 printf("open proto buf file succeed.\n"); 84 85 Write(FILE_MAGIC, sizeof(FILE_MAGIC) - 1); 86 Write(&FILE_VERSION, sizeof(FILE_VERSION)); 87 88 printf("create proto buf file succeed.\n"); 89 return true; 90 } catch (const std::fstream::failure &e) { 91 printf("open proto buf file faild. %s\n", e.what()); 92 } 93 return false; 94} 95 96bool ReportProtobufFileWriter::IsOpen() 97{ 98 return protobufFileStream_->is_open(); 99} 100 101bool ReportProtobufFileWriter::ProcessRecord(const PerfEventRecord &record) 102{ 103 HLOGM("ProcessRecord %d", record.GetType()); 104 if (record.GetType() == PERF_RECORD_SAMPLE) { 105 HLOGF("record.GetType() == PERF_RECORD_SAMPLE"); 106 } else if (record.GetType() == PERF_RECORD_LOST_SAMPLES) { 107 ProcessRecord(*static_cast<const PerfRecordLost *>(&record)); 108 } else if (record.GetType() == PERF_RECORD_COMM) { 109 ProcessRecord(*static_cast<const PerfRecordComm *>(&record)); 110 } else { 111 HLOGM("skip record type %d", record.GetType()); 112 return false; 113 } 114 return true; 115} 116 117bool ReportProtobufFileWriter::ProcessSampleRecord( 118 const PerfRecordSample &recordSample, uint32_t configIndex, 119 const std::vector<std::unique_ptr<SymbolsFile>> &symbolsFiles) 120{ 121 HiperfRecord record; 122 CallStackSample *sample = record.mutable_sample(); 123 sample->set_time(recordSample.data_.time); 124 sample->set_tid(recordSample.data_.tid); 125 for (const DfxFrame &frame : recordSample.callFrames_) { 126 auto callframe = sample->add_callstackframe(); 127 callframe->set_symbols_vaddr(frame.funcOffset); 128 callframe->set_loaded_vaddr(frame.pc - frame.mapOffset); 129 if (frame.symbolFileIndex >= 0) { 130 callframe->set_symbols_file_id(frame.symbolFileIndex); 131 callframe->set_function_name_id(frame.index); 132 } 133 } 134 sample->set_event_count(recordSample.data_.period); 135 sample->set_config_name_id(configIndex); 136 137 protpbufCodedOutputStream_->WriteLittleEndian32(record.ByteSizeLong()); 138 record.SerializeToCodedStream(protpbufCodedOutputStream_.get()); 139 recordCount_++; 140 141 return true; 142} 143bool ReportProtobufFileWriter::ProcessReportInfo(const std::vector<std::string> &configNames, 144 const std::string &workloadCmd) 145{ 146 HiperfRecord record; 147 ReportInfo *info = record.mutable_info(); 148 HLOGV("configNames:%zu", configNames.size()); 149 for (auto configName : configNames) { 150 info->add_config_name(configName); 151 } 152 info->set_workload_cmd(workloadCmd); 153 154 protpbufCodedOutputStream_->WriteLittleEndian32(record.ByteSizeLong()); 155 record.SerializeToCodedStream(protpbufCodedOutputStream_.get()); 156 return true; 157} 158 159bool ReportProtobufFileWriter::ProcessRecord(const PerfRecordLost &recordLost) 160{ 161 recordLost.DumpLog(__FUNCTION__); 162 recordLost_ += recordLost.data_.lost; 163 return true; 164} 165 166bool ReportProtobufFileWriter::ProcessRecord(const PerfRecordComm &recordComm) 167{ 168 recordComm.DumpLog(__FUNCTION__); 169 HiperfRecord record; 170 VirtualThreadInfo *thread = record.mutable_thread(); 171 172 thread->set_tid(recordComm.data_.tid); 173 thread->set_pid(recordComm.data_.pid); 174 thread->set_name(recordComm.data_.comm); 175 176 protpbufCodedOutputStream_->WriteLittleEndian32(record.ByteSizeLong()); 177 record.SerializeToCodedStream(protpbufCodedOutputStream_.get()); 178 return true; 179} 180 181bool ReportProtobufFileWriter::ProcessSymbolsFiles( 182 const std::vector<std::unique_ptr<SymbolsFile>> &symbolsFiles) 183{ 184 uint32_t id = 0; 185 for (auto &symbolsFile : symbolsFiles) { 186 HiperfRecord record; 187 SymbolTableFile *message = record.mutable_file(); 188 189 message->set_id(id++); 190 message->set_path(symbolsFile->filePath_); 191 192 for (auto &symbol : symbolsFile->GetSymbols()) { 193 message->add_function_name(symbol.GetName().data()); 194 } 195 196 protpbufCodedOutputStream_->WriteLittleEndian32(record.ByteSizeLong()); 197 record.SerializeToCodedStream(protpbufCodedOutputStream_.get()); 198 } 199 return true; 200} 201 202// input 203int ReportProtobufFileReader::Read(void *buffer, int size) 204{ 205 if (protobufFileStream_->is_open()) { 206 try { 207 protobufFileStream_->read(static_cast<char *>(buffer), size); 208 HLOGM("readed %d bytes", size); 209 return size; 210 } catch (std::ifstream::failure &readErr) { 211 if (protobufFileStream_->eof()) { 212 // this is not a issue 213 HLOGW("read file %d bytes failed eof. only return %zu\n", size, 214 protobufFileStream_->gcount()); 215 216 return protobufFileStream_->gcount(); 217 } 218 printf("read file %d bytes failed %s : %s\n", size, fileName_.c_str(), readErr.what()); 219 } 220 } else { 221 printf("no file open for read (request %d bytes).\n", size); 222 } 223 return 0; 224} 225 226bool ReportProtobufFileReader::CheckFileMagic() 227{ 228 char fileMagic[sizeof(FILE_MAGIC)] = {0}; 229 Read(fileMagic, sizeof(FILE_MAGIC) - 1); 230 if (memcmp(fileMagic, FILE_MAGIC, sizeof(FILE_MAGIC) - 1) != 0) { 231 printf("file magic is NOT correct. %s: %x\n", fileMagic, fileMagic[0]); 232 return false; 233 } 234 235 uint16_t version = 0; 236 Read(&version, sizeof(version)); 237 if (version != FILE_VERSION) { 238 printf("file version is NOT correct.\n"); 239 return false; 240 } 241 242 return true; 243} 244 245bool ReportProtobufFileReader::Dump(std::string fileName, ProtobufReadBack readBack) 246{ 247 const int defaultIndent = 0; 248 fileName_ = fileName; 249 try { 250 protobufFileStream_->exceptions(std::ifstream::failbit | std::ifstream::badbit); 251 std::string resolvedPath = CanonicalizeSpecPath(fileName_.c_str()); 252 protobufFileStream_->open(resolvedPath.c_str(), std::fstream::in | std::fstream::binary); 253 printf("open proto buf file succeed.\n"); 254 if (!CheckFileMagic()) { 255 return false; 256 } 257 protpbufInputStream_ = std::make_unique<google::protobuf::io::CopyingInputStreamAdaptor>(this); 258 protpbufCodedInputStream_ = 259 std::make_unique<google::protobuf::io::CodedInputStream>(protpbufInputStream_.get()); 260 uint32_t recordLength = 0; 261 do { 262 protpbufCodedInputStream_->ReadLittleEndian32(&recordLength); 263 if (recordLength != 0) { 264 PRINT_INDENT(defaultIndent, "record length:%u (%x)\n", recordLength, recordLength); 265 HiperfRecord record; 266 std::string recordBuf; 267 recordBuf.resize(recordLength); 268 if (!protpbufCodedInputStream_->ReadString(&recordBuf, recordLength)) { 269 printf("read record error\n"); 270 return false; 271 } 272 if (!record.ParseFromString(recordBuf)) { 273 printf("parse format error\n"); 274 return false; 275 } else { 276 if (readBack == nullptr) { 277 PRINT_INDENT(defaultIndent, "\n"); 278 Dump(record, defaultIndent); 279 } else { 280 readBack(record); 281 } 282 } 283 } else { 284 if (readBack == nullptr) { 285 printf("no more record\n"); 286 } 287 break; 288 } 289 } while (recordLength != 0); 290 return true; 291 } catch (const std::fstream::failure &e) { 292 HLOGE("open proto buf file faild. %s\n", e.what()); 293 } 294 return false; 295} 296 297bool ReportProtobufFileReader::Dump(const CallStackSample &message, int indent) 298{ 299 PRINT_INDENT(indent, "%s:\n", message.GetTypeName().c_str()); 300 if (message.has_time()) { 301 PRINT_INDENT(indent + 1, "time:%" PRId64 "\n", message.time()); 302 } 303 if (message.has_tid()) { 304 PRINT_INDENT(indent + 1, "tid:%u\n", message.tid()); 305 } 306 for (int i = 0; i < message.callstackframe_size(); i++) { 307 PRINT_INDENT(indent + 1, "%d:\n", i); 308 auto &callframe = message.callstackframe(i); 309 if (callframe.has_symbols_vaddr()) { 310 PRINT_INDENT(indent + INDENT_TWO, "symbols_vaddr: 0x%" PRIx64 " \n", 311 callframe.symbols_vaddr()); 312 } 313 if (callframe.has_symbols_file_id()) { 314 PRINT_INDENT(indent + INDENT_TWO, "symbols_file_id: %u\n", callframe.symbols_file_id()); 315 } 316 if (callframe.has_function_name_id()) { 317 PRINT_INDENT(indent + INDENT_TWO, "function_name_id: %d\n", callframe.function_name_id()); 318 } 319 } 320 if (message.has_event_count()) { 321 PRINT_INDENT(indent + 1, "event_count:%" PRIu64 "\n", message.event_count()); 322 } 323 if (message.has_config_name_id()) { 324 PRINT_INDENT(indent + 1, "config_name_id:%u\n", message.config_name_id()); 325 } 326 return true; 327} 328 329bool ReportProtobufFileReader::Dump(const SampleStatistic &message, int indent) 330{ 331 PRINT_INDENT(indent, "%s:\n", message.GetTypeName().c_str()); 332 if (message.has_count()) { 333 PRINT_INDENT(indent + 1, "count:%" PRIu64 "\n", message.count()); 334 } 335 if (message.has_lost()) { 336 PRINT_INDENT(indent + 1, "lost:%" PRIu64 "\n", message.lost()); 337 } 338 return false; 339} 340 341bool ReportProtobufFileReader::Dump(const SymbolTableFile &message, int indent) 342{ 343 PRINT_INDENT(indent, "%s:\n", message.GetTypeName().c_str()); 344 if (message.has_id()) { 345 PRINT_INDENT(indent + 1, "id: %u\n", message.id()); 346 } 347 if (message.has_path()) { 348 PRINT_INDENT(indent + 1, "path: %s\n", message.path().c_str()); 349 } 350 for (int i = 0; i < message.function_name_size(); i++) { 351 PRINT_INDENT(indent + INDENT_TWO, "%d:%s\n", i, message.function_name(i).c_str()); 352 } 353 return false; 354} 355bool ReportProtobufFileReader::Dump(const VirtualThreadInfo &message, int indent) 356{ 357 PRINT_INDENT(indent, "%s:\n", message.GetTypeName().c_str()); 358 if (message.has_pid()) { 359 PRINT_INDENT(indent + 1, "pid:%u\n", message.pid()); 360 } 361 if (message.has_tid()) { 362 PRINT_INDENT(indent + 1, "tid:%u\n", message.tid()); 363 } 364 if (message.has_pid()) { 365 PRINT_INDENT(indent + 1, "name:%s\n", message.name().c_str()); 366 } 367 return false; 368} 369bool ReportProtobufFileReader::Dump(const ReportInfo &message, int indent) 370{ 371 PRINT_INDENT(indent, "%s:\n", message.GetTypeName().c_str()); 372 for (int i = 0; i < message.config_name_size(); i++) { 373 PRINT_INDENT(indent + 1, "config_name:%d:%s\n", i, message.config_name(i).c_str()); 374 } 375 if (message.has_workload_cmd()) { 376 PRINT_INDENT(indent + 1, "workload:%s\n", message.workload_cmd().c_str()); 377 } 378 return true; 379} 380bool ReportProtobufFileReader::Dump(const HiperfRecord &record, int indent) 381{ 382 PRINT_INDENT(indent, "%s:\n", record.GetTypeName().c_str()); 383 if (record.has_sample()) { 384 return Dump(record.sample(), indent + 1); 385 } else if (record.has_statistic()) { 386 return Dump(record.statistic(), indent + 1); 387 } else if (record.has_file()) { 388 return Dump(record.file(), indent + 1); 389 } else if (record.has_thread()) { 390 return Dump(record.thread(), indent + 1); 391 } else if (record.has_info()) { 392 return Dump(record.info(), indent + 1); 393 } else { 394 printf("unknow proto buf format\n"); 395 return false; 396 } 397} 398bool ReportProtobufFileReader::IsOpen() 399{ 400 return protobufFileStream_->is_open(); 401} 402} // namespace HiPerf 403} // namespace Developtools 404} // namespace OHOS 405