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