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