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