Add MinidumpSimpleStringDictionaryWriter and its test.

TEST=minidump_test MinidumpSimpleStringDictionaryWriter.*
R=rsesek@chromium.org

Review URL: https://codereview.chromium.org/656173003
This commit is contained in:
Mark Mentovai 2014-10-16 18:09:18 -04:00
parent 6c1a46f2bb
commit c9db1b1d19
6 changed files with 523 additions and 2 deletions

View File

@ -44,6 +44,8 @@
'minidump_misc_info_writer.h', 'minidump_misc_info_writer.h',
'minidump_module_writer.cc', 'minidump_module_writer.cc',
'minidump_module_writer.h', 'minidump_module_writer.h',
'minidump_simple_string_dictionary_writer.cc',
'minidump_simple_string_dictionary_writer.h',
'minidump_stream_writer.cc', 'minidump_stream_writer.cc',
'minidump_stream_writer.h', 'minidump_stream_writer.h',
'minidump_string_writer.cc', 'minidump_string_writer.cc',
@ -83,6 +85,7 @@
'minidump_memory_writer_test_util.h', 'minidump_memory_writer_test_util.h',
'minidump_misc_info_writer_test.cc', 'minidump_misc_info_writer_test.cc',
'minidump_module_writer_test.cc', 'minidump_module_writer_test.cc',
'minidump_simple_string_dictionary_writer_test.cc',
'minidump_string_writer_test.cc', 'minidump_string_writer_test.cc',
'minidump_system_info_writer_test.cc', 'minidump_system_info_writer_test.cc',
'minidump_thread_writer_test.cc', 'minidump_thread_writer_test.cc',

View File

@ -0,0 +1,158 @@
// 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_simple_string_dictionary_writer.h"
#include "base/logging.h"
#include "util/numeric/safe_assignment.h"
namespace crashpad {
MinidumpSimpleStringDictionaryEntryWriter::
MinidumpSimpleStringDictionaryEntryWriter()
: MinidumpWritable(), entry_(), key_(), value_() {
}
MinidumpSimpleStringDictionaryEntryWriter::
~MinidumpSimpleStringDictionaryEntryWriter() {
}
const MinidumpSimpleStringDictionaryEntry*
MinidumpSimpleStringDictionaryEntryWriter::MinidumpSimpleStringDictionaryEntry()
const {
DCHECK_EQ(state(), kStateWritable);
return &entry_;
}
void MinidumpSimpleStringDictionaryEntryWriter::SetKeyValue(
const std::string& key,
const std::string& value) {
key_.SetUTF8(key);
value_.SetUTF8(value);
}
bool MinidumpSimpleStringDictionaryEntryWriter::Freeze() {
DCHECK_EQ(state(), kStateMutable);
if (!MinidumpWritable::Freeze()) {
return false;
}
key_.RegisterRVA(&entry_.key);
value_.RegisterRVA(&entry_.value);
return true;
}
size_t MinidumpSimpleStringDictionaryEntryWriter::SizeOfObject() {
DCHECK_GE(state(), kStateFrozen);
// This object doesnt directly write anything itself. Its
// MinidumpSimpleStringDictionaryEntry is written by its parent as part of a
// MinidumpSimpleStringDictionary, and its children are responsible for
// writing themselves.
return 0;
}
std::vector<internal::MinidumpWritable*>
MinidumpSimpleStringDictionaryEntryWriter::Children() {
DCHECK_GE(state(), kStateFrozen);
std::vector<MinidumpWritable*> children(1, &key_);
children.push_back(&value_);
return children;
}
bool MinidumpSimpleStringDictionaryEntryWriter::WriteObject(
FileWriterInterface* file_writer) {
DCHECK_EQ(state(), kStateWritable);
// This object doesnt directly write anything itself. Its
// MinidumpSimpleStringDictionaryEntry is written by its parent as part of a
// MinidumpSimpleStringDictionary, and its children are responsible for
// writing themselves.
return true;
}
MinidumpSimpleStringDictionaryWriter::MinidumpSimpleStringDictionaryWriter()
: MinidumpWritable(), simple_string_dictionary_base_(), entries_() {
}
MinidumpSimpleStringDictionaryWriter::~MinidumpSimpleStringDictionaryWriter() {
}
void MinidumpSimpleStringDictionaryWriter::AddEntry(
MinidumpSimpleStringDictionaryEntryWriter* entry) {
DCHECK_GE(state(), kStateMutable);
entries_[entry->Key()] = entry;
}
bool MinidumpSimpleStringDictionaryWriter::Freeze() {
DCHECK_EQ(state(), kStateMutable);
if (!MinidumpWritable::Freeze()) {
return false;
}
size_t entry_count = entries_.size();
if (!AssignIfInRange(&simple_string_dictionary_base_.count, entry_count)) {
LOG(ERROR) << "entry_count " << entry_count << " out of range";
return false;
}
return true;
}
size_t MinidumpSimpleStringDictionaryWriter::SizeOfObject() {
DCHECK_GE(state(), kStateFrozen);
return sizeof(simple_string_dictionary_base_) +
entries_.size() * sizeof(MinidumpSimpleStringDictionaryEntry);
}
std::vector<internal::MinidumpWritable*>
MinidumpSimpleStringDictionaryWriter::Children() {
DCHECK_GE(state(), kStateMutable);
std::vector<MinidumpWritable*> children;
for (const auto& key_entry : entries_) {
children.push_back(key_entry.second);
}
return children;
}
bool MinidumpSimpleStringDictionaryWriter::WriteObject(
FileWriterInterface* file_writer) {
DCHECK_GE(state(), kStateWritable);
WritableIoVec iov;
iov.iov_base = &simple_string_dictionary_base_;
iov.iov_len = sizeof(simple_string_dictionary_base_);
std::vector<WritableIoVec> iovecs(1, iov);
if (!entries_.empty()) {
iov.iov_len = sizeof(MinidumpSimpleStringDictionaryEntry);
for (const auto& key_entry : entries_) {
iov.iov_base = key_entry.second->MinidumpSimpleStringDictionaryEntry();
iovecs.push_back(iov);
}
}
return file_writer->WriteIoVec(&iovecs);
}
} // namespace crashpad

