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
16 #include <iostream>
17 #include <string>
18
19 #include "report_protobuf_file.h"
20 #include "report_protobuf_file_test.h"
21
22 using namespace Proto;
23 using namespace testing::ext;
24 using namespace std;
25 using namespace OHOS::HiviewDFX;
26 using namespace ::testing;
27 namespace OHOS {
28 namespace Developtools {
29 namespace HiPerf {
30 class ReportProtobufFileTest : public testing::Test {
31 public:
32 static void SetUpTestCase(void);
33 static void TearDownTestCase(void);
34 void SetUp();
35 void TearDown();
36 std::unique_ptr<ReportProtobufFileWriter> protobufOutputFileWriter_ = nullptr;
37 std::unique_ptr<ReportProtobufFileReader> protobufInputFileReader_ = nullptr;
38 std::vector<std::unique_ptr<SymbolsFile>> symbolsFiles_;
39 void PrepareSymbolsFile();
40 };
PrepareSymbolsFile()41 void ReportProtobufFileTest::PrepareSymbolsFile()
42 {
43 symbolsFiles_.clear();
44
45 std::string userSymbol = "user_symbol";
46 std::unique_ptr<SymbolsFile> user = SymbolsFile::CreateSymbolsFile(SYMBOL_ELF_FILE);
47 user->symbols_.emplace_back(0x1, 1u, "first_user_func", user->filePath_);
48 user->symbols_.emplace_back(0x2, 1u, "second_user_func", user->filePath_);
49 user->filePath_ = userSymbol;
50 symbolsFiles_.emplace_back(std::move(user));
51
52 std::string userSymbol2 = "user_symbol2";
53 std::unique_ptr<SymbolsFile> user2 = SymbolsFile::CreateSymbolsFile(SYMBOL_ELF_FILE);
54 user2->symbols_.emplace_back(0x1, 1u, "first_user2_func", user2->filePath_);
55 user2->symbols_.emplace_back(0x2, 1u, "second_user2_func", user2->filePath_);
56 user2->symbols_.emplace_back(0x3, 1u, "third_user2_func", user2->filePath_);
57 user2->filePath_ = userSymbol2;
58 symbolsFiles_.emplace_back(std::move(user2));
59 }
60
SetUpTestCase()61 void ReportProtobufFileTest::SetUpTestCase() {}
62
TearDownTestCase()63 void ReportProtobufFileTest::TearDownTestCase() {}
64
SetUp()65 void ReportProtobufFileTest::SetUp()
66 {
67 protobufOutputFileWriter_ = std::make_unique<ReportProtobufFileWriter>();
68 protobufInputFileReader_ = std::make_unique<ReportProtobufFileReader>();
69 PrepareSymbolsFile();
70 }
TearDown()71 void ReportProtobufFileTest::TearDown() {}
72
73 /**
74 * @tc.name: Create
75 * @tc.desc:
76 * @tc.type: FUNC
77 */
78 HWTEST_F(ReportProtobufFileTest, Create, TestSize.Level1)
79 {
80 std::string fileName = "perf.proto";
81 ASSERT_EQ(protobufOutputFileWriter_->Create(fileName), true);
82 EXPECT_EQ(access(fileName.c_str(), F_OK), 0);
83
84 std::string errorFileName = "!@#$%^";
85 EXPECT_EQ(protobufOutputFileWriter_->Create(fileName), false);
86 EXPECT_EQ(access(errorFileName.c_str(), F_OK), -1);
87 }
88
89 /**
90 * @tc.name: Close
91 * @tc.desc:
92 * @tc.type: FUNC
93 */
94 HWTEST_F(ReportProtobufFileTest, Close, TestSize.Level1)
95 {
96 std::string fileName = "perf.proto";
97 EXPECT_EQ(protobufOutputFileWriter_->isOpen(), false);
98 ASSERT_EQ(protobufOutputFileWriter_->Create(fileName), true);
99 EXPECT_EQ(protobufOutputFileWriter_->isOpen(), true);
100 protobufOutputFileWriter_->Close();
101 EXPECT_EQ(protobufOutputFileWriter_->isOpen(), false);
102 }
103
104 /**
105 * @tc.name: ProcessRecord
106 * @tc.desc:
107 * @tc.type: FUNC
108 */
109 HWTEST_F(ReportProtobufFileTest, ProcessRecord, TestSize.Level1)
110 {
111 std::string fileName = "perf.proto";
112 class ReportProtobufFileWriterMock : public ReportProtobufFileWriter {
113 public:
114 MOCK_METHOD1(ProcessRecord, bool(const PerfRecordComm &));
115 MOCK_METHOD1(ProcessRecord, bool(const PerfRecordLost &));
116 } protobufOutputFileWriter;
117
118 const PerfRecordComm comm(false, 2, 3, "dummy");
119 const PerfRecordLost lost(false, 1, 99);
120
121 EXPECT_CALL(protobufOutputFileWriter, ProcessRecord(Matcher<const PerfRecordComm &>(_)))
122 .Times(1);
123 protobufOutputFileWriter.ProcessRecord(comm);
124
125 EXPECT_CALL(protobufOutputFileWriter, ProcessRecord(Matcher<const PerfRecordLost &>(_)))
126 .Times(2);
127 protobufOutputFileWriter.ProcessRecord(lost);
128 protobufOutputFileWriter.ProcessRecord(lost);
129 }
130
131 /**
132 * @tc.name: ProcessRecordPerfRecordLost
133 * @tc.desc:
134 * @tc.type: FUNC
135 */
136 HWTEST_F(ReportProtobufFileTest, ProcessRecordPerfRecordLost, TestSize.Level1)
137 {
138 const PerfRecordLost lost(false, 1, 99);
139
140 protobufOutputFileWriter_->ProcessRecord(lost);
141 protobufOutputFileWriter_->ProcessRecord(lost);
142 EXPECT_EQ(protobufOutputFileWriter_->recordLost_, lost.data_.lost * 2);
143 }
144
145 /**
146 * @tc.name: ProcessRecordPerfRecordComm
147 * @tc.desc:
148 * @tc.type: FUNC
149 */
150 HWTEST_F(ReportProtobufFileTest, ProcessRecordPerfRecordComm, TestSize.Level1)
151 {
152 int expectRecord = 0;
153 std::string fileName = "perf.proto";
154 const PerfRecordComm comm(false, 2, 3, "dummy");
155 ASSERT_EQ(protobufOutputFileWriter_->Create(fileName), true);
156
157 protobufOutputFileWriter_->ProcessRecord(comm);
158
159 protobufOutputFileWriter_->Close();
160
__anoncfc228cc0102(Proto::HiperfRecord &record) 161 auto protobufReadBack = [&](Proto::HiperfRecord &record) {
162 printf("record name %s %d\n", record.GetTypeName().c_str(), record.RecordType_case());
163 // the SampleStatistic always write in close.
164 // so there at least 2 record ,one is expect , one is statiistic
165 if (record.has_thread()) {
166 const VirtualThreadInfo &message = record.thread();
167 ASSERT_EQ(message.tid(), comm.data_.tid);
168 ASSERT_EQ(message.pid(), comm.data_.pid);
169 ASSERT_STREQ(message.name().c_str(), comm.data_.comm);
170 expectRecord++;
171 }
172 };
173 protobufInputFileReader_->Dump(fileName, protobufReadBack);
174
175 EXPECT_EQ(expectRecord, 1);
176 }
177
178 /**
179 * @tc.name: BeforeClose
180 * @tc.desc:
181 * @tc.type: FUNC
182 */
183 HWTEST_F(ReportProtobufFileTest, BeforeClose, TestSize.Level1)
184 {
185 int expectRecord = 0;
186 std::string fileName = "perf.proto";
187 ASSERT_EQ(protobufOutputFileWriter_->Create(fileName), true);
188 protobufOutputFileWriter_->recordLost_ = 10;
189 protobufOutputFileWriter_->recordCount_ = 20;
190 protobufOutputFileWriter_->Close();
191
__anoncfc228cc0202(Proto::HiperfRecord &record) 192 auto protobufReadBack = [&](Proto::HiperfRecord &record) {
193 printf("record name %s %d\n", record.GetTypeName().c_str(), record.RecordType_case());
194 // the SampleStatistic always write in close.
195 // so there at least 2 record ,one is expect , one is statiistic
196 if (record.has_statistic()) {
197 const SampleStatistic &message = record.statistic();
198 ASSERT_EQ(message.count(), protobufOutputFileWriter_->recordCount_);
199 ASSERT_EQ(message.lost(), protobufOutputFileWriter_->recordLost_);
200 expectRecord++;
201 }
202 };
203
204 protobufInputFileReader_->Dump(fileName, protobufReadBack);
205
206 EXPECT_EQ(expectRecord, 1);
207 }
208
209 /**
210 * @tc.name: ProcessSymbolsFiles
211 * @tc.desc:
212 * @tc.type: FUNC
213 */
214 HWTEST_F(ReportProtobufFileTest, ProcessSymbolsFiles, TestSize.Level1)
215 {
216 unsigned int expectRecord = 0;
217 std::string fileName = "perf.proto";
218 ASSERT_EQ(protobufOutputFileWriter_->Create(fileName), true);
219
220 protobufOutputFileWriter_->ProcessSymbolsFiles(symbolsFiles_);
221 protobufOutputFileWriter_->Close();
222
__anoncfc228cc0302(Proto::HiperfRecord &record) 223 auto protobufReadBack = [&](Proto::HiperfRecord &record) {
224 printf("record name %s %d\n", record.GetTypeName().c_str(), record.RecordType_case());
225 // the SampleStatistic always write in close.
226 // so there at least 2 record ,one is expect , one is statiistic
227 if (record.has_file()) {
228 expectRecord++;
229 const SymbolTableFile &message = record.file();
230 ASSERT_EQ(message.id() >= 0, true);
231 ASSERT_EQ(message.id() < symbolsFiles_.size(), true);
232 const std::unique_ptr<SymbolsFile> &symbolFile = symbolsFiles_.at(message.id());
233 ASSERT_STREQ(message.path().c_str(), symbolFile->filePath_.c_str());
234 ASSERT_EQ(static_cast<size_t>(message.function_name_size()),
235 symbolFile->GetSymbols().size());
236 for (int i = 0; i < message.function_name_size(); i++) {
237 EXPECT_STREQ(message.function_name(i).c_str(),
238 symbolFile->GetSymbols().at(i).name_.data());
239 }
240 }
241 };
242
243 protobufInputFileReader_->Dump(fileName, protobufReadBack);
244
245 EXPECT_EQ(expectRecord, symbolsFiles_.size());
246 }
247
248 /**
249 * @tc.name: ProcessReportInfo
250 * @tc.desc:
251 * @tc.type: FUNC
252 */
253 HWTEST_F(ReportProtobufFileTest, ProcessReportInfo, TestSize.Level1)
254 {
255 int expectRecord = 0;
256 std::string fileName = "perf.proto";
257 std::vector<std::string> configNames = {"config1", "config2", "config3"};
258 std::string workloadCmd = "workcommand";
259
260 ASSERT_EQ(protobufOutputFileWriter_->Create(fileName), true);
261 protobufOutputFileWriter_->ProcessReportInfo(configNames, workloadCmd);
262 protobufOutputFileWriter_->Close();
263
__anoncfc228cc0402(Proto::HiperfRecord &record) 264 auto protobufReadBack = [&](Proto::HiperfRecord &record) {
265 printf("record name %s %d\n", record.GetTypeName().c_str(), record.RecordType_case());
266 // the SampleStatistic always write in close.
267 // so there at least 2 record ,one is expect , one is statiistic
268 if (record.has_info()) {
269 const ReportInfo &message = record.info();
270 ASSERT_EQ(message.config_name_size() > 0, true);
271 ASSERT_EQ(static_cast<size_t>(message.config_name_size()), configNames.size());
272 for (int i = 0; i < message.config_name_size(); i++) {
273 EXPECT_STREQ(message.config_name(i).c_str(), configNames.at(i).c_str());
274 }
275 if (message.has_workload_cmd()) {
276 EXPECT_STREQ(message.workload_cmd().c_str(), workloadCmd.c_str());
277 }
278 expectRecord++;
279 }
280 };
281 protobufInputFileReader_->Dump(fileName, protobufReadBack);
282
283 EXPECT_EQ(expectRecord, 1);
284 }
285
286 /**
287 * @tc.name: ProcessSampleRecord
288 * @tc.desc:
289 * @tc.type: FUNC
290 */
291 HWTEST_F(ReportProtobufFileTest, ProcessSampleRecord, TestSize.Level1)
292 {
293 int expectRecord = 0;
294 std::string fileName = "perf.proto";
295 PerfRecordSample sample(false, 1, 2, 100, 200u);
296 sample.callFrames_.emplace_back(0x1, 0x1234, "first_user_func", "user_symbol");
297 sample.callFrames_.emplace_back(0x2, 0x1234, "first_user2_func", "user_symbol2");
298 sample.callFrames_.emplace_back(0x3, 0x1234, "second_user2_func", "user_symbol2");
299
300 sample.callFrames_.at(0).symbolFileIndex_ = 0;
301 sample.callFrames_.at(1).symbolFileIndex_ = 1;
302 sample.callFrames_.at(2).symbolFileIndex_ = 1;
303 sample.callFrames_.at(0).symbolIndex_ = 0;
304 sample.callFrames_.at(1).symbolIndex_ = 0;
305 sample.callFrames_.at(2).symbolIndex_ = 1;
306
307 ASSERT_EQ(protobufOutputFileWriter_->Create(fileName), true);
308 protobufOutputFileWriter_->ProcessSampleRecord(sample, 0u, symbolsFiles_);
309
310 protobufOutputFileWriter_->Close();
311
__anoncfc228cc0502(Proto::HiperfRecord &record) 312 auto protobufReadBack = [&](Proto::HiperfRecord &record) {
313 printf("record name %s %d\n", record.GetTypeName().c_str(), record.RecordType_case());
314 // the SampleStatistic always write in close.
315 // so there at least 2 record ,one is expect , one is statiistic
316 if (record.has_sample()) {
317 const CallStackSample &message = record.sample();
318 ASSERT_EQ(message.has_time(), true);
319 ASSERT_EQ(message.time(), 200u);
320
321 ASSERT_EQ(message.has_tid(), true);
322 ASSERT_EQ(message.tid(), 2u);
323
324 ASSERT_EQ(message.callstackframe_size() > 0, true);
325 ASSERT_EQ(static_cast<size_t>(message.callstackframe_size()),
326 sample.callFrames_.size());
327
328 for (int i = 0; i < message.callstackframe_size(); i++) {
329 auto &callframe = message.callstackframe(i);
330 ASSERT_EQ(callframe.has_symbols_vaddr(), true);
331 ASSERT_EQ(callframe.symbols_vaddr(), sample.callFrames_.at(i).vaddrInFile_);
332
333 ASSERT_EQ(callframe.has_symbols_file_id(), true);
334 printf("symbols file id %d\n", callframe.symbols_file_id());
335 ASSERT_EQ(callframe.symbols_file_id() < symbolsFiles_.size(), true);
336 const std::unique_ptr<SymbolsFile> &symbolsFile =
337 symbolsFiles_.at(callframe.symbols_file_id());
338
339 ASSERT_EQ(callframe.has_function_name_id(), true);
340 printf("function id %d\n", callframe.function_name_id());
341 ASSERT_EQ(callframe.function_name_id() >= 0, true);
342 int funcNameId = callframe.function_name_id();
343 ASSERT_EQ(static_cast<size_t>(funcNameId) < symbolsFile->GetSymbols().size(), true);
344 ASSERT_STREQ(sample.callFrames_.at(i).symbolName_.data(),
345 symbolsFile->GetSymbols().at(funcNameId).name_.data());
346 }
347 ASSERT_EQ(message.has_event_count(), true);
348 ASSERT_EQ(message.event_count(), 100u);
349
350 ASSERT_EQ(message.has_config_name_id(), true);
351 ASSERT_EQ(message.config_name_id(), 0u);
352 expectRecord++;
353 }
354 };
355 protobufInputFileReader_->Dump(fileName, protobufReadBack);
356
357 EXPECT_EQ(expectRecord, 1);
358 }
359 } // namespace HiPerf
360 } // namespace Developtools
361 } // namespace OHOS
362