1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #define LOG_TAG "NBLog"
18 //#define LOG_NDEBUG 0
19
20 #include <memory>
21 #include <stddef.h>
22 #include <stdint.h>
23
24 #include <audio_utils/fifo.h>
25 #include <media/nblog/Entry.h>
26 #include <media/nblog/Events.h>
27 #include <utils/Log.h>
28
29 namespace android {
30 namespace NBLog {
31
copyEntryDataAt(size_t offset) const32 int Entry::copyEntryDataAt(size_t offset) const
33 {
34 // FIXME This is too slow
35 if (offset == 0) {
36 return mEvent;
37 } else if (offset == 1) {
38 return mLength;
39 } else if (offset < (size_t) (mLength + 2)) {
40 return (int) ((char *) mData)[offset - 2];
41 } else if (offset == (size_t) (mLength + 2)) {
42 return mLength;
43 } else {
44 return 0; // FIXME is this an error?
45 }
46 }
47
EntryIterator()48 EntryIterator::EntryIterator() // Dummy initialization.
49 : mPtr(nullptr)
50 {
51 }
52
EntryIterator(const uint8_t * entry)53 EntryIterator::EntryIterator(const uint8_t *entry)
54 : mPtr(entry)
55 {
56 }
57
EntryIterator(const EntryIterator & other)58 EntryIterator::EntryIterator(const EntryIterator &other)
59 : mPtr(other.mPtr)
60 {
61 }
62
operator *() const63 const entry& EntryIterator::operator*() const
64 {
65 return *(entry*) mPtr;
66 }
67
operator ->() const68 const entry* EntryIterator::operator->() const
69 {
70 return (entry*) mPtr;
71 }
72
operator ++()73 EntryIterator& EntryIterator::operator++()
74 {
75 mPtr += mPtr[offsetof(entry, length)] + Entry::kOverhead;
76 return *this;
77 }
78
operator --()79 EntryIterator& EntryIterator::operator--()
80 {
81 mPtr -= mPtr[Entry::kPreviousLengthOffset] + Entry::kOverhead;
82 return *this;
83 }
84
next() const85 EntryIterator EntryIterator::next() const
86 {
87 EntryIterator aux(*this);
88 return ++aux;
89 }
90
prev() const91 EntryIterator EntryIterator::prev() const
92 {
93 EntryIterator aux(*this);
94 return --aux;
95 }
96
operator !=(const EntryIterator & other) const97 bool EntryIterator::operator!=(const EntryIterator &other) const
98 {
99 return mPtr != other.mPtr;
100 }
101
operator -(const EntryIterator & other) const102 int EntryIterator::operator-(const EntryIterator &other) const
103 {
104 return mPtr - other.mPtr;
105 }
106
hasConsistentLength() const107 bool EntryIterator::hasConsistentLength() const
108 {
109 return mPtr[offsetof(entry, length)] == mPtr[mPtr[offsetof(entry, length)] +
110 Entry::kOverhead + Entry::kPreviousLengthOffset];
111 }
112
copyTo(std::unique_ptr<audio_utils_fifo_writer> & dst) const113 void EntryIterator::copyTo(std::unique_ptr<audio_utils_fifo_writer> &dst) const
114 {
115 size_t length = mPtr[offsetof(entry, length)] + Entry::kOverhead;
116 dst->write(mPtr, length);
117 }
118
copyData(uint8_t * dst) const119 void EntryIterator::copyData(uint8_t *dst) const
120 {
121 memcpy((void*) dst, mPtr + offsetof(entry, data), mPtr[offsetof(entry, length)]);
122 }
123
124 // ---------------------------------------------------------------------------
125
buildEntry(const uint8_t * ptr)126 std::unique_ptr<AbstractEntry> AbstractEntry::buildEntry(const uint8_t *ptr)
127 {
128 if (ptr == nullptr) {
129 return nullptr;
130 }
131 const uint8_t type = EntryIterator(ptr)->type;
132 switch (type) {
133 case EVENT_FMT_START:
134 return std::make_unique<FormatEntry>(FormatEntry(ptr));
135 case EVENT_AUDIO_STATE:
136 case EVENT_HISTOGRAM_ENTRY_TS:
137 return std::make_unique<HistogramEntry>(HistogramEntry(ptr));
138 default:
139 ALOGW("Tried to create AbstractEntry of type %d", type);
140 return nullptr;
141 }
142 }
143
begin() const144 EntryIterator FormatEntry::begin() const
145 {
146 return EntryIterator(mEntry);
147 }
148
formatString() const149 const char *FormatEntry::formatString() const
150 {
151 return (const char*) mEntry + offsetof(entry, data);
152 }
153
formatStringLength() const154 size_t FormatEntry::formatStringLength() const
155 {
156 return mEntry[offsetof(entry, length)];
157 }
158
args() const159 EntryIterator FormatEntry::args() const
160 {
161 auto it = begin();
162 ++it; // skip start fmt
163 ++it; // skip timestamp
164 ++it; // skip hash
165 // Skip author if present
166 if (it->type == EVENT_FMT_AUTHOR) {
167 ++it;
168 }
169 return it;
170 }
171
timestamp() const172 int64_t FormatEntry::timestamp() const
173 {
174 auto it = begin();
175 ++it; // skip start fmt
176 return it.payload<int64_t>();
177 }
178
hash() const179 log_hash_t FormatEntry::hash() const
180 {
181 auto it = begin();
182 ++it; // skip start fmt
183 ++it; // skip timestamp
184 // unaligned 64-bit read not supported
185 log_hash_t hash;
186 memcpy(&hash, it->data, sizeof(hash));
187 return hash;
188 }
189
author() const190 int FormatEntry::author() const
191 {
192 auto it = begin();
193 ++it; // skip start fmt
194 ++it; // skip timestamp
195 ++it; // skip hash
196 // if there is an author entry, return it, return -1 otherwise
197 return it->type == EVENT_FMT_AUTHOR ? it.payload<int>() : -1;
198 }
199
copyWithAuthor(std::unique_ptr<audio_utils_fifo_writer> & dst,int author) const200 EntryIterator FormatEntry::copyWithAuthor(
201 std::unique_ptr<audio_utils_fifo_writer> &dst, int author) const
202 {
203 auto it = begin();
204 it.copyTo(dst); // copy fmt start entry
205 (++it).copyTo(dst); // copy timestamp
206 (++it).copyTo(dst); // copy hash
207 // insert author entry
208 size_t authorEntrySize = Entry::kOverhead + sizeof(author);
209 uint8_t authorEntry[authorEntrySize];
210 authorEntry[offsetof(entry, type)] = EVENT_FMT_AUTHOR;
211 authorEntry[offsetof(entry, length)] =
212 authorEntry[authorEntrySize + Entry::kPreviousLengthOffset] =
213 sizeof(author);
214 *(int*) (&authorEntry[offsetof(entry, data)]) = author;
215 dst->write(authorEntry, authorEntrySize);
216 // copy rest of entries
217 while ((++it)->type != EVENT_FMT_END) {
218 it.copyTo(dst);
219 }
220 it.copyTo(dst);
221 ++it;
222 return it;
223 }
224
timestamp() const225 int64_t HistogramEntry::timestamp() const
226 {
227 return EntryIterator(mEntry).payload<HistTsEntry>().ts;
228 }
229
hash() const230 log_hash_t HistogramEntry::hash() const
231 {
232 return EntryIterator(mEntry).payload<HistTsEntry>().hash;
233 }
234
author() const235 int HistogramEntry::author() const
236 {
237 EntryIterator it(mEntry);
238 return it->length == sizeof(HistTsEntryWithAuthor)
239 ? it.payload<HistTsEntryWithAuthor>().author : -1;
240 }
241
copyWithAuthor(std::unique_ptr<audio_utils_fifo_writer> & dst,int author) const242 EntryIterator HistogramEntry::copyWithAuthor(
243 std::unique_ptr<audio_utils_fifo_writer> &dst, int author) const
244 {
245 // Current histogram entry has {type, length, struct HistTsEntry, length}.
246 // We now want {type, length, struct HistTsEntryWithAuthor, length}
247 uint8_t buffer[Entry::kOverhead + sizeof(HistTsEntryWithAuthor)];
248 // Copy content until the point we want to add the author
249 memcpy(buffer, mEntry, sizeof(entry) + sizeof(HistTsEntry));
250 // Copy the author
251 *(int*) (buffer + sizeof(entry) + sizeof(HistTsEntry)) = author;
252 // Update lengths
253 buffer[offsetof(entry, length)] = sizeof(HistTsEntryWithAuthor);
254 buffer[offsetof(entry, data) + sizeof(HistTsEntryWithAuthor) + offsetof(ending, length)]
255 = sizeof(HistTsEntryWithAuthor);
256 // Write new buffer into FIFO
257 dst->write(buffer, sizeof(buffer));
258 return EntryIterator(mEntry).next();
259 }
260
261 } // namespace NBLog
262 } // namespace android
263