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