Chromium Code Reviews| Index: minidump/minidump_writable.cc |
| diff --git a/minidump/minidump_writable.cc b/minidump/minidump_writable.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..73e005dda541a1c83c620d2cebdadc07ff5dc3be |
| --- /dev/null |
| +++ b/minidump/minidump_writable.cc |
| @@ -0,0 +1,261 @@ |
| +// Copyright 2014 The Crashpad Authors. All rights reserved. |
| +// |
| +// 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 "minidump/minidump_writable.h" |
| + |
| +#include "base/logging.h" |
| +#include "util/numeric/safe_assignment.h" |
| + |
| +namespace { |
| + |
| +const size_t kMaximumAlignment = 16; |
| + |
| +} // namespace |
| + |
| +namespace crashpad { |
| +namespace internal { |
| + |
| +bool MinidumpWritable::WriteEverything(FileWriterInterface* file_writer) { |
| + DCHECK_EQ(state_, kStateMutable); |
| + |
| + if (!Freeze()) { |
| + return false; |
| + } |
| + |
| + DCHECK_EQ(state_, kStateFrozen); |
| + |
| + off_t offset = 0; |
| + std::vector<MinidumpWritable*> write_sequence; |
| + size_t size = WillWriteAtOffset(kPhaseEarly, &offset, &write_sequence); |
| + if (size == kInvalidSize) { |
| + return false; |
| + } |
| + |
| + offset += size; |
| + if (WillWriteAtOffset(kPhaseLate, &offset, &write_sequence) == kInvalidSize) { |
| + return false; |
| + } |
| + |
| + DCHECK_EQ(state_, kStateWritable); |
| + DCHECK_EQ(write_sequence.front(), this); |
| + |
| + for (MinidumpWritable* writable : write_sequence) { |
| + if (!writable->WritePaddingAndObject(file_writer)) { |
| + return false; |
| + } |
| + } |
| + |
| + DCHECK_EQ(state_, kStateWritten); |
| + |
| + return true; |
| +} |
| + |
| +void MinidumpWritable::RegisterRVA(RVA* rva) { |
| + DCHECK_LE(state_, kStateFrozen); |
| + |
| + registered_rvas_.push_back(rva); |
| +} |
| + |
| +void MinidumpWritable::RegisterLocationDescriptor( |
| + MINIDUMP_LOCATION_DESCRIPTOR* location_descriptor) { |
| + DCHECK_LE(state_, kStateFrozen); |
| + |
| + registered_location_descriptors_.push_back(location_descriptor); |
| +} |
| + |
| +const size_t MinidumpWritable::kInvalidSize = |
| + std::numeric_limits<size_t>::max(); |
| + |
| +MinidumpWritable::MinidumpWritable() |
| + : registered_rvas_(), |
| + registered_location_descriptors_(), |
| + leading_pad_bytes_(0), |
| + state_(kStateMutable) { |
| +} |
| + |
| +MinidumpWritable::~MinidumpWritable() { |
| +} |
| + |
| +bool MinidumpWritable::Freeze() { |
| + DCHECK_EQ(state_, kStateMutable); |
| + state_ = kStateFrozen; |
| + |
| + std::vector<MinidumpWritable*> children = Children(); |
| + for (MinidumpWritable* child : children) { |
| + if (!child->Freeze()) { |
| + return false; |
| + } |
| + } |
| + |
| + return true; |
| +} |
| + |
| +size_t MinidumpWritable::Alignment() { |
| + DCHECK_GE(state_, kStateFrozen); |
| + |
| + return 4; |
| +} |
| + |
| +std::vector<MinidumpWritable*> MinidumpWritable::Children() { |
| + DCHECK_GE(state_, kStateFrozen); |
| + |
| + return std::vector<MinidumpWritable*>(); |
| +} |
| + |
| +MinidumpWritable::Phase MinidumpWritable::WritePhase() { |
| + return kPhaseEarly; |
| +} |
| + |
| +size_t MinidumpWritable::WillWriteAtOffset( |
| + Phase phase, |
| + off_t* offset, |
| + std::vector<MinidumpWritable*>* write_sequence) { |
| + off_t local_offset = *offset; |
| + CHECK_GE(local_offset, 0); |
| + |
| + size_t leading_pad_bytes_this_phase; |
| + size_t size; |
| + if (phase == WritePhase()) { |
| + DCHECK_EQ(state_, kStateFrozen); |
| + |
| + // Add this object to the sequence of MinidumpWritable objects to be |
| + // written. |
| + write_sequence->push_back(this); |
| + |
| + size = SizeOfObject(); |
| + |
| + if (size > 0) { |
| + // Honor this object’s request to be aligned to a specific byte boundary. |
| + // Once the alignment is corrected, this object knows exactly what file |
| + // offset it will be written at. |
| + size_t alignment = Alignment(); |
| + CHECK_LE(alignment, kMaximumAlignment); |
| + |
| + leading_pad_bytes_this_phase = |
| + (alignment - (local_offset % alignment)) % alignment; |
| + local_offset += leading_pad_bytes_this_phase; |
| + *offset = local_offset; |
| + } else { |
| + // If the object is size 0, alignment is of no concern. |
| + leading_pad_bytes_this_phase = 0; |
| + } |
| + leading_pad_bytes_ = leading_pad_bytes_this_phase; |
| + |
| + // Now that the file offset that this object will be written at is known, |
| + // let the subclass implementation know in case it’s interested. |
| + if (!WillWriteAtOffsetImpl(local_offset)) { |
| + return kInvalidSize; |
| + } |
| + |
| + // Populate the RVA fileds in other objects that have registered to point to |
|
Robert Sesek
2014/08/01 14:38:04
nit: fields
|
| + // this one. Typically, a parent object will have registered to point to its |
| + // children, but this can also occur where no parent-child relationship |
| + // exists. |
| + if (!registered_rvas_.empty() || |
| + !registered_location_descriptors_.empty()) { |
| + RVA local_rva; |
| + if (!AssignIfInRange(&local_rva, local_offset)) { |
| + LOG(ERROR) << "offset " << local_offset << " out of range"; |
| + return kInvalidSize; |
| + } |
| + |
| + for (RVA* rva : registered_rvas_) { |
| + *rva = local_rva; |
| + } |
| + |
| + if (!registered_location_descriptors_.empty()) { |
| + typeof(registered_location_descriptors_[0]->DataSize) local_size; |
| + if (!AssignIfInRange(&local_size, size)) { |
| + LOG(ERROR) << "size " << size << " out of range"; |
| + return kInvalidSize; |
| + } |
| + |
| + for (MINIDUMP_LOCATION_DESCRIPTOR* location_descriptor : |
| + registered_location_descriptors_) { |
|
Robert Sesek
2014/08/01 14:38:04
nit: indent 4 more, or are we just letting clang-f
Mark Mentovai
2014/08/01 16:09:34
rsesek wrote:
|
| + location_descriptor->DataSize = local_size; |
| + location_descriptor->Rva = local_rva; |
| + } |
| + } |
| + } |
| + |
| + // This object is now considered writable. However, RVA fields that it |
| + // registered to have populated may not yet be populated, because this |
|
Robert Sesek
2014/08/01 14:38:04
This sentence is hard to parse.
|
| + // method may not yet have been called on the objects that this object’s RVA |
| + // fields point to. Once WillWriteAtOffset has completed running for both |
| + // phases on an entire tree, all RVA fields within that tree will be |
| + // populated. |
| + state_ = kStateWritable; |
| + } else { |
| + if (phase == kPhaseEarly) { |
| + DCHECK_EQ(state_, kStateFrozen); |
| + } else { |
| + DCHECK_EQ(state_, kStateWritable); |
| + } |
| + |
| + size = 0; |
| + leading_pad_bytes_this_phase = 0; |
| + } |
| + |
| + // Loop over children regardless of whether this object itself will write |
| + // during this phase. An object’s children are not required to be written |
| + // during the same phase as their parent. |
| + std::vector<MinidumpWritable*> children = Children(); |
| + for (MinidumpWritable* child : children) { |
| + auto unaligned_child_offset = local_offset + size; |
|
Robert Sesek
2014/08/01 14:38:03
This use of auto seems out of place IMO.
Mark Mentovai
2014/08/01 16:09:34
rsesek wrote:
|
| + off_t child_offset; |
| + if (!AssignIfInRange(&child_offset, unaligned_child_offset)) { |
| + LOG(ERROR) << "offset " << unaligned_child_offset << " out of range"; |
| + return kInvalidSize; |
| + } |
| + |
| + size_t child_size = |
| + child->WillWriteAtOffset(phase, &child_offset, write_sequence); |
| + if (child_size == kInvalidSize) { |
| + return kInvalidSize; |
| + } |
| + |
| + size += child_size; |
| + } |
| + |
| + return leading_pad_bytes_this_phase + size; |
| +} |
| + |
| +bool MinidumpWritable::WillWriteAtOffsetImpl(off_t offset) { |
| + return true; |
| +} |
| + |
| +bool MinidumpWritable::WritePaddingAndObject(FileWriterInterface* file_writer) { |
| + DCHECK_EQ(state_, kStateWritable); |
| + |
| + // The number of elements in kZeroes must be at least one less than the |
| + // maximum Alignment() ever encountered. |
| + const uint8_t kZeroes[kMaximumAlignment - 1] = {}; |
| + DCHECK_LE(leading_pad_bytes_, arraysize(kZeroes)); |
| + |
| + if (leading_pad_bytes_) { |
| + if (!file_writer->Write(&kZeroes, leading_pad_bytes_)) { |
| + return false; |
| + } |
| + } |
| + |
| + if (!WriteObject(file_writer)) { |
| + return false; |
| + } |
| + |
| + state_ = kStateWritten; |
| + return true; |
| +} |
| + |
| +} // namespace internal |
| +} // namespace crashpad |