DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Header

Mercurial (03b97487f359)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
/* -*- 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 "mozilla/EnumeratedRange.h"
#include "nsISupportsImpl.h"

namespace mozilla {
namespace ipc {

using CrashReporter::Annotation;

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(Annotation aKey,
                                                     const nsACString& aData) {
  mAnnotations[aKey] = aData;
  SyncNotesToShmem();
}

void CrashReporterMetadataShmem::AppendAppNotes(const nsACString& aData) {
  mAppNotes.Append(aData);
  mAnnotations[Annotation::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(Annotation 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 key : MakeEnumeratedRange(Annotation::Count)) {
    if (!mAnnotations[key].IsEmpty()) {
      if (!writer.WriteAnnotation(key, mAnnotations[key])) {
        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;
    }
  }

  template <typename T>
  bool Read(T* aOut) {
    return Read(aOut, sizeof(T));
  }

  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:
  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;
};

void CrashReporterMetadataShmem::ReadAppNotes(const Shmem& aShmem,
                                              AnnotationTable& aNotes) {
  for (MetadataShmemReader reader(aShmem); !reader.Done(); reader.Next()) {
    switch (reader.Type()) {
      case EntryType::Annotation: {
        Annotation key;
        nsCString value;
        if (!reader.Read(&key) || !reader.Read(value)) {
          return;
        }

        aNotes[key] = value;
        break;
      }
      default:
        NS_ASSERTION(false, "Unknown metadata entry type");
        break;
    }
  }
}

}  // namespace ipc
}  // namespace mozilla