blob: a9ec2f7131b04a97572e6c7fd3132515f030bcf1 [file] [log] [blame]
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "src/tracing/service/zlib_compressor.h"
#include <random>
#include <zlib.h>
#include "protos/perfetto/trace/test_event.gen.h"
#include "protos/perfetto/trace/trace.gen.h"
#include "protos/perfetto/trace/trace_packet.gen.h"
#include "src/tracing/service/tracing_service_impl.h"
#include "test/gtest_and_gmock.h"
namespace perfetto {
namespace {
using ::testing::Each;
using ::testing::ElementsAre;
using ::testing::Field;
using ::testing::IsEmpty;
using ::testing::Le;
using ::testing::Not;
using ::testing::Property;
using ::testing::SizeIs;
template <typename F>
TracePacket CreateTracePacket(F fill_function) {
protos::gen::TracePacket msg;
fill_function(&msg);
std::vector<uint8_t> buf = msg.SerializeAsArray();
Slice slice = Slice::Allocate(buf.size());
memcpy(slice.own_data(), buf.data(), buf.size());
perfetto::TracePacket packet;
packet.AddSlice(std::move(slice));
return packet;
}
// Return a copy of the `old` trace packets that owns its own slices data.
TracePacket CopyTracePacket(const TracePacket& old) {
TracePacket ret;
for (const Slice& slice : old.slices()) {
auto new_slice = Slice::Allocate(slice.size);
memcpy(new_slice.own_data(), slice.start, slice.size);
ret.AddSlice(std::move(new_slice));
}
return ret;
}
std::vector<TracePacket> CopyTracePackets(const std::vector<TracePacket>& old) {
std::vector<TracePacket> ret;
ret.reserve(old.size());
for (const TracePacket& trace_packet : old) {
ret.push_back(CopyTracePacket(trace_packet));
}
return ret;
}
std::string RandomString(size_t size) {
std::default_random_engine rnd(0);
std::uniform_int_distribution<> dist(0, 255);
std::string s;
s.resize(size);
for (size_t i = 0; i < s.size(); i++)
s[i] = static_cast<char>(dist(rnd));
return s;
}
std::string Decompress(const std::string& data) {
uint8_t out[1024];
z_stream stream{};
stream.next_in = reinterpret_cast<uint8_t*>(const_cast<char*>(data.data()));
stream.avail_in = static_cast<unsigned int>(data.size());
EXPECT_EQ(inflateInit(&stream), Z_OK);
std::string s;
int ret;
do {
stream.next_out = out;
stream.avail_out = sizeof(out);
ret = inflate(&stream, Z_NO_FLUSH);
EXPECT_NE(ret, Z_STREAM_ERROR);
EXPECT_NE(ret, Z_NEED_DICT);
EXPECT_NE(ret, Z_DATA_ERROR);
EXPECT_NE(ret, Z_MEM_ERROR);
s.append(reinterpret_cast<char*>(out), sizeof(out) - stream.avail_out);
} while (ret != Z_STREAM_END);
inflateEnd(&stream);
return s;
}
static_assert(kZlibCompressSliceSize ==
TracingServiceImpl::kMaxTracePacketSliceSize);
TEST(ZlibCompressFnTest, Empty) {
std::vector<TracePacket> packets;
ZlibCompressFn(&packets);
EXPECT_THAT(packets, IsEmpty());
}
TEST(ZlibCompressFnTest, End2EndCompressAndDecompress) {
std::vector<TracePacket> packets;
packets.push_back(CreateTracePacket([](protos::gen::TracePacket* msg) {
auto* for_testing = msg->mutable_for_testing();
for_testing->set_str("abc");
}));
packets.push_back(CreateTracePacket([](protos::gen::TracePacket* msg) {
auto* for_testing = msg->mutable_for_testing();
for_testing->set_str("def");
}));
ZlibCompressFn(&packets);
ASSERT_THAT(packets, SizeIs(1));
protos::gen::TracePacket compressed_packet_proto;
ASSERT_TRUE(compressed_packet_proto.ParseFromString(
packets[0].GetRawBytesForTesting()));
const std::string& data = compressed_packet_proto.compressed_packets();
EXPECT_THAT(data, Not(IsEmpty()));
protos::gen::Trace subtrace;
ASSERT_TRUE(subtrace.ParseFromString(Decompress(data)));
EXPECT_THAT(
subtrace.packet(),
ElementsAre(Property(&protos::gen::TracePacket::for_testing,
Property(&protos::gen::TestEvent::str, "abc")),
Property(&protos::gen::TracePacket::for_testing,
Property(&protos::gen::TestEvent::str, "def"))));
}
TEST(ZlibCompressFnTest, MaxSliceSize) {
std::vector<TracePacket> packets;
constexpr size_t kStopOutputSize =
TracingServiceImpl::kMaxTracePacketSliceSize + 2000;
TracePacket compressed_packet;
while (compressed_packet.size() < kStopOutputSize) {
packets.push_back(CreateTracePacket([](protos::gen::TracePacket* msg) {
auto* for_testing = msg->mutable_for_testing();
for_testing->set_str(RandomString(65536));
}));
{
std::vector<TracePacket> packets_copy = CopyTracePackets(packets);
ZlibCompressFn(&packets_copy);
ASSERT_THAT(packets_copy, SizeIs(1));
compressed_packet = std::move(packets_copy[0]);
}
}
EXPECT_GE(compressed_packet.slices().size(), 2u);
ASSERT_GT(compressed_packet.size(),
TracingServiceImpl::kMaxTracePacketSliceSize);
EXPECT_THAT(compressed_packet.slices(),
Each(Field(&Slice::size,
Le(TracingServiceImpl::kMaxTracePacketSliceSize))));
}
} // namespace
} // namespace perfetto