gecko-dev/ipc/glue/CrashReporterMetadataShmem.cpp

236 lines
5.7 KiB
C++

/* -*- 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