View File

@ -0,0 +1,125 @@
// 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.
#ifndef CRASHPAD_MINIDUMP_MINIDUMP_SIMPLE_STRING_DICTIONARY_WRITER_H_
#define CRASHPAD_MINIDUMP_MINIDUMP_SIMPLE_STRING_DICTIONARY_WRITER_H_
#include <sys/types.h>
#include <string>
#include <map>
#include <vector>
#include "base/basictypes.h"
#include "minidump/minidump_extensions.h"
#include "minidump/minidump_string_writer.h"
#include "minidump/minidump_writable.h"
#include "util/file/file_writer.h"
namespace crashpad {
//! \brief The writer for a MinidumpSimpleStringDictionaryEntry object in a
//! minidump file.
//!
//! Because MinidumpSimpleStringDictionaryEntry objects only appear as elements
//! of MinidumpSimpleStringDictionary objects, this class does not write any
//! data on its own. It makes its MinidumpSimpleStringDictionaryEntry data
//! available to its MinidumpSimpleStringDictionaryWriter parent, which writes
//! it as part of a MinidumpSimpleStringDictionary.
class MinidumpSimpleStringDictionaryEntryWriter final
: public internal::MinidumpWritable {
public:
MinidumpSimpleStringDictionaryEntryWriter();
~MinidumpSimpleStringDictionaryEntryWriter();
//! \brief Returns a MinidumpSimpleStringDictionaryEntry referencing this
//! objects data.
//!
//! This method is expected to be called by a
//! MinidumpSimpleStringDictionaryWriter in order to obtain a
//! MinidumpSimpleStringDictionaryEntry to include in its list.
//!
//! \note Valid in #kStateWritable.
const MinidumpSimpleStringDictionaryEntry*
MinidumpSimpleStringDictionaryEntry() const;
//! \brief Sets the strings to be written as the entry objects key and value.
//!
//! \note Valid in #kStateMutable.
void SetKeyValue(const std::string& key, const std::string& value);
//! \brief Retrieves the key to be written.
//!
//! \note Valid in any state.
const std::string& Key() const { return key_.UTF8(); }
protected:
// MinidumpWritable:
bool Freeze() override;
size_t SizeOfObject() override;
std::vector<MinidumpWritable*> Children() override;
bool WriteObject(FileWriterInterface* file_writer) override;
private:
struct MinidumpSimpleStringDictionaryEntry entry_;
internal::MinidumpUTF8StringWriter key_;
internal::MinidumpUTF8StringWriter value_;
DISALLOW_COPY_AND_ASSIGN(MinidumpSimpleStringDictionaryEntryWriter);
};
//! \brief The writer for a MinidumpSimpleStringDictionary object in a minidump
//! file, containing a list of MinidumpSimpleStringDictionaryEntry objects.
//!
//! Because this class writes a representatin of a dictionary, the order of
//! entries is insignificant. Entries may be written in any order.
class MinidumpSimpleStringDictionaryWriter final
: public internal::MinidumpWritable {
public:
MinidumpSimpleStringDictionaryWriter();
~MinidumpSimpleStringDictionaryWriter();
//! \brief Adds a MinidumpSimpleStringDictionaryEntryWriter to the
//! MinidumpSimpleStringDictionary.
//!
//! \a entry will become a child of this object in the overall tree of
//! internal::MinidumpWritable objects.
//!
//! If the key contained in \a entry duplicates the key of an entry already
//! present in the MinidumpSimpleStringDictionary, the new \a entry will
//! replace the previous one.
//!
//! \note Valid in #kStateMutable.
void AddEntry(MinidumpSimpleStringDictionaryEntryWriter* entry);
protected:
// MinidumpWritable:
bool Freeze() override;
size_t SizeOfObject() override;
std::vector<MinidumpWritable*> Children() override;
bool WriteObject(FileWriterInterface* file_writer) override;
private:
MinidumpSimpleStringDictionary simple_string_dictionary_base_;
std::map<std::string, MinidumpSimpleStringDictionaryEntryWriter*>
entries_; // weak
DISALLOW_COPY_AND_ASSIGN(MinidumpSimpleStringDictionaryWriter);
};
} // namespace crashpad
#endif // CRASHPAD_MINIDUMP_MINIDUMP_SIMPLE_STRING_DICTIONARY_WRITER_H_

