blob: e796a3ac94ceb371e641d39ff847cfff9e31ac8c [file] [log] [blame]
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/profiling/memlog_stream_parser.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace profiling {
namespace {
void SendData(const scoped_refptr<MemlogStreamParser>& parser,
const void* data,
size_t size) {
std::unique_ptr<char[]> heap(new char[size]);
memcpy(heap.get(), data, size);
parser->OnStreamData(std::move(heap), size);
}
void SendHeader(scoped_refptr<MemlogStreamParser>& parser) {
StreamHeader header;
SendData(parser, &header, sizeof(StreamHeader));
}
class TestReceiver : public MemlogReceiver {
public:
TestReceiver() {
// Make our saved header invalid so we can't confuse the locally
// initialized one with a valid one that's received.
header_.signature = 0;
last_barrier_.barrier_id =
std::numeric_limits<decltype(last_barrier_.barrier_id)>::max();
}
bool got_header() const { return got_header_; }
const StreamHeader& header() const { return header_; }
void OnHeader(const StreamHeader& header) override {
ASSERT_FALSE(got_header_); // Don't expect more than one.
got_header_ = true;
header_ = header;
}
int alloc_count() const { return alloc_count_; }
const AllocPacket& last_alloc() const { return last_alloc_; }
const std::vector<Address>& last_alloc_stack() const {
return last_alloc_stack_;
}
const std::string& last_alloc_context() const { return last_alloc_context_; }
void OnAlloc(const AllocPacket& alloc_packet,
std::vector<Address>&& stack,
std::string&& context) override {
alloc_count_++;
last_alloc_ = alloc_packet;
last_alloc_stack_ = std::move(stack);
last_alloc_context_ = std::move(context);
}
int free_count() const { return free_count_; }
const FreePacket& last_free() const { return last_free_; }
void OnFree(const FreePacket& free_packet) override {
free_count_++;
last_free_ = free_packet;
}
int barrier_count() const { return barrier_count_; }
const BarrierPacket& last_barrier() const { return last_barrier_; }
void OnBarrier(const BarrierPacket& barrier) override {
barrier_count_++;
last_barrier_.barrier_id = barrier.barrier_id;
}
bool got_complete() const { return got_complete_; }
void OnComplete() override {
ASSERT_FALSE(got_complete_); // Don't expect more than one.
got_complete_ = true;
}
private:
bool got_header_ = false;
StreamHeader header_;
int alloc_count_ = 0;
AllocPacket last_alloc_;
std::vector<Address> last_alloc_stack_;
std::string last_alloc_context_;
int free_count_ = 0;
FreePacket last_free_;
int barrier_count_ = 0;
BarrierPacket last_barrier_;
bool got_complete_ = false;
};
} // namespace
TEST(MemlogStreamParser, NormalHeader) {
TestReceiver receiver;
scoped_refptr<MemlogStreamParser> parser(new MemlogStreamParser(&receiver));
// Should work to send in two packets.
StreamHeader header;
size_t first_size = sizeof(StreamHeader) / 2;
SendData(parser, &header, first_size);
EXPECT_FALSE(receiver.got_header());
SendData(parser, &reinterpret_cast<char*>(&header)[first_size],
sizeof(StreamHeader) - first_size);
EXPECT_TRUE(receiver.got_header());
EXPECT_FALSE(receiver.got_complete());
parser->OnStreamComplete();
EXPECT_TRUE(receiver.got_complete());
}
TEST(MemlogStreamParser, BadHeader) {
TestReceiver receiver;
scoped_refptr<MemlogStreamParser> parser(new MemlogStreamParser(&receiver));
StreamHeader header;
header.signature = 15;
SendData(parser, &header, sizeof(StreamHeader));
EXPECT_FALSE(receiver.got_header());
EXPECT_TRUE(receiver.got_complete());
EXPECT_TRUE(parser->has_error());
}
TEST(MemlogStreamParser, GoodAlloc) {
TestReceiver receiver;
scoped_refptr<MemlogStreamParser> parser(new MemlogStreamParser(&receiver));
SendHeader(parser);
constexpr size_t kStackSize = 4;
uint64_t stack[kStackSize] = {0x1, 0x2, 0x3, 0x4};
std::string context("hello");
AllocPacket alloc;
alloc.allocator = AllocatorType::kMalloc;
alloc.address = 0x87654321;
alloc.size = 128;
alloc.stack_len = kStackSize;
alloc.context_byte_len = static_cast<uint32_t>(context.size());
SendData(parser, &alloc, sizeof(AllocPacket));
EXPECT_EQ(0, receiver.alloc_count());
SendData(parser, stack, sizeof(uint64_t) * kStackSize);
ASSERT_EQ(0, receiver.alloc_count());
SendData(parser, context.data(), context.size());
ASSERT_EQ(1, receiver.alloc_count());
EXPECT_EQ(alloc.allocator, receiver.last_alloc().allocator);
EXPECT_EQ(alloc.address, receiver.last_alloc().address);
EXPECT_EQ(alloc.size, receiver.last_alloc().size);
EXPECT_EQ(alloc.stack_len, receiver.last_alloc().stack_len);
EXPECT_EQ(context, receiver.last_alloc_context());
ASSERT_EQ(4u, receiver.last_alloc_stack().size());
EXPECT_EQ(stack[0], receiver.last_alloc_stack()[0].value);
EXPECT_EQ(stack[1], receiver.last_alloc_stack()[1].value);
EXPECT_EQ(stack[2], receiver.last_alloc_stack()[2].value);
EXPECT_EQ(stack[3], receiver.last_alloc_stack()[3].value);
}
TEST(MemlogStreamParser, AllocBigStack) {
TestReceiver receiver;
scoped_refptr<MemlogStreamParser> parser(new MemlogStreamParser(&receiver));
SendHeader(parser);
AllocPacket alloc;
alloc.allocator = AllocatorType::kMalloc;
alloc.address = 0x87654321;
alloc.size = 128;
alloc.stack_len = 10000; // Too large a stack.
alloc.context_byte_len = 0;
SendData(parser, &alloc, sizeof(AllocPacket));
// Even though no stack was sent, the alloc header with the too-large stack
// should have triggered an error.
EXPECT_EQ(0, receiver.alloc_count());
EXPECT_TRUE(receiver.got_complete());
}
TEST(MemlogStreamParser, AllocBigContext) {
TestReceiver receiver;
scoped_refptr<MemlogStreamParser> parser(new MemlogStreamParser(&receiver));
SendHeader(parser);
AllocPacket alloc;
alloc.allocator = AllocatorType::kMalloc;
alloc.address = 0x87654321;
alloc.size = 128;
alloc.stack_len = 0; // Too large a stack.
alloc.context_byte_len = 10000;
SendData(parser, &alloc, sizeof(AllocPacket));
// Even though no stack or context was sent, the alloc header with the
// too-large stack should have triggered an error.
EXPECT_EQ(0, receiver.alloc_count());
EXPECT_TRUE(receiver.got_complete());
}
TEST(MemlogStreamParser, GoodFree) {
TestReceiver receiver;
scoped_refptr<MemlogStreamParser> parser(new MemlogStreamParser(&receiver));
SendHeader(parser);
FreePacket fr;
fr.address = 0x87654321;
SendData(parser, &fr, sizeof(FreePacket));
EXPECT_EQ(1, receiver.free_count());
EXPECT_EQ(fr.address, receiver.last_free().address);
}
TEST(MemlogStreamParser, Barrier) {
TestReceiver receiver;
scoped_refptr<MemlogStreamParser> parser(new MemlogStreamParser(&receiver));
SendHeader(parser);
constexpr uint32_t barrier_id = 0x12345678;
BarrierPacket b;
b.barrier_id = barrier_id;
SendData(parser, &b, sizeof(BarrierPacket));
EXPECT_EQ(1, receiver.barrier_count());
EXPECT_EQ(barrier_id, receiver.last_barrier().barrier_id);
}
} // namespace profiling