blob: 6186444bd3628c7b89cc4e9f94813c6becc2ce23 [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 <algorithm>
#include "base/containers/stack_container.h"
#include "base/strings/stringprintf.h"
#include "chrome/common/profiling/memlog_stream.h"
#include "chrome/profiling/address.h"
#include "chrome/profiling/backtrace.h"
namespace profiling {
namespace {
using AddressVector = base::StackVector<Address, kMaxStackEntries>;
} // namespace
MemlogStreamParser::Block::Block(std::unique_ptr<char[]> d, size_t s)
: data(std::move(d)), size(s) {}
MemlogStreamParser::Block::~Block() {}
MemlogStreamParser::MemlogStreamParser(MemlogReceiver* receiver)
: receiver_(receiver) {}
MemlogStreamParser::~MemlogStreamParser() {}
void MemlogStreamParser::DisconnectReceivers() {
base::AutoLock lock(lock_);
receiver_ = nullptr;
}
bool MemlogStreamParser::OnStreamData(std::unique_ptr<char[]> data, size_t sz) {
base::AutoLock l(lock_);
if (!receiver_ || error_)
return false;
blocks_.emplace_back(std::move(data), sz);
if (!received_header_) {
ReadStatus status = ParseHeader();
if (status == READ_NO_DATA)
return true; // Wait for more data.
if (status == READ_ERROR) {
SetErrorState();
return false;
}
received_header_ = true;
}
while (true) {
uint32_t msg_type;
if (!PeekBytes(sizeof(msg_type), &msg_type))
return true; // Not enough data for a message type field.
ReadStatus status;
switch (msg_type) {
case kAllocPacketType:
status = ParseAlloc();
break;
case kFreePacketType:
status = ParseFree();
break;
default:
// Invalid message type.
status = READ_ERROR;
break;
}
if (status == READ_NO_DATA)
return true; // Wait for more data.
if (status == READ_ERROR) {
SetErrorState();
return false;
}
// Success, loop around for more data.
}
}
void MemlogStreamParser::OnStreamComplete() {
base::AutoLock l(lock_);
if (receiver_)
receiver_->OnComplete();
}
bool MemlogStreamParser::AreBytesAvailable(size_t count) const {
size_t used = 0;
size_t current_block_offset = block_zero_offset_;
for (auto it = blocks_.begin(); it != blocks_.end() && used < count; ++it) {
used += it->size - current_block_offset;
current_block_offset = 0;
}
return used >= count;
}
bool MemlogStreamParser::PeekBytes(size_t count, void* dest) const {
char* dest_char = static_cast<char*>(dest);
size_t used = 0;
size_t current_block_offset = block_zero_offset_;
for (const auto& block : blocks_) {
size_t in_current_block = block.size - current_block_offset;
size_t to_copy = std::min(count - used, in_current_block);
memcpy(&dest_char[used], &block.data[current_block_offset], to_copy);
used += to_copy;
// All subsequent blocks start reading at offset 0.
current_block_offset = 0;
}
return used == count;
}
bool MemlogStreamParser::ReadBytes(size_t count, void* dest) {
if (!PeekBytes(count, dest))
return false;
ConsumeBytes(count);
return true;
}
void MemlogStreamParser::ConsumeBytes(size_t count) {
DCHECK(AreBytesAvailable(count));
while (count > 0) {
size_t bytes_left_in_block = blocks_.front().size - block_zero_offset_;
if (bytes_left_in_block > count) {
// Still data left in this block;
block_zero_offset_ += count;
return;
}
// Current block is consumed.
blocks_.pop_front();
block_zero_offset_ = 0;
count -= bytes_left_in_block;
}
}
MemlogStreamParser::ReadStatus MemlogStreamParser::ParseHeader() {
StreamHeader header;
if (!ReadBytes(sizeof(StreamHeader), &header))
return READ_NO_DATA;
if (header.signature != kStreamSignature)
return READ_ERROR;
receiver_->OnHeader(header);
return READ_OK;
}
MemlogStreamParser::ReadStatus MemlogStreamParser::ParseAlloc() {
// Read the packet. Can't commit the read until the stack is read and
// that has to be done below.
AllocPacket alloc_packet;
if (!PeekBytes(sizeof(AllocPacket), &alloc_packet))
return READ_NO_DATA;
std::vector<Address> stack;
if (alloc_packet.stack_len > kMaxStackEntries)
return READ_ERROR; // Prevent overflow on corrupted or malicious data.
stack.resize(alloc_packet.stack_len);
size_t stack_byte_size = sizeof(Address) * alloc_packet.stack_len;
if (!AreBytesAvailable(sizeof(AllocPacket) + stack_byte_size))
return READ_NO_DATA;
// Everything will fit, mark packet consumed, read stack.
ConsumeBytes(sizeof(AllocPacket));
if (!stack.empty())
ReadBytes(stack_byte_size, stack.data());
receiver_->OnAlloc(alloc_packet, std::move(stack));
return READ_OK;
}
MemlogStreamParser::ReadStatus MemlogStreamParser::ParseFree() {
FreePacket free_packet;
if (!ReadBytes(sizeof(FreePacket), &free_packet))
return READ_NO_DATA;
receiver_->OnFree(free_packet);
return READ_OK;
}
void MemlogStreamParser::SetErrorState() {
error_ = true;
receiver_->OnComplete();
}
} // namespace profiling