View File

@ -0,0 +1,224 @@
// 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_simple_string_dictionary_writer.h"
#include <string>
#include "gtest/gtest.h"
#include "minidump/minidump_extensions.h"
#include "util/file/string_file_writer.h"
namespace crashpad {
namespace test {
namespace {
const MinidumpSimpleStringDictionary* MinidumpSimpleStringDictionaryCast(
const StringFileWriter& file_writer) {
return reinterpret_cast<const MinidumpSimpleStringDictionary*>(
&file_writer.string()[0]);
}
TEST(MinidumpSimpleStringDictionaryWriter, EmptySimpleStringDictionary) {
StringFileWriter file_writer;
MinidumpSimpleStringDictionaryWriter dictionary_writer;
EXPECT_TRUE(dictionary_writer.WriteEverything(&file_writer));
ASSERT_EQ(sizeof(MinidumpSimpleStringDictionary),
file_writer.string().size());
const MinidumpSimpleStringDictionary* dictionary =
MinidumpSimpleStringDictionaryCast(file_writer);
EXPECT_EQ(0u, dictionary->count);
}
std::string MinidumpUTF8StringAtRVA(const StringFileWriter& file_writer,
RVA rva) {
const std::string& contents = file_writer.string();
if (rva == 0) {
return std::string();
}
if (rva + sizeof(MinidumpUTF8String) > contents.size()) {
ADD_FAILURE()
<< "rva " << rva << " too large for contents " << contents.size();
return std::string();
}
const MinidumpUTF8String* minidump_string =
reinterpret_cast<const MinidumpUTF8String*>(&contents[rva]);
// Verify that the file has enough data for the strings stated length plus
// its required NUL terminator.
if (rva + sizeof(MinidumpUTF8String) + minidump_string->Length + 1 >
contents.size()) {
ADD_FAILURE()
<< "rva " << rva << ", length " << minidump_string->Length
<< " too large for contents " << contents.size();
return std::string();
}
std::string minidump_string_data(
reinterpret_cast<const char*>(&minidump_string->Buffer[0]),
minidump_string->Length);
return minidump_string_data;
}
TEST(MinidumpSimpleStringDictionaryWriter, EmptyKeyValue) {
StringFileWriter file_writer;
MinidumpSimpleStringDictionaryWriter dictionary_writer;
MinidumpSimpleStringDictionaryEntryWriter entry_writer;
dictionary_writer.AddEntry(&entry_writer);
EXPECT_TRUE(dictionary_writer.WriteEverything(&file_writer));
ASSERT_EQ(sizeof(MinidumpSimpleStringDictionary) +
sizeof(MinidumpSimpleStringDictionaryEntry) +
2 * sizeof(MinidumpUTF8String) + 1 + 3 + 1, // 3 for padding
file_writer.string().size());
const MinidumpSimpleStringDictionary* dictionary =
MinidumpSimpleStringDictionaryCast(file_writer);
EXPECT_EQ(1u, dictionary->count);
EXPECT_EQ(12u, dictionary->entries[0].key);
EXPECT_EQ(20u, dictionary->entries[0].value);
EXPECT_EQ("",
MinidumpUTF8StringAtRVA(file_writer, dictionary->entries[0].key));
EXPECT_EQ("",
MinidumpUTF8StringAtRVA(file_writer, dictionary->entries[0].value));
}
TEST(MinidumpSimpleStringDictionaryWriter, OneKeyValue) {
StringFileWriter file_writer;
char kKey[] = "key";
char kValue[] = "value";
MinidumpSimpleStringDictionaryWriter dictionary_writer;
MinidumpSimpleStringDictionaryEntryWriter entry_writer;
entry_writer.SetKeyValue(kKey, kValue);
dictionary_writer.AddEntry(&entry_writer);
EXPECT_TRUE(dictionary_writer.WriteEverything(&file_writer));
ASSERT_EQ(sizeof(MinidumpSimpleStringDictionary) +
sizeof(MinidumpSimpleStringDictionaryEntry) +
2 * sizeof(MinidumpUTF8String) + sizeof(kKey) + sizeof(kValue),
file_writer.string().size());
const MinidumpSimpleStringDictionary* dictionary =
MinidumpSimpleStringDictionaryCast(file_writer);
EXPECT_EQ(1u, dictionary->count);
EXPECT_EQ(12u, dictionary->entries[0].key);
EXPECT_EQ(20u, dictionary->entries[0].value);
EXPECT_EQ(kKey,
MinidumpUTF8StringAtRVA(file_writer, dictionary->entries[0].key));
EXPECT_EQ(kValue,
MinidumpUTF8StringAtRVA(file_writer, dictionary->entries[0].value));
}
TEST(MinidumpSimpleStringDictionaryWriter, ThreeKeysValues) {
StringFileWriter file_writer;
char kKey0[] = "m0";
char kValue0[] = "value0";
char kKey1[] = "zzz1";
char kValue1[] = "v1";
char kKey2[] = "aa2";
char kValue2[] = "val2";
MinidumpSimpleStringDictionaryWriter dictionary_writer;
MinidumpSimpleStringDictionaryEntryWriter entry_writer_0;
entry_writer_0.SetKeyValue(kKey0, kValue0);
dictionary_writer.AddEntry(&entry_writer_0);
MinidumpSimpleStringDictionaryEntryWriter entry_writer_1;
entry_writer_1.SetKeyValue(kKey1, kValue1);
dictionary_writer.AddEntry(&entry_writer_1);
MinidumpSimpleStringDictionaryEntryWriter entry_writer_2;
entry_writer_2.SetKeyValue(kKey2, kValue2);
dictionary_writer.AddEntry(&entry_writer_2);
EXPECT_TRUE(dictionary_writer.WriteEverything(&file_writer));
ASSERT_EQ(sizeof(MinidumpSimpleStringDictionary) +
3 * sizeof(MinidumpSimpleStringDictionaryEntry) +
6 * sizeof(MinidumpUTF8String) + sizeof(kKey2) +
sizeof(kValue2) + 3 + sizeof(kKey0) + 1 + sizeof(kValue0) + 1 +
sizeof(kKey1) + 3 + sizeof(kValue1),
file_writer.string().size());
const MinidumpSimpleStringDictionary* dictionary =
MinidumpSimpleStringDictionaryCast(file_writer);
EXPECT_EQ(3u, dictionary->count);
EXPECT_EQ(28u, dictionary->entries[0].key);
EXPECT_EQ(36u, dictionary->entries[0].value);
EXPECT_EQ(48u, dictionary->entries[1].key);
EXPECT_EQ(56u, dictionary->entries[1].value);
EXPECT_EQ(68u, dictionary->entries[2].key);
EXPECT_EQ(80u, dictionary->entries[2].value);
// The entries dont appear in the order they were added. The current
// implementation uses a std::map and sorts keys, so the entires appear in
// alphabetical order. However, this is an implementation detail, and its OK
// if the writer stops sorting in this order. Testing for a specific order is
// just the easiest way to write this test while the writer will output things
// in a known order.
EXPECT_EQ(kKey2,
MinidumpUTF8StringAtRVA(file_writer, dictionary->entries[0].key));
EXPECT_EQ(kValue2,
MinidumpUTF8StringAtRVA(file_writer, dictionary->entries[0].value));
EXPECT_EQ(kKey0,
MinidumpUTF8StringAtRVA(file_writer, dictionary->entries[1].key));
EXPECT_EQ(kValue0,
MinidumpUTF8StringAtRVA(file_writer, dictionary->entries[1].value));
EXPECT_EQ(kKey1,
MinidumpUTF8StringAtRVA(file_writer, dictionary->entries[2].key));
EXPECT_EQ(kValue1,
MinidumpUTF8StringAtRVA(file_writer, dictionary->entries[2].value));
}
TEST(MinidumpSimpleStringDictionaryWriter, DuplicateKeyValue) {
StringFileWriter file_writer;
char kKey[] = "key";
char kValue0[] = "fake_value";
char kValue1[] = "value";
MinidumpSimpleStringDictionaryWriter dictionary_writer;
MinidumpSimpleStringDictionaryEntryWriter entry_writer_0;
entry_writer_0.SetKeyValue(kKey, kValue0);
dictionary_writer.AddEntry(&entry_writer_0);
MinidumpSimpleStringDictionaryEntryWriter entry_writer_1;
entry_writer_1.SetKeyValue(kKey, kValue1);
dictionary_writer.AddEntry(&entry_writer_1);
EXPECT_TRUE(dictionary_writer.WriteEverything(&file_writer));
ASSERT_EQ(sizeof(MinidumpSimpleStringDictionary) +
sizeof(MinidumpSimpleStringDictionaryEntry) +
2 * sizeof(MinidumpUTF8String) + sizeof(kKey) + sizeof(kValue1),
file_writer.string().size());
const MinidumpSimpleStringDictionary* dictionary =
MinidumpSimpleStringDictionaryCast(file_writer);
EXPECT_EQ(1u, dictionary->count);
EXPECT_EQ(12u, dictionary->entries[0].key);
EXPECT_EQ(20u, dictionary->entries[0].value);
EXPECT_EQ(kKey,
MinidumpUTF8StringAtRVA(file_writer, dictionary->entries[0].key));
EXPECT_EQ(kValue1,
MinidumpUTF8StringAtRVA(file_writer, dictionary->entries[0].value));
}
} // namespace
} // namespace test
} // namespace crashpad

