Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "CrashReporterMetadataShmem.h"
8 : #include "mozilla/Attributes.h"
9 : #include "nsISupportsImpl.h"
10 :
11 : namespace mozilla {
12 : namespace ipc {
13 :
14 : enum class EntryType : uint8_t {
15 : None,
16 : Annotation,
17 : };
18 :
19 2 : CrashReporterMetadataShmem::CrashReporterMetadataShmem(const Shmem& aShmem)
20 2 : : mShmem(aShmem)
21 : {
22 2 : MOZ_COUNT_CTOR(CrashReporterMetadataShmem);
23 2 : }
24 :
25 0 : CrashReporterMetadataShmem::~CrashReporterMetadataShmem()
26 : {
27 0 : MOZ_COUNT_DTOR(CrashReporterMetadataShmem);
28 0 : }
29 :
30 : void
31 0 : CrashReporterMetadataShmem::AnnotateCrashReport(const nsCString& aKey, const nsCString& aData)
32 : {
33 0 : mNotes.Put(aKey, aData);
34 0 : SyncNotesToShmem();
35 0 : }
36 :
37 : void
38 0 : CrashReporterMetadataShmem::AppendAppNotes(const nsCString& aData)
39 : {
40 0 : mAppNotes.Append(aData);
41 0 : mNotes.Put(NS_LITERAL_CSTRING("Notes"), mAppNotes);
42 0 : SyncNotesToShmem();
43 0 : }
44 :
45 : class MOZ_STACK_CLASS MetadataShmemWriter
46 : {
47 : public:
48 0 : explicit MetadataShmemWriter(const Shmem& aShmem)
49 0 : : mCursor(aShmem.get<uint8_t>()),
50 0 : mEnd(mCursor + aShmem.Size<uint8_t>())
51 : {
52 0 : *mCursor = uint8_t(EntryType::None);
53 0 : }
54 :
55 0 : MOZ_MUST_USE bool WriteAnnotation(const nsCString& aKey, const nsCString& aValue) {
56 : // This shouldn't happen because Commit() guarantees mCursor < mEnd. But
57 : // we might as well be safe.
58 0 : if (mCursor >= mEnd) {
59 0 : return false;
60 : }
61 :
62 : // Save the current position so we can write the entry type if the entire
63 : // entry fits.
64 0 : uint8_t* start = mCursor++;
65 0 : if (!Write(aKey) || !Write(aValue)) {
66 0 : return false;
67 : }
68 0 : return Commit(start, EntryType::Annotation);
69 : }
70 :
71 : private:
72 : // On success, append a new terminal byte. On failure, rollback the cursor.
73 0 : MOZ_MUST_USE bool Commit(uint8_t* aStart, EntryType aType) {
74 0 : MOZ_ASSERT(aStart < mEnd);
75 0 : MOZ_ASSERT(EntryType(*aStart) == EntryType::None);
76 :
77 0 : if (mCursor >= mEnd) {
78 : // No room for a terminating byte - rollback.
79 0 : mCursor = aStart;
80 0 : return false;
81 : }
82 :
83 : // Commit the entry and write a new terminal byte.
84 0 : *aStart = uint8_t(aType);
85 0 : *mCursor = uint8_t(EntryType::None);
86 0 : return true;
87 : }
88 :
89 0 : MOZ_MUST_USE bool Write(const nsCString& aString) {
90 : // 32-bit length is okay since our shmems are very small (16K),
91 : // a huge write would fail anyway.
92 0 : return Write(static_cast<uint32_t>(aString.Length())) &&
93 0 : Write(aString.get(), aString.Length());
94 : }
95 :
96 : template <typename T>
97 0 : MOZ_MUST_USE bool Write(const T& aT) {
98 0 : return Write(&aT, sizeof(T));
99 : }
100 :
101 0 : MOZ_MUST_USE bool Write(const void* aData, size_t aLength) {
102 0 : if (size_t(mEnd - mCursor) < aLength) {
103 0 : return false;
104 : }
105 0 : memcpy(mCursor, aData, aLength);
106 0 : mCursor += aLength;
107 0 : return true;
108 : }
109 :
110 : private:
111 : // The cursor (beginning at start) always points to a single byte
112 : // representing the next EntryType. An EntryType is either None,
113 : // indicating there are no more entries, or Annotation, meaning
114 : // two strings follow.
115 : //
116 : // Strings are written as a 32-bit length and byte sequence. After each new
117 : // entry, a None entry is always appended, and a subsequent entry will
118 : // overwrite this byte.
119 : uint8_t* mCursor;
120 : uint8_t* mEnd;
121 : };
122 :
123 : void
124 0 : CrashReporterMetadataShmem::SyncNotesToShmem()
125 : {
126 0 : MetadataShmemWriter writer(mShmem);
127 :
128 0 : for (auto it = mNotes.Iter(); !it.Done(); it.Next()) {
129 0 : nsCString key = nsCString(it.Key());
130 0 : nsCString value = nsCString(it.Data());
131 0 : if (!writer.WriteAnnotation(key, value)) {
132 0 : return;
133 : }
134 : }
135 : }
136 :
137 : // Helper class to iterate over metadata entries encoded in shmem.
138 : class MOZ_STACK_CLASS MetadataShmemReader
139 : {
140 : public:
141 0 : explicit MetadataShmemReader(const Shmem& aShmem)
142 0 : : mEntryType(EntryType::None)
143 : {
144 0 : mCursor = aShmem.get<uint8_t>();
145 0 : mEnd = mCursor + aShmem.Size<uint8_t>();
146 :
147 : // Advance to the first item, if any.
148 0 : Next();
149 0 : }
150 :
151 0 : bool Done() const {
152 0 : return mCursor >= mEnd || Type() == EntryType::None;
153 : }
154 0 : EntryType Type() const {
155 0 : return mEntryType;
156 : }
157 0 : void Next() {
158 0 : if (mCursor < mEnd) {
159 0 : mEntryType = EntryType(*mCursor++);
160 : } else {
161 0 : mEntryType = EntryType::None;
162 : }
163 0 : }
164 :
165 0 : bool Read(nsCString& aOut) {
166 0 : uint32_t length = 0;
167 0 : if (!Read(&length)) {
168 0 : return false;
169 : }
170 :
171 0 : const uint8_t* src = Read(length);
172 0 : if (!src) {
173 0 : return false;
174 : }
175 :
176 0 : aOut.Assign((const char *)src, length);
177 0 : return true;
178 : }
179 :
180 : private:
181 : template <typename T>
182 0 : bool Read(T* aOut) {
183 0 : return Read(aOut, sizeof(T));
184 : }
185 0 : bool Read(void* aOut, size_t aLength) {
186 0 : const uint8_t* src = Read(aLength);
187 0 : if (!src) {
188 0 : return false;
189 : }
190 0 : memcpy(aOut, src, aLength);
191 0 : return true;
192 : }
193 :
194 : // If buffer has |aLength| bytes, return cursor and then advance it.
195 : // Otherwise, return null.
196 0 : const uint8_t* Read(size_t aLength) {
197 0 : if (size_t(mEnd - mCursor) < aLength) {
198 0 : return nullptr;
199 : }
200 0 : const uint8_t* result = mCursor;
201 0 : mCursor += aLength;
202 0 : return result;
203 : }
204 :
205 : private:
206 : const uint8_t* mCursor;
207 : const uint8_t* mEnd;
208 : EntryType mEntryType;
209 : };
210 :
211 : #ifdef MOZ_CRASHREPORTER
212 : void
213 0 : CrashReporterMetadataShmem::ReadAppNotes(const Shmem& aShmem, CrashReporter::AnnotationTable* aNotes)
214 : {
215 0 : for (MetadataShmemReader reader(aShmem); !reader.Done(); reader.Next()) {
216 0 : switch (reader.Type()) {
217 : case EntryType::Annotation: {
218 0 : nsCString key, value;
219 0 : if (!reader.Read(key) || !reader.Read(value)) {
220 0 : return;
221 : }
222 :
223 0 : aNotes->Put(key, value);
224 0 : break;
225 : }
226 : default:
227 0 : NS_ASSERTION(false, "Unknown metadata entry type");
228 0 : break;
229 : }
230 : }
231 : }
232 : #endif
233 :
234 : } // namespace ipc
235 : } // namespace mozilla
|