summaryrefslogtreecommitdiff
path: root/ipc/glue/CrashReporterMetadataShmem.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'ipc/glue/CrashReporterMetadataShmem.cpp')
-rw-r--r--ipc/glue/CrashReporterMetadataShmem.cpp235
1 files changed, 235 insertions, 0 deletions
diff --git a/ipc/glue/CrashReporterMetadataShmem.cpp b/ipc/glue/CrashReporterMetadataShmem.cpp
new file mode 100644
index 0000000000..f579d5bb0e
--- /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