summaryrefslogtreecommitdiff
path: root/ipc/glue/CrashReporterMetadataShmem.cpp
blob: 5b889948bc574c08018f900c3a1ebe7515c796d4 (plain)
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
/* -*- 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;
};

} // namespace ipc
} // namespace mozilla