• 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 
130         for (auto fileIt = symbolsFiles.begin(); fileIt != symbolsFiles.end(); fileIt++) {
131             if (fileIt->get()->filePath_ == frame.filePath_) {
132                 callframe->set_symbols_file_id(fileIt - symbolsFiles.begin());
133                 callframe->set_function_name_id(frame.symbolIndex_);
134                 break;
135             }
136         }
137 
138         sample->set_event_count(recordSample.data_.period);
139         sample->set_config_name_id(configIndex);
140     }
141 
142     protpbufCodedOutputStream_->WriteLittleEndian32(record.ByteSizeLong());
143     record.SerializeToCodedStream(protpbufCodedOutputStream_.get());
144     recordCount_++;
145 
146     return true;
147 }
ProcessReportInfo(const std::vector<std::string> & configNames,const std::string & workloadCmd)148 bool ReportProtobufFileWriter::ProcessReportInfo(const std::vector<std::string> &configNames,
149                                                  const std::string &workloadCmd)
150 {
151     HiperfRecord record;
152     ReportInfo *info = record.mutable_info();
153     HLOGV("configNames:%zu", configNames.size());
154     for (auto configName : configNames) {
155         info->add_config_name(configName);
156     }
157     info->set_workload_cmd(workloadCmd);
158 
159     protpbufCodedOutputStream_->WriteLittleEndian32(record.ByteSizeLong());
160     record.SerializeToCodedStream(protpbufCodedOutputStream_.get());
161     return true;
162 }
163 
ProcessRecord(const PerfRecordLost & recordLost)164 bool ReportProtobufFileWriter::ProcessRecord(const PerfRecordLost &recordLost)
165 {
166     recordLost.DumpLog(__FUNCTION__);
167     recordLost_ += recordLost.data_.lost;
168     return true;
169 }
170 
ProcessRecord(const PerfRecordComm & recordComm)171 bool ReportProtobufFileWriter::ProcessRecord(const PerfRecordComm &recordComm)
172 {
173     recordComm.DumpLog(__FUNCTION__);
174     HiperfRecord record;
175     VirtualThreadInfo *thread = record.mutable_thread();
176 
177     thread->set_tid(recordComm.data_.tid);
178     thread->set_pid(recordComm.data_.pid);
179     thread->set_name(recordComm.data_.comm);
180 
181     protpbufCodedOutputStream_->WriteLittleEndian32(record.ByteSizeLong());
182     record.SerializeToCodedStream(protpbufCodedOutputStream_.get());
183     return true;
184 }
185 
ProcessSymbolsFiles(const std::vector<std::unique_ptr<SymbolsFile>> & symbolsFiles)186 bool ReportProtobufFileWriter::ProcessSymbolsFiles(
187     const std::vector<std::unique_ptr<SymbolsFile>> &symbolsFiles)
188 {
189     uint32_t id = 0;
190     for (auto &symbolsFile : symbolsFiles) {
191         HiperfRecord record;
192         SymbolTableFile *message = record.mutable_file();
193 
194         message->set_id(id++);
195         message->set_path(symbolsFile->filePath_);
196 
197         for (auto &symbol : symbolsFile->GetSymbols()) {
198             message->add_function_name(symbol.Name().data());
199         }
200 
201         protpbufCodedOutputStream_->WriteLittleEndian32(record.ByteSizeLong());
202         record.SerializeToCodedStream(protpbufCodedOutputStream_.get());
203     }
204     return true;
205 }
206 
207 // input
Read(void * buffer,int size)208 int ReportProtobufFileReader::Read(void *buffer, int size)
209 {
210     if (protobufFileStream_->is_open()) {
211         try {
212             protobufFileStream_->read(static_cast<char *>(buffer), size);
213             HLOGM("readed %d bytes", size);
214             return size;
215         } catch (std::ifstream::failure &readErr) {
216             if (protobufFileStream_->eof()) {
217                 // this is not a issue
218                 HLOGW("read file %d bytes failed eof. only return %zu\n", size,
219                       protobufFileStream_->gcount());
220 
221                 return protobufFileStream_->gcount();
222             }
223             printf("read file %d bytes failed %s : %s\n", size, fileName_.c_str(), readErr.what());
224         }
225     } else {
226         printf("no file open for read (request %d bytes).\n", size);
227     }
228     return 0;
229 }
230 
CheckFileMagic()231 bool ReportProtobufFileReader::CheckFileMagic()
232 {
233     char fileMagic[sizeof(FILE_MAGIC)] = {0};
234     Read(fileMagic, sizeof(FILE_MAGIC) - 1);
235     if (memcmp(fileMagic, FILE_MAGIC, sizeof(FILE_MAGIC) - 1) != 0) {
236         printf("file magic is NOT correct. %s: %x\n", fileMagic, fileMagic[0]);
237         return false;
238     }
239 
240     uint16_t version = 0;
241     Read(&version, sizeof(version));
242     if (version != FILE_VERSION) {
243         printf("file version is NOT correct.\n");
244         return false;
245     }
246 
247     return true;
248 }
249 
Dump(std::string fileName,ProtobufReadBack readBack)250 bool ReportProtobufFileReader::Dump(std::string fileName, ProtobufReadBack readBack)
251 {
252     const int defaultIndent = 0;
253     fileName_ = fileName;
254     try {
255         protobufFileStream_->exceptions(std::ifstream::failbit | std::ifstream::badbit);
256         std::string resolvedPath = CanonicalizeSpecPath(fileName_.c_str());
257         protobufFileStream_->open(resolvedPath.c_str(), std::fstream::in | std::fstream::binary);
258         printf("open proto buf file succeed.\n");
259         if (!CheckFileMagic()) {
260             return false;
261         }
262         protpbufInputStream_ =std::make_unique<google::protobuf::io::CopyingInputStreamAdaptor>(this);
263         protpbufCodedInputStream_ =
264             std::make_unique<google::protobuf::io::CodedInputStream>(protpbufInputStream_.get());
265         uint32_t recordLength = 0;
266         do {
267             protpbufCodedInputStream_->ReadLittleEndian32(&recordLength);
268             if (recordLength != 0) {
269                 PrintIndent(defaultIndent, "record length:%u (%x)\n", recordLength, recordLength);
270                 HiperfRecord record;
271                 std::string recordBuf;
272                 recordBuf.resize(recordLength);
273                 if (!protpbufCodedInputStream_->ReadString(&recordBuf, recordLength)) {
274                     printf("read record error\n");
275                     return false;
276                 }
277                 if (!record.ParseFromString(recordBuf)) {
278                     printf("parse format error\n");
279                     return false;
280                 } else {
281                     if (readBack == nullptr) {
282                         PrintIndent(defaultIndent, "\n");
283                         Dump(record, defaultIndent);
284                     } else {
285                         readBack(record);
286                     }
287                 }
288             } else {
289                 if (readBack == nullptr) {
290                     printf("no more record\n");
291                 }
292                 break;
293             }
294         } while (recordLength != 0);
295         return true;
296     } catch (const std::fstream::failure &e) {
297         HLOGE("open proto buf file faild. %s\n", e.what());
298     }
299     return false;
300 }
301 
Dump(const CallStackSample & message,int indent)302 bool ReportProtobufFileReader::Dump(const CallStackSample &message, int indent)
303 {
304     PrintIndent(indent, "%s:\n", message.GetTypeName().c_str());
305     if (message.has_time()) {
306         PrintIndent(INDENT_ONE_LEVEL, "time:%" PRId64 "\n", message.time());
307     }
308     if (message.has_tid()) {
309         PrintIndent(INDENT_ONE_LEVEL, "tid:%u\n", message.tid());
310     }
311     for (int i = 0; i < message.callstackframe_size(); i++) {
312         PrintIndent(INDENT_ONE_LEVEL, "%d:\n", i);
313         auto &callframe = message.callstackframe(i);
314         if (callframe.has_symbols_vaddr()) {
315             PrintIndent(INDENT_TWO_LEVEL, "symbols_vaddr: 0x%" PRIx64 " \n",
316                         callframe.symbols_vaddr());
317         }
318         if (callframe.has_symbols_file_id()) {
319             PrintIndent(INDENT_TWO_LEVEL, "symbols_file_id: %u\n", callframe.symbols_file_id());
320         }
321         if (callframe.has_function_name_id()) {
322             PrintIndent(INDENT_TWO_LEVEL, "function_name_id: %d\n", callframe.function_name_id());
323         }
324     }
325     if (message.has_event_count()) {
326         PrintIndent(INDENT_ONE_LEVEL, "event_count:%" PRIu64 "\n", message.event_count());
327     }
328     if (message.has_config_name_id()) {
329         PrintIndent(INDENT_ONE_LEVEL, "config_name_id:%u\n", message.config_name_id());
330     }
331     return true;
332 }
333 
Dump(const SampleStatistic & message,int indent)334 bool ReportProtobufFileReader::Dump(const SampleStatistic &message, int indent)
335 {
336     PrintIndent(indent, "%s:\n", message.GetTypeName().c_str());
337     if (message.has_count()) {
338         PrintIndent(INDENT_ONE_LEVEL, "count:%" PRIu64 "\n", message.count());
339     }
340     if (message.has_lost()) {
341         PrintIndent(INDENT_ONE_LEVEL, "lost:%" PRIu64 "\n", message.lost());
342     }
343     return false;
344 }
345 
Dump(const SymbolTableFile & message,int indent)346 bool ReportProtobufFileReader::Dump(const SymbolTableFile &message, int indent)
347 {
348     PrintIndent(indent, "%s:\n", message.GetTypeName().c_str());
349     if (message.has_id()) {
350         PrintIndent(INDENT_ONE_LEVEL, "id: %u\n", message.id());
351     }
352     if (message.has_path()) {
353         PrintIndent(INDENT_ONE_LEVEL, "path: %s\n", message.path().c_str());
354     }
355     for (int i = 0; i < message.function_name_size(); i++) {
356         PrintIndent(INDENT_TWO_LEVEL, "%d:%s\n", i, message.function_name(i).c_str());
357     }
358     return false;
359 }
Dump(const VirtualThreadInfo & message,int indent)360 bool ReportProtobufFileReader::Dump(const VirtualThreadInfo &message, int indent)
361 {
362     PrintIndent(indent, "%s:\n", message.GetTypeName().c_str());
363     if (message.has_pid()) {
364         PrintIndent(INDENT_ONE_LEVEL, "pid:%u\n", message.pid());
365     }
366     if (message.has_tid()) {
367         PrintIndent(INDENT_ONE_LEVEL, "tid:%u\n", message.tid());
368     }
369     if (message.has_pid()) {
370         PrintIndent(INDENT_ONE_LEVEL, "name:%s\n", message.name().c_str());
371     }
372     return false;
373 }
Dump(const ReportInfo & message,int indent)374 bool ReportProtobufFileReader::Dump(const ReportInfo &message, int indent)
375 {
376     PrintIndent(indent, "%s:\n", message.GetTypeName().c_str());
377     for (int i = 0; i < message.config_name_size(); i++) {
378         PrintIndent(INDENT_ONE_LEVEL, "config_name:%d:%s\n", i, message.config_name(i).c_str());
379     }
380     if (message.has_workload_cmd()) {
381         PrintIndent(INDENT_ONE_LEVEL, "workload:%s\n", message.workload_cmd().c_str());
382     }
383     return true;
384 }
Dump(const HiperfRecord & record,int indent)385 bool ReportProtobufFileReader::Dump(const HiperfRecord &record, int indent)
386 {
387     PrintIndent(indent, "%s:\n", record.GetTypeName().c_str());
388     if (record.has_sample()) {
389         return Dump(record.sample(), INDENT_ONE_LEVEL);
390     } else if (record.has_statistic()) {
391         return Dump(record.statistic(), INDENT_ONE_LEVEL);
392     } else if (record.has_file()) {
393         return Dump(record.file(), INDENT_ONE_LEVEL);
394     } else if (record.has_thread()) {
395         return Dump(record.thread(), INDENT_ONE_LEVEL);
396     } else if (record.has_info()) {
397         return Dump(record.info(), INDENT_ONE_LEVEL);
398     } else {
399         printf("unknow proto buf format\n");
400         return false;
401     }
402 }
isOpen()403 bool ReportProtobufFileReader::isOpen()
404 {
405     return protobufFileStream_->is_open();
406 }
407 } // namespace HiPerf
408 } // namespace Developtools
409 } // namespace OHOS