diff options
Diffstat (limited to 'ipc/glue/CrashReporterMetadataShmem.cpp')
-rw-r--r-- | ipc/glue/CrashReporterMetadataShmem.cpp | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/ipc/glue/CrashReporterMetadataShmem.cpp b/ipc/glue/CrashReporterMetadataShmem.cpp new file mode 100644 index 000000000..f579d5bb0 --- /dev/null +++ b/ipc/glue/CrashReporterMetadataShmem.cpp @@ -0,0 +1,235 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "CrashReporterMetadataShmem.h" +#include "mozilla/Attributes.h" +#include "nsISupportsImpl.h" + +namespace mozilla { +namespace ipc { + +enum class EntryType : uint8_t { + None, + Annotation, +}; + +CrashReporterMetadataShmem::CrashReporterMetadataShmem(const Shmem& aShmem) + : mShmem(aShmem) +{ + MOZ_COUNT_CTOR(CrashReporterMetadataShmem); +} + +CrashReporterMetadataShmem::~CrashReporterMetadataShmem() +{ + MOZ_COUNT_DTOR(CrashReporterMetadataShmem); +} + +void +CrashReporterMetadataShmem::AnnotateCrashReport(const nsCString& aKey, const nsCString& aData) +{ + mNotes.Put(aKey, aData); + SyncNotesToShmem(); +} + +void +CrashReporterMetadataShmem::AppendAppNotes(const nsCString& aData) +{ + mAppNotes.Append(aData); + mNotes.Put(NS_LITERAL_CSTRING("Notes"), mAppNotes); + SyncNotesToShmem(); +} + +class MOZ_STACK_CLASS MetadataShmemWriter +{ +public: + explicit MetadataShmemWriter(const Shmem& aShmem) + : mCursor(aShmem.get<uint8_t>()), + mEnd(mCursor + aShmem.Size<uint8_t>()) + { + *mCursor = uint8_t(EntryType::None); + } + + MOZ_MUST_USE bool WriteAnnotation(const nsCString& aKey, const nsCString& aValue) { + // This shouldn't happen because Commit() guarantees mCursor < mEnd. But + // we might as well be safe. + if (mCursor >= mEnd) { + return false; + } + + // Save the current position so we can write the entry type if the entire + // entry fits. + uint8_t* start = mCursor++; + if (!Write(aKey) || !Write(aValue)) { + return false; + } + return Commit(start, EntryType::Annotation); + } + +private: + // On success, append a new terminal byte. On failure, rollback the cursor. + MOZ_MUST_USE bool Commit(uint8_t* aStart, EntryType aType) { + MOZ_ASSERT(aStart < mEnd); + MOZ_ASSERT(EntryType(*aStart) == EntryType::None); + + if (mCursor >= mEnd) { + // No room for a terminating byte - rollback. + mCursor = aStart; + return false; + } + + // Commit the entry and write a new terminal byte. + *aStart = uint8_t(aType); + *mCursor = uint8_t(EntryType::None); + return true; + } + + MOZ_MUST_USE bool Write(const nsCString& aString) { + // 32-bit length is okay since our shmems are very small (16K), + // a huge write would fail anyway. + return Write(static_cast<uint32_t>(aString.Length())) && + Write(aString.get(), aString.Length()); + } + + template <typename T> + MOZ_MUST_USE bool Write(const T& aT) { + return Write(&aT, sizeof(T)); + } + + MOZ_MUST_USE bool Write(const void* aData, size_t aLength) { + if (size_t(mEnd - mCursor) < aLength) { + return false; + } + memcpy(mCursor, aData, aLength); + mCursor += aLength; + return true; + } + + private: + // The cursor (beginning at start) always points to a single byte + // representing the next EntryType. An EntryType is either None, + // indicating there are no more entries, or Annotation, meaning + // two strings follow. + // + // Strings are written as a 32-bit length and byte sequence. After each new + // entry, a None entry is always appended, and a subsequent entry will + // overwrite this byte. + uint8_t* mCursor; + uint8_t* mEnd; +}; + +void +CrashReporterMetadataShmem::SyncNotesToShmem() +{ + MetadataShmemWriter writer(mShmem); + + for (auto it = mNotes.Iter(); !it.Done(); it.Next()) { + nsCString key = nsCString(it.Key()); + nsCString value = nsCString(it.Data()); + if (!writer.WriteAnnotation(key, value)) { + return; + } + } +} + +// Helper class to iterate over metadata entries encoded in shmem. +class MOZ_STACK_CLASS MetadataShmemReader +{ +public: + explicit MetadataShmemReader(const Shmem& aShmem) + : mEntryType(EntryType::None) + { + mCursor = aShmem.get<uint8_t>(); + mEnd = mCursor + aShmem.Size<uint8_t>(); + + // Advance to the first item, if any. + Next(); + } + + bool Done() const { + return mCursor >= mEnd || Type() == EntryType::None; + } + EntryType Type() const { + return mEntryType; + } + void Next() { + if (mCursor < mEnd) { + mEntryType = EntryType(*mCursor++); + } else { + mEntryType = EntryType::None; + } + } + + bool Read(nsCString& aOut) { + uint32_t length = 0; + if (!Read(&length)) { + return false; + } + + const uint8_t* src = Read(length); + if (!src) { + return false; + } + + aOut.Assign((const char *)src, length); + return true; + } + +private: + template <typename T> + bool Read(T* aOut) { + return Read(aOut, sizeof(T)); + } + bool Read(void* aOut, size_t aLength) { + const uint8_t* src = Read(aLength); + if (!src) { + return false; + } + memcpy(aOut, src, aLength); + return true; + } + + // If buffer has |aLength| bytes, return cursor and then advance it. + // Otherwise, return null. + const uint8_t* Read(size_t aLength) { + if (size_t(mEnd - mCursor) < aLength) { + return nullptr; + } + const uint8_t* result = mCursor; + mCursor += aLength; + return result; + } + +private: + const uint8_t* mCursor; + const uint8_t* mEnd; + EntryType mEntryType; +}; + +#ifdef MOZ_CRASHREPORTER +void +CrashReporterMetadataShmem::ReadAppNotes(const Shmem& aShmem, CrashReporter::AnnotationTable* aNotes) +{ + for (MetadataShmemReader reader(aShmem); !reader.Done(); reader.Next()) { + switch (reader.Type()) { + case EntryType::Annotation: { + nsCString key, value; + if (!reader.Read(key) || !reader.Read(value)) { + return; + } + + aNotes->Put(key, value); + break; + } + default: + NS_ASSERTION(false, "Unknown metadata entry type"); + break; + } + } +} +#endif + +} // namespace ipc +} // namespace mozilla |