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