View File

@ -69,6 +69,11 @@ class MinidumpStringWriter : public MinidumpWritable {
//! \note Valid in #kStateMutable. //! \note Valid in #kStateMutable.
void set_string(const StringType& string) { string_.assign(string); } void set_string(const StringType& string) { string_.assign(string); }
//! \brief Retrieves the string to be written.
//!
//! \note Valid in any state.
const StringType& string() const { return string_; }
private: private:
MinidumpStringType string_base_; MinidumpStringType string_base_;
StringType string_; StringType string_;
@ -113,6 +118,11 @@ class MinidumpUTF8StringWriter final
//! \note Valid in #kStateMutable. //! \note Valid in #kStateMutable.
void SetUTF8(const std::string& string_utf8) { set_string(string_utf8); } void SetUTF8(const std::string& string_utf8) { set_string(string_utf8); }
//! \brief Retrieves the string to be written.
//!
//! \note Valid in any state.
const std::string& UTF8() const { return string(); }
private: private:
DISALLOW_COPY_AND_ASSIGN(MinidumpUTF8StringWriter); DISALLOW_COPY_AND_ASSIGN(MinidumpUTF8StringWriter);
}; };

View File

@ -181,8 +181,9 @@ TEST(MinidumpStringWriter, MinidumpUTF8StringWriter) {
file_writer.Reset(); file_writer.Reset();
crashpad::internal::MinidumpUTF8StringWriter string_writer; crashpad::internal::MinidumpUTF8StringWriter string_writer;
string_writer.SetUTF8( std::string test_string(kTestData[index].string, kTestData[index].length);
std::string(kTestData[index].string, kTestData[index].length)); string_writer.SetUTF8(test_string);
EXPECT_EQ(test_string, string_writer.UTF8());
EXPECT_TRUE(string_writer.WriteEverything(&file_writer)); EXPECT_TRUE(string_writer.WriteEverything(&file_writer));
const size_t expected_utf8_bytes_with_nul = kTestData[index].length + 1; const size_t expected_utf8_bytes_with_nul = kTestData[index].length + 1;