1 /*
2 * Copyright (C) 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "src/perfetto_cmd/packet_writer.h"
18
19 #include <string.h>
20 #include <unistd.h>
21
22 #include <random>
23
24 #include <gmock/gmock.h>
25 #include <gtest/gtest.h>
26 #include <zlib.h>
27
28 #include "perfetto/base/file_utils.h"
29 #include "perfetto/base/scoped_file.h"
30 #include "perfetto/base/temp_file.h"
31 #include "perfetto/tracing/core/trace_packet.h"
32 #include "perfetto/protozero/scattered_heap_buffer.h"
33 #include "perfetto/trace/test_event.pbzero.h"
34 #include "perfetto/trace/trace.pb.h"
35 #include "perfetto/trace/trace_packet.pb.h"
36 #include "perfetto/trace/trace_packet.pbzero.h"
37 #include "src/perfetto_cmd/packet_writer.h"
38
39 namespace perfetto {
40 namespace {
41
42 using TracePacketZero = protos::pbzero::TracePacket;
43
44 template <typename F>
CreateTracePacket(F fill_function)45 TracePacket CreateTracePacket(F fill_function) {
46 protozero::HeapBuffered<TracePacketZero> msg;
47 fill_function(msg.get());
48 msg->Finalize();
49 std::vector<uint8_t> buf = msg.SerializeAsArray();
50 Slice slice = Slice::Allocate(buf.size());
51 memcpy(slice.own_data(), buf.data(), buf.size());
52 perfetto::TracePacket packet;
53 packet.AddSlice(std::move(slice));
54 return packet;
55 }
56
Decompress(const std::string & data)57 std::string Decompress(const std::string& data) {
58 uint8_t out[1024];
59
60 z_stream stream{};
61 stream.next_in = reinterpret_cast<uint8_t*>(const_cast<char*>(data.data()));
62 stream.avail_in = static_cast<unsigned int>(data.size());
63
64 EXPECT_EQ(inflateInit(&stream), Z_OK);
65 std::string s;
66
67 int ret;
68 do {
69 stream.next_out = out;
70 stream.avail_out = sizeof(out);
71 ret = inflate(&stream, Z_NO_FLUSH);
72 EXPECT_NE(ret, Z_STREAM_ERROR);
73 EXPECT_NE(ret, Z_NEED_DICT);
74 EXPECT_NE(ret, Z_DATA_ERROR);
75 EXPECT_NE(ret, Z_MEM_ERROR);
76 s.append(reinterpret_cast<char*>(out), sizeof(out) - stream.avail_out);
77 } while (ret != Z_STREAM_END);
78
79 inflateEnd(&stream);
80 return s;
81 }
82
TEST(PacketWriter,FilePacketWriter)83 TEST(PacketWriter, FilePacketWriter) {
84 base::TempFile tmp = base::TempFile::Create();
85 FILE* f = fdopen(tmp.fd(), "wb");
86
87 {
88 std::unique_ptr<PacketWriter> writer = CreateFilePacketWriter(f);
89
90 std::vector<perfetto::TracePacket> packets;
91
92 packets.push_back(CreateTracePacket([](TracePacketZero* msg) {
93 auto* for_testing = msg->set_for_testing();
94 for_testing->set_str("abc");
95 }));
96
97 EXPECT_TRUE(writer->WritePackets(std::move(packets)));
98 }
99
100 fseek(f, 0, SEEK_SET);
101 std::string s;
102 EXPECT_TRUE(base::ReadFileStream(f, &s));
103 EXPECT_GT(s.size(), 0);
104
105 protos::Trace trace;
106 EXPECT_TRUE(trace.ParseFromString(s));
107 EXPECT_EQ(trace.packet().Get(0).for_testing().str(), "abc");
108 }
109
TEST(PacketWriter,ZipPacketWriter)110 TEST(PacketWriter, ZipPacketWriter) {
111 base::TempFile tmp = base::TempFile::Create();
112 FILE* f = fdopen(tmp.fd(), "wb");
113
114 {
115 std::unique_ptr<PacketWriter> writer =
116 CreateZipPacketWriter(CreateFilePacketWriter(f));
117
118 std::vector<perfetto::TracePacket> packets;
119
120 packets.push_back(CreateTracePacket([](TracePacketZero* msg) {
121 auto* for_testing = msg->set_for_testing();
122 for_testing->set_str("abc");
123 }));
124
125 EXPECT_TRUE(writer->WritePackets(std::move(packets)));
126 }
127
128 std::string s;
129 fseek(f, 0, SEEK_SET);
130 EXPECT_TRUE(base::ReadFileStream(f, &s));
131 EXPECT_GT(s.size(), 0);
132
133 protos::Trace trace;
134 EXPECT_TRUE(trace.ParseFromString(s));
135
136 const std::string& data = trace.packet().Get(0).compressed_packets();
137 EXPECT_GT(data.size(), 0);
138
139 protos::Trace subtrace;
140 EXPECT_TRUE(subtrace.ParseFromString(Decompress(data)));
141 EXPECT_EQ(subtrace.packet().Get(0).for_testing().str(), "abc");
142 }
143
TEST(PacketWriter,ZipPacketWriter_Empty)144 TEST(PacketWriter, ZipPacketWriter_Empty) {
145 base::TempFile tmp = base::TempFile::Create();
146 FILE* f = fdopen(tmp.fd(), "wb");
147
148 {
149 std::unique_ptr<PacketWriter> writer =
150 CreateZipPacketWriter(CreateFilePacketWriter(f));
151 }
152
153 EXPECT_EQ(fseek(f, 0, SEEK_END), 0);
154 }
155
TEST(PacketWriter,ZipPacketWriter_EmptyWithEmptyWrite)156 TEST(PacketWriter, ZipPacketWriter_EmptyWithEmptyWrite) {
157 base::TempFile tmp = base::TempFile::Create();
158 FILE* f = fdopen(tmp.fd(), "wb");
159
160 {
161 std::unique_ptr<PacketWriter> writer =
162 CreateZipPacketWriter(CreateFilePacketWriter(f));
163 writer->WritePackets(std::vector<TracePacket>());
164 writer->WritePackets(std::vector<TracePacket>());
165 writer->WritePackets(std::vector<TracePacket>());
166 }
167
168 EXPECT_EQ(fseek(f, 0, SEEK_END), 0);
169 }
170
TEST(PacketWriter,ZipPacketWriter_ShouldCompress)171 TEST(PacketWriter, ZipPacketWriter_ShouldCompress) {
172 base::TempFile tmp = base::TempFile::Create();
173 FILE* f = fdopen(tmp.fd(), "wb");
174 size_t uncompressed_size = 0;
175
176 {
177 std::unique_ptr<PacketWriter> writer =
178 CreateZipPacketWriter(CreateFilePacketWriter(f));
179
180 for (size_t i = 0; i < 200; i++) {
181 std::vector<perfetto::TracePacket> packets;
182
183 packets.push_back(CreateTracePacket([](TracePacketZero* msg) {
184 auto* for_testing = msg->set_for_testing();
185 for_testing->set_str("abcdefghijklmn");
186 }));
187
188 packets.push_back(CreateTracePacket([](TracePacketZero* msg) {
189 auto* for_testing = msg->set_for_testing();
190 for_testing->set_str("abcdefghijklmn");
191 }));
192
193 for (const TracePacket& packet : packets)
194 uncompressed_size += packet.size();
195
196 EXPECT_TRUE(writer->WritePackets(std::move(packets)));
197 }
198 }
199
200 std::string s;
201 EXPECT_LT(fseek(f, 0, SEEK_END), uncompressed_size);
202 fseek(f, 0, SEEK_SET);
203 EXPECT_TRUE(base::ReadFileStream(f, &s));
204 EXPECT_GT(s.size(), 0);
205
206 protos::Trace trace;
207 EXPECT_TRUE(trace.ParseFromString(s));
208
209 size_t packet_count = 0;
210 for (const auto& packet : trace.packet()) {
211 const std::string& data = packet.compressed_packets();
212 EXPECT_GT(data.size(), 0);
213 EXPECT_LT(data.size(), 500 * 1024);
214 protos::Trace subtrace;
215 EXPECT_TRUE(subtrace.ParseFromString(Decompress(data)));
216 for (const auto& subpacket : subtrace.packet()) {
217 packet_count++;
218 EXPECT_EQ(subpacket.for_testing().str(), "abcdefghijklmn");
219 }
220 }
221
222 EXPECT_EQ(packet_count, 200 * 2);
223 }
224
TEST(PacketWriter,ZipPacketWriter_ShouldSplitPackets)225 TEST(PacketWriter, ZipPacketWriter_ShouldSplitPackets) {
226 base::TempFile tmp = base::TempFile::Create();
227 FILE* f = fdopen(tmp.fd(), "wb");
228
229 std::minstd_rand0 rnd(0);
230 std::uniform_int_distribution<> dist(0, 255);
231 auto randomString = [&dist, &rnd]() {
232 std::string s;
233 s.resize(1024);
234 for (size_t i = 0; i < s.size(); i++)
235 s[i] = static_cast<char>(dist(rnd));
236 return s;
237 };
238
239 {
240 std::unique_ptr<PacketWriter> writer =
241 CreateZipPacketWriter(CreateFilePacketWriter(f));
242
243 for (uint32_t i = 0; i < 1000; i++) {
244 std::vector<perfetto::TracePacket> packets;
245
246 std::string s = randomString();
247
248 packets.push_back(CreateTracePacket([i, &s](TracePacketZero* msg) {
249 auto* for_testing = msg->set_for_testing();
250 for_testing->set_seq_value(i);
251 for_testing->set_str(s.data(), s.size());
252 }));
253
254 EXPECT_TRUE(writer->WritePackets(std::move(packets)));
255 }
256 }
257
258 std::string s;
259 fseek(f, 0, SEEK_SET);
260 EXPECT_TRUE(base::ReadFileStream(f, &s));
261 EXPECT_GT(s.size(), 0);
262
263 protos::Trace trace;
264 EXPECT_TRUE(trace.ParseFromString(s));
265
266 size_t packet_count = 0;
267 for (const auto& packet : trace.packet()) {
268 const std::string& data = packet.compressed_packets();
269 EXPECT_GT(data.size(), 0);
270 EXPECT_LT(data.size(), 500 * 1024);
271 protos::Trace subtrace;
272 EXPECT_TRUE(subtrace.ParseFromString(Decompress(data)));
273 for (const auto& subpacket : subtrace.packet()) {
274 EXPECT_EQ(subpacket.for_testing().seq_value(), packet_count++);
275 }
276 }
277
278 EXPECT_EQ(packet_count, 1000);
279 }
280
281 } // namespace
282 } // namespace perfetto
283