| Index: chrome/profiling/memlog_stream_parser.cc
|
| diff --git a/chrome/profiling/memlog_stream_parser.cc b/chrome/profiling/memlog_stream_parser.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..b67d0d7f151879f82042773793c07e19ce377bca
|
| --- /dev/null
|
| +++ b/chrome/profiling/memlog_stream_parser.cc
|
| @@ -0,0 +1,163 @@
|
| +// 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"
|
| +#include "chrome/profiling/profiling_globals.h"
|
| +
|
| +namespace profiling {
|
| +
|
| +namespace {
|
| +
|
| +using AddressVector = base::StackVector<Address, 128>;
|
| +
|
| +} // namespace
|
| +
|
| +MemlogStreamParser::Block::Block(std::unique_ptr<char[]> d, size_t s)
|
| + : data(std::move(d)), size(s) {}
|
| +
|
| +MemlogStreamParser::MemlogStreamParser(MemlogReceiver* receiver)
|
| + : receiver_(receiver) {}
|
| +
|
| +MemlogStreamParser::~MemlogStreamParser() {}
|
| +
|
| +void MemlogStreamParser::OnStreamData(std::unique_ptr<char[]> data, size_t sz) {
|
| + blocks_.emplace_back(std::move(data), sz);
|
| +
|
| + if (!received_header_) {
|
| + received_header_ = true;
|
| + ReadStatus status = ParseHeader();
|
| + if (status != READ_OK)
|
| + return; // TODO(brettw) signal error.
|
| + }
|
| +
|
| + while (true) {
|
| + uint32_t msg_type;
|
| + if (!PeekBytes(sizeof(msg_type), &msg_type))
|
| + return;
|
| +
|
| + ReadStatus status;
|
| + switch (msg_type) {
|
| + case kAllocPacketType:
|
| + status = ParseAlloc();
|
| + break;
|
| + case kFreePacketType:
|
| + status = ParseFree();
|
| + break;
|
| + default:
|
| + return; // TODO(brettw) signal error.
|
| + }
|
| + if (status != READ_OK)
|
| + return; // TODO(brettw) signal error.
|
| + }
|
| +}
|
| +
|
| +void MemlogStreamParser::OnStreamComplete() {
|
| + 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;
|
| + 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;
|
| +}
|
| +
|
| +} // namespace profiling
|
|
|