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