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