1 /*
2 * Copyright (C) 2023 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/tracing/service/zlib_compressor.h"
18
19 #include <random>
20
21 #include <zlib.h>
22
23 #include "protos/perfetto/trace/test_event.gen.h"
24 #include "protos/perfetto/trace/trace.gen.h"
25 #include "protos/perfetto/trace/trace_packet.gen.h"
26 #include "src/tracing/service/tracing_service_impl.h"
27 #include "test/gtest_and_gmock.h"
28
29 namespace perfetto {
30 namespace {
31
32 using ::testing::Each;
33 using ::testing::ElementsAre;
34 using ::testing::Field;
35 using ::testing::IsEmpty;
36 using ::testing::Le;
37 using ::testing::Not;
38 using ::testing::Property;
39 using ::testing::SizeIs;
40
41 template <typename F>
CreateTracePacket(F fill_function)42 TracePacket CreateTracePacket(F fill_function) {
43 protos::gen::TracePacket msg;
44 fill_function(&msg);
45 std::vector<uint8_t> buf = msg.SerializeAsArray();
46 Slice slice = Slice::Allocate(buf.size());
47 memcpy(slice.own_data(), buf.data(), buf.size());
48 perfetto::TracePacket packet;
49 packet.AddSlice(std::move(slice));
50 return packet;
51 }
52
53 // Return a copy of the `old` trace packets that owns its own slices data.
CopyTracePacket(const TracePacket & old)54 TracePacket CopyTracePacket(const TracePacket& old) {
55 TracePacket ret;
56 for (const Slice& slice : old.slices()) {
57 auto new_slice = Slice::Allocate(slice.size);
58 memcpy(new_slice.own_data(), slice.start, slice.size);
59 ret.AddSlice(std::move(new_slice));
60 }
61 return ret;
62 }
63
CopyTracePackets(const std::vector<TracePacket> & old)64 std::vector<TracePacket> CopyTracePackets(const std::vector<TracePacket>& old) {
65 std::vector<TracePacket> ret;
66 ret.reserve(old.size());
67 for (const TracePacket& trace_packet : old) {
68 ret.push_back(CopyTracePacket(trace_packet));
69 }
70 return ret;
71 }
RandomString(size_t size)72 std::string RandomString(size_t size) {
73 std::default_random_engine rnd(0);
74 std::uniform_int_distribution<> dist(0, 255);
75 std::string s;
76 s.resize(size);
77 for (size_t i = 0; i < s.size(); i++)
78 s[i] = static_cast<char>(dist(rnd));
79 return s;
80 }
81
Decompress(const std::string & data)82 std::string Decompress(const std::string& data) {
83 uint8_t out[1024];
84
85 z_stream stream{};
86 stream.next_in = reinterpret_cast<uint8_t*>(const_cast<char*>(data.data()));
87 stream.avail_in = static_cast<unsigned int>(data.size());
88
89 EXPECT_EQ(inflateInit(&stream), Z_OK);
90 std::string s;
91
92 int ret;
93 do {
94 stream.next_out = out;
95 stream.avail_out = sizeof(out);
96 ret = inflate(&stream, Z_NO_FLUSH);
97 EXPECT_NE(ret, Z_STREAM_ERROR);
98 EXPECT_NE(ret, Z_NEED_DICT);
99 EXPECT_NE(ret, Z_DATA_ERROR);
100 EXPECT_NE(ret, Z_MEM_ERROR);
101 s.append(reinterpret_cast<char*>(out), sizeof(out) - stream.avail_out);
102 } while (ret != Z_STREAM_END);
103
104 inflateEnd(&stream);
105 return s;
106 }
107
108 static_assert(kZlibCompressSliceSize ==
109 TracingServiceImpl::kMaxTracePacketSliceSize);
110
TEST(ZlibCompressFnTest,Empty)111 TEST(ZlibCompressFnTest, Empty) {
112 std::vector<TracePacket> packets;
113
114 ZlibCompressFn(&packets);
115
116 EXPECT_THAT(packets, IsEmpty());
117 }
118
TEST(ZlibCompressFnTest,End2EndCompressAndDecompress)119 TEST(ZlibCompressFnTest, End2EndCompressAndDecompress) {
120 std::vector<TracePacket> packets;
121
122 packets.push_back(CreateTracePacket([](protos::gen::TracePacket* msg) {
123 auto* for_testing = msg->mutable_for_testing();
124 for_testing->set_str("abc");
125 }));
126 packets.push_back(CreateTracePacket([](protos::gen::TracePacket* msg) {
127 auto* for_testing = msg->mutable_for_testing();
128 for_testing->set_str("def");
129 }));
130
131 ZlibCompressFn(&packets);
132
133 ASSERT_THAT(packets, SizeIs(1));
134 protos::gen::TracePacket compressed_packet_proto;
135 ASSERT_TRUE(compressed_packet_proto.ParseFromString(
136 packets[0].GetRawBytesForTesting()));
137 const std::string& data = compressed_packet_proto.compressed_packets();
138 EXPECT_THAT(data, Not(IsEmpty()));
139 protos::gen::Trace subtrace;
140 ASSERT_TRUE(subtrace.ParseFromString(Decompress(data)));
141 EXPECT_THAT(
142 subtrace.packet(),
143 ElementsAre(Property(&protos::gen::TracePacket::for_testing,
144 Property(&protos::gen::TestEvent::str, "abc")),
145 Property(&protos::gen::TracePacket::for_testing,
146 Property(&protos::gen::TestEvent::str, "def"))));
147 }
148
TEST(ZlibCompressFnTest,MaxSliceSize)149 TEST(ZlibCompressFnTest, MaxSliceSize) {
150 std::vector<TracePacket> packets;
151
152 constexpr size_t kStopOutputSize =
153 TracingServiceImpl::kMaxTracePacketSliceSize + 2000;
154
155 TracePacket compressed_packet;
156 while (compressed_packet.size() < kStopOutputSize) {
157 packets.push_back(CreateTracePacket([](protos::gen::TracePacket* msg) {
158 auto* for_testing = msg->mutable_for_testing();
159 for_testing->set_str(RandomString(65536));
160 }));
161 {
162 std::vector<TracePacket> packets_copy = CopyTracePackets(packets);
163 ZlibCompressFn(&packets_copy);
164 ASSERT_THAT(packets_copy, SizeIs(1));
165 compressed_packet = std::move(packets_copy[0]);
166 }
167 }
168
169 EXPECT_GE(compressed_packet.slices().size(), 2u);
170 ASSERT_GT(compressed_packet.size(),
171 TracingServiceImpl::kMaxTracePacketSliceSize);
172 EXPECT_THAT(compressed_packet.slices(),
173 Each(Field(&Slice::size,
174 Le(TracingServiceImpl::kMaxTracePacketSliceSize))));
175 }
176
177 } // namespace
178 } // namespace perfetto
179