| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright (C) 2013 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 |  | 
| Sanna Catherine de Treville Wager | 2d1631e | 2017-05-30 16:12:36 -0700 | [diff] [blame] | 17 | /* | 
|  | 18 | * Documentation: Workflow summary for histogram data processing: | 
|  | 19 | * For more details on FIFO, please see system/media/audio_utils; doxygen | 
|  | 20 | * TODO: add this documentation to doxygen once it is further developed | 
| Sanna Catherine de Treville Wager | 1bb6862 | 2017-06-14 14:18:31 -0700 | [diff] [blame] | 21 | * 1) Writing buffer period timestamp to the circular buffer | 
|  | 22 | * onWork() | 
| Sanna Catherine de Treville Wager | 2d1631e | 2017-05-30 16:12:36 -0700 | [diff] [blame] | 23 | *     Called every period length (e.g., 4ms) | 
|  | 24 | *     Calls LOG_HIST_TS | 
|  | 25 | * LOG_HIST_TS | 
|  | 26 | *     Hashes file name and line number | 
|  | 27 | *     calls NBLOG::Writer::logHistTS once | 
|  | 28 | * NBLOG::Writer::logHistTS | 
|  | 29 | *     calls NBLOG::Writer::log on hash and current timestamp | 
|  | 30 | *     time is in CLOCK_MONOTONIC converted to ns | 
|  | 31 | * NBLOG::Writer::log(Event, const void*, size_t) | 
|  | 32 | *     Initializes Entry, a struct containing one log entry | 
|  | 33 | *     Entry contains the event type (mEvent), data length (mLength), | 
|  | 34 | *     and data pointer (mData) | 
|  | 35 | *     TODO: why mLength (max length of buffer data)  must be <= kMaxLength = 255? | 
|  | 36 | *     calls NBLOG::Writer::log(Entry *, bool) | 
|  | 37 | * NBLog::Writer::log(Entry *, bool) | 
|  | 38 | *     Calls copyEntryDataAt to format data as follows in temp array: | 
|  | 39 | *     [type][length][data ... ][length] | 
|  | 40 | *     calls audio_utils_fifo_writer.write on temp | 
|  | 41 | * audio_utils_fifo_writer.write | 
|  | 42 | *     calls obtain(), memcpy (reference in doxygen) | 
|  | 43 | *     returns number of frames written | 
|  | 44 | * ssize_t audio_utils_fifo_reader::obtain | 
|  | 45 | *     Determines readable buffer section via pointer arithmetic on reader | 
|  | 46 | *     and writer pointers | 
|  | 47 | * | 
| Sanna Catherine de Treville Wager | 1bb6862 | 2017-06-14 14:18:31 -0700 | [diff] [blame] | 48 | * 2) Writing LOG_HIST_FLUSH event to console when audio is turned on or off | 
|  | 49 | *    When this event is found when reading from the buffer, all histograms are | 
|  | 50 | *    printed to the console | 
|  | 51 | *    TODO: remove this: always write data to another data structure or the console | 
|  | 52 | * FastMixer::onStateChange() | 
|  | 53 | *     is called when audio is turned on/off | 
|  | 54 | *     calls LOG_HIST_FLUSH() | 
|  | 55 | * LOG_HIST_FLUSH() | 
|  | 56 | *     calls logHistFlush | 
|  | 57 | * NBLog::Writer::logHistFlush | 
| Sanna Catherine de Treville Wager | 9484bae | 2017-06-15 14:39:44 -0700 | [diff] [blame^] | 58 | *     records current timestamp | 
| Sanna Catherine de Treville Wager | 1bb6862 | 2017-06-14 14:18:31 -0700 | [diff] [blame] | 59 | *     calls log(EVENT_HISTOGRAM_FLUSH) | 
|  | 60 | *     From here, everything is the same as in 1), resulting in call to fifo write | 
|  | 61 | * | 
|  | 62 | * 3) reading the data from shared memory | 
| Sanna Catherine de Treville Wager | 2d1631e | 2017-05-30 16:12:36 -0700 | [diff] [blame] | 63 | * Thread::threadloop() | 
|  | 64 | *     TODO: add description? | 
|  | 65 | * NBLog::MergeThread::threadLoop() | 
|  | 66 | *     calls NBLog::Merger::merge | 
|  | 67 | * NBLog::Merger::merge | 
| Sanna Catherine de Treville Wager | 1bb6862 | 2017-06-14 14:18:31 -0700 | [diff] [blame] | 68 | *     Merges snapshots sorted by timestamp | 
| Sanna Catherine de Treville Wager | 2d1631e | 2017-05-30 16:12:36 -0700 | [diff] [blame] | 69 | *     for each reader in vector of class NamedReader, | 
|  | 70 | *     callsNamedReader::reader()->getSnapshot | 
|  | 71 | *     TODO: check whether the rest of this function is relevant | 
|  | 72 | * NBLog::Reader::getSnapshot | 
|  | 73 | *     copies snapshot of reader's fifo buffer into its own buffer | 
|  | 74 | *     calls mFifoReader->obtain to find readable data | 
|  | 75 | *     sets snapshot.begin() and .end() iterators to boundaries of valid entries | 
|  | 76 | *     moves the fifo reader index to after the last entry read | 
| Sanna Catherine de Treville Wager | 1bb6862 | 2017-06-14 14:18:31 -0700 | [diff] [blame] | 77 | *     in this case, the buffer is in shared memory. in (4), the buffer is private | 
| Sanna Catherine de Treville Wager | 2d1631e | 2017-05-30 16:12:36 -0700 | [diff] [blame] | 78 | * | 
| Sanna Catherine de Treville Wager | 1bb6862 | 2017-06-14 14:18:31 -0700 | [diff] [blame] | 79 | * 4) reading the data from private buffer | 
| Sanna Catherine de Treville Wager | 2d1631e | 2017-05-30 16:12:36 -0700 | [diff] [blame] | 80 | * MediaLogService::dump | 
| Sanna Catherine de Treville Wager | 9484bae | 2017-06-15 14:39:44 -0700 | [diff] [blame^] | 81 | *     calls NBLog::Reader::dump(CONSOLE) | 
|  | 82 | *     The private buffer contains all logs for all readers in shared memory | 
| Sanna Catherine de Treville Wager | 2d1631e | 2017-05-30 16:12:36 -0700 | [diff] [blame] | 83 | * NBLog::Reader::dump(int) | 
|  | 84 | *     calls getSnapshot on the current reader | 
|  | 85 | *     calls dump(int, size_t, Snapshot) | 
|  | 86 | * NBLog::Reader::dump(int, size, snapshot) | 
|  | 87 | *     iterates through snapshot's events and switches based on their type | 
|  | 88 | *     (string, timestamp, etc...) | 
|  | 89 | *     In the case of EVENT_HISTOGRAM_ENTRY_TS, adds a list of timestamp sequences | 
|  | 90 | *     (histogram entry) to NBLog::mHists | 
|  | 91 | *     In the case of EVENT_HISTOGRAM_FLUSH, calls drawHistogram on each element in | 
|  | 92 | *     the list and erases it | 
| Sanna Catherine de Treville Wager | 1bb6862 | 2017-06-14 14:18:31 -0700 | [diff] [blame] | 93 | *     TODO: get rid of the FLUSH, instead add every HISTOGRAM_ENTRY_TS to two | 
|  | 94 | *     circular buffers: one short-term and one long-term (can add even longer-term | 
|  | 95 | *     structures in the future). When dump is called, print everything currently | 
|  | 96 | *     in the buffer. | 
| Sanna Catherine de Treville Wager | 2d1631e | 2017-05-30 16:12:36 -0700 | [diff] [blame] | 97 | * NBLog::drawHistogram | 
|  | 98 | *     input: timestamp array | 
|  | 99 | *     buckets this to a histogram and prints | 
|  | 100 | * | 
|  | 101 | */ | 
|  | 102 |  | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 103 | #define LOG_TAG "NBLog" | 
| Sanna Catherine de Treville Wager | 1bb6862 | 2017-06-14 14:18:31 -0700 | [diff] [blame] | 104 | // #define LOG_NDEBUG 0 | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 105 |  | 
| Sanna Catherine de Treville Wager | 697a8a5 | 2017-06-01 09:49:05 -0700 | [diff] [blame] | 106 | #include <algorithm> | 
| Nicolas Roulet | dcdfaec | 2017-02-14 10:18:39 -0800 | [diff] [blame] | 107 | #include <climits> | 
| Sanna Catherine de Treville Wager | 201079a | 2017-05-18 16:36:29 -0700 | [diff] [blame] | 108 | #include <deque> | 
| Sanna Catherine de Treville Wager | 697a8a5 | 2017-06-01 09:49:05 -0700 | [diff] [blame] | 109 | #include <fstream> | 
|  | 110 | // #include <inttypes.h> | 
|  | 111 | #include <iostream> | 
| Sanna Catherine de Treville Wager | cced674 | 2017-05-10 14:42:54 -0700 | [diff] [blame] | 112 | #include <math.h> | 
| Sanna Catherine de Treville Wager | 201079a | 2017-05-18 16:36:29 -0700 | [diff] [blame] | 113 | #include <numeric> | 
| Sanna Catherine de Treville Wager | 697a8a5 | 2017-06-01 09:49:05 -0700 | [diff] [blame] | 114 | #include <vector> | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 115 | #include <stdarg.h> | 
|  | 116 | #include <stdint.h> | 
|  | 117 | #include <stdio.h> | 
|  | 118 | #include <string.h> | 
| Nicolas Roulet | c20cb50 | 2017-02-01 12:35:24 -0800 | [diff] [blame] | 119 | #include <sys/prctl.h> | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 120 | #include <time.h> | 
|  | 121 | #include <new> | 
| Glenn Kasten | 535e161 | 2016-12-05 12:19:36 -0800 | [diff] [blame] | 122 | #include <audio_utils/roundup.h> | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 123 | #include <media/nbaio/NBLog.h> | 
| Sanna Catherine de Treville Wager | 2d1631e | 2017-05-30 16:12:36 -0700 | [diff] [blame] | 124 | // #include <utils/CallStack.h> // used to print callstack | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 125 | #include <utils/Log.h> | 
| Glenn Kasten | 4e01ef6 | 2013-07-11 14:29:59 -0700 | [diff] [blame] | 126 | #include <utils/String8.h> | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 127 |  | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 128 | #include <queue> | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 129 | #include <utility> | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 130 |  | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 131 | namespace android { | 
|  | 132 |  | 
| Sanna Catherine de Treville Wager | 2d1631e | 2017-05-30 16:12:36 -0700 | [diff] [blame] | 133 | int NBLog::Entry::copyEntryDataAt(size_t offset) const | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 134 | { | 
| Sanna Catherine de Treville Wager | 2d1631e | 2017-05-30 16:12:36 -0700 | [diff] [blame] | 135 | // FIXME This is too slow | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 136 | if (offset == 0) | 
|  | 137 | return mEvent; | 
|  | 138 | else if (offset == 1) | 
|  | 139 | return mLength; | 
|  | 140 | else if (offset < (size_t) (mLength + 2)) | 
|  | 141 | return ((char *) mData)[offset - 2]; | 
|  | 142 | else if (offset == (size_t) (mLength + 2)) | 
|  | 143 | return mLength; | 
|  | 144 | else | 
|  | 145 | return 0; | 
|  | 146 | } | 
|  | 147 |  | 
|  | 148 | // --------------------------------------------------------------------------- | 
|  | 149 |  | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 150 | /*static*/ | 
|  | 151 | std::unique_ptr<NBLog::AbstractEntry> NBLog::AbstractEntry::buildEntry(const uint8_t *ptr) { | 
| Sanna Catherine de Treville Wager | cced674 | 2017-05-10 14:42:54 -0700 | [diff] [blame] | 152 | const uint8_t type = EntryIterator(ptr)->type; | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 153 | switch (type) { | 
|  | 154 | case EVENT_START_FMT: | 
|  | 155 | return std::make_unique<FormatEntry>(FormatEntry(ptr)); | 
|  | 156 | case EVENT_HISTOGRAM_FLUSH: | 
|  | 157 | case EVENT_HISTOGRAM_ENTRY_TS: | 
|  | 158 | return std::make_unique<HistogramEntry>(HistogramEntry(ptr)); | 
|  | 159 | default: | 
|  | 160 | ALOGW("Tried to create AbstractEntry of type %d", type); | 
|  | 161 | return nullptr; | 
|  | 162 | } | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 163 | } | 
|  | 164 |  | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 165 | NBLog::AbstractEntry::AbstractEntry(const uint8_t *entry) : mEntry(entry) { | 
|  | 166 | } | 
|  | 167 |  | 
|  | 168 | // --------------------------------------------------------------------------- | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 169 |  | 
| Sanna Catherine de Treville Wager | dd92d7e | 2017-05-15 14:56:53 -0700 | [diff] [blame] | 170 | NBLog::EntryIterator NBLog::FormatEntry::begin() const { | 
|  | 171 | return EntryIterator(mEntry); | 
|  | 172 | } | 
|  | 173 |  | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 174 | const char *NBLog::FormatEntry::formatString() const { | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 175 | return (const char*) mEntry + offsetof(entry, data); | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 176 | } | 
|  | 177 |  | 
|  | 178 | size_t NBLog::FormatEntry::formatStringLength() const { | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 179 | return mEntry[offsetof(entry, length)]; | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 180 | } | 
|  | 181 |  | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 182 | NBLog::EntryIterator NBLog::FormatEntry::args() const { | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 183 | auto it = begin(); | 
| Nicolas Roulet | 1ca7512 | 2017-03-16 14:19:59 -0700 | [diff] [blame] | 184 | // skip start fmt | 
|  | 185 | ++it; | 
|  | 186 | // skip timestamp | 
|  | 187 | ++it; | 
| Nicolas Roulet | bd0c6b4 | 2017-03-16 13:54:23 -0700 | [diff] [blame] | 188 | // skip hash | 
|  | 189 | ++it; | 
| Nicolas Roulet | 1ca7512 | 2017-03-16 14:19:59 -0700 | [diff] [blame] | 190 | // Skip author if present | 
|  | 191 | if (it->type == EVENT_AUTHOR) { | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 192 | ++it; | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 193 | } | 
| Nicolas Roulet | 1ca7512 | 2017-03-16 14:19:59 -0700 | [diff] [blame] | 194 | return it; | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 195 | } | 
|  | 196 |  | 
| Nicolas Roulet | f42f156 | 2017-03-30 19:16:22 -0700 | [diff] [blame] | 197 | int64_t NBLog::FormatEntry::timestamp() const { | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 198 | auto it = begin(); | 
| Nicolas Roulet | 1ca7512 | 2017-03-16 14:19:59 -0700 | [diff] [blame] | 199 | // skip start fmt | 
|  | 200 | ++it; | 
| Nicolas Roulet | f42f156 | 2017-03-30 19:16:22 -0700 | [diff] [blame] | 201 | return it.payload<int64_t>(); | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 202 | } | 
|  | 203 |  | 
| Nicolas Roulet | bd0c6b4 | 2017-03-16 13:54:23 -0700 | [diff] [blame] | 204 | NBLog::log_hash_t NBLog::FormatEntry::hash() const { | 
|  | 205 | auto it = begin(); | 
|  | 206 | // skip start fmt | 
|  | 207 | ++it; | 
|  | 208 | // skip timestamp | 
|  | 209 | ++it; | 
|  | 210 | // unaligned 64-bit read not supported | 
|  | 211 | log_hash_t hash; | 
|  | 212 | memcpy(&hash, it->data, sizeof(hash)); | 
|  | 213 | return hash; | 
|  | 214 | } | 
|  | 215 |  | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 216 | int NBLog::FormatEntry::author() const { | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 217 | auto it = begin(); | 
| Nicolas Roulet | 1ca7512 | 2017-03-16 14:19:59 -0700 | [diff] [blame] | 218 | // skip start fmt | 
|  | 219 | ++it; | 
|  | 220 | // skip timestamp | 
|  | 221 | ++it; | 
| Nicolas Roulet | bd0c6b4 | 2017-03-16 13:54:23 -0700 | [diff] [blame] | 222 | // skip hash | 
|  | 223 | ++it; | 
| Nicolas Roulet | 1ca7512 | 2017-03-16 14:19:59 -0700 | [diff] [blame] | 224 | // if there is an author entry, return it, return -1 otherwise | 
|  | 225 | if (it->type == EVENT_AUTHOR) { | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 226 | return it.payload<int>(); | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 227 | } | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 228 | return -1; | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 229 | } | 
|  | 230 |  | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 231 | NBLog::EntryIterator NBLog::FormatEntry::copyWithAuthor( | 
| Nicolas Roulet | 6ea1d7e | 2017-02-14 16:17:31 -0800 | [diff] [blame] | 232 | std::unique_ptr<audio_utils_fifo_writer> &dst, int author) const { | 
|  | 233 | auto it = begin(); | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 234 | // copy fmt start entry | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 235 | it.copyTo(dst); | 
| Nicolas Roulet | 1ca7512 | 2017-03-16 14:19:59 -0700 | [diff] [blame] | 236 | // copy timestamp | 
| Sanna Catherine de Treville Wager | 2d1631e | 2017-05-30 16:12:36 -0700 | [diff] [blame] | 237 | (++it).copyTo(dst);    // copy hash | 
| Nicolas Roulet | bd0c6b4 | 2017-03-16 13:54:23 -0700 | [diff] [blame] | 238 | (++it).copyTo(dst); | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 239 | // insert author entry | 
|  | 240 | size_t authorEntrySize = NBLog::Entry::kOverhead + sizeof(author); | 
|  | 241 | uint8_t authorEntry[authorEntrySize]; | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 242 | authorEntry[offsetof(entry, type)] = EVENT_AUTHOR; | 
|  | 243 | authorEntry[offsetof(entry, length)] = | 
|  | 244 | authorEntry[authorEntrySize + NBLog::Entry::kPreviousLengthOffset] = | 
|  | 245 | sizeof(author); | 
|  | 246 | *(int*) (&authorEntry[offsetof(entry, data)]) = author; | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 247 | dst->write(authorEntry, authorEntrySize); | 
|  | 248 | // copy rest of entries | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 249 | while ((++it)->type != EVENT_END_FMT) { | 
|  | 250 | it.copyTo(dst); | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 251 | } | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 252 | it.copyTo(dst); | 
|  | 253 | ++it; | 
| Nicolas Roulet | 6ea1d7e | 2017-02-14 16:17:31 -0800 | [diff] [blame] | 254 | return it; | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 255 | } | 
|  | 256 |  | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 257 | void NBLog::EntryIterator::copyTo(std::unique_ptr<audio_utils_fifo_writer> &dst) const { | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 258 | size_t length = ptr[offsetof(entry, length)] + NBLog::Entry::kOverhead; | 
|  | 259 | dst->write(ptr, length); | 
|  | 260 | } | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 261 |  | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 262 | void NBLog::EntryIterator::copyData(uint8_t *dst) const { | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 263 | memcpy((void*) dst, ptr + offsetof(entry, data), ptr[offsetof(entry, length)]); | 
|  | 264 | } | 
|  | 265 |  | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 266 | NBLog::EntryIterator::EntryIterator() | 
| Nicolas Roulet | 6ea1d7e | 2017-02-14 16:17:31 -0800 | [diff] [blame] | 267 | : ptr(nullptr) {} | 
|  | 268 |  | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 269 | NBLog::EntryIterator::EntryIterator(const uint8_t *entry) | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 270 | : ptr(entry) {} | 
|  | 271 |  | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 272 | NBLog::EntryIterator::EntryIterator(const NBLog::EntryIterator &other) | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 273 | : ptr(other.ptr) {} | 
|  | 274 |  | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 275 | const NBLog::entry& NBLog::EntryIterator::operator*() const { | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 276 | return *(entry*) ptr; | 
|  | 277 | } | 
|  | 278 |  | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 279 | const NBLog::entry* NBLog::EntryIterator::operator->() const { | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 280 | return (entry*) ptr; | 
|  | 281 | } | 
|  | 282 |  | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 283 | NBLog::EntryIterator& NBLog::EntryIterator::operator++() { | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 284 | ptr += ptr[offsetof(entry, length)] + NBLog::Entry::kOverhead; | 
|  | 285 | return *this; | 
|  | 286 | } | 
|  | 287 |  | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 288 | NBLog::EntryIterator& NBLog::EntryIterator::operator--() { | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 289 | ptr -= ptr[NBLog::Entry::kPreviousLengthOffset] + NBLog::Entry::kOverhead; | 
|  | 290 | return *this; | 
|  | 291 | } | 
|  | 292 |  | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 293 | NBLog::EntryIterator NBLog::EntryIterator::next() const { | 
|  | 294 | EntryIterator aux(*this); | 
| Nicolas Roulet | 6ea1d7e | 2017-02-14 16:17:31 -0800 | [diff] [blame] | 295 | return ++aux; | 
|  | 296 | } | 
|  | 297 |  | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 298 | NBLog::EntryIterator NBLog::EntryIterator::prev() const { | 
|  | 299 | EntryIterator aux(*this); | 
| Nicolas Roulet | 6ea1d7e | 2017-02-14 16:17:31 -0800 | [diff] [blame] | 300 | return --aux; | 
|  | 301 | } | 
|  | 302 |  | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 303 | int NBLog::EntryIterator::operator-(const NBLog::EntryIterator &other) const { | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 304 | return ptr - other.ptr; | 
|  | 305 | } | 
|  | 306 |  | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 307 | bool NBLog::EntryIterator::operator!=(const EntryIterator &other) const { | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 308 | return ptr != other.ptr; | 
|  | 309 | } | 
|  | 310 |  | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 311 | bool NBLog::EntryIterator::hasConsistentLength() const { | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 312 | return ptr[offsetof(entry, length)] == ptr[ptr[offsetof(entry, length)] + | 
|  | 313 | NBLog::Entry::kOverhead + NBLog::Entry::kPreviousLengthOffset]; | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 314 | } | 
|  | 315 |  | 
|  | 316 | // --------------------------------------------------------------------------- | 
|  | 317 |  | 
| Nicolas Roulet | f42f156 | 2017-03-30 19:16:22 -0700 | [diff] [blame] | 318 | int64_t NBLog::HistogramEntry::timestamp() const { | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 319 | return EntryIterator(mEntry).payload<HistTsEntry>().ts; | 
|  | 320 | } | 
|  | 321 |  | 
|  | 322 | NBLog::log_hash_t NBLog::HistogramEntry::hash() const { | 
|  | 323 | return EntryIterator(mEntry).payload<HistTsEntry>().hash; | 
|  | 324 | } | 
|  | 325 |  | 
|  | 326 | int NBLog::HistogramEntry::author() const { | 
|  | 327 | EntryIterator it(mEntry); | 
|  | 328 | if (it->length == sizeof(HistTsEntryWithAuthor)) { | 
|  | 329 | return it.payload<HistTsEntryWithAuthor>().author; | 
|  | 330 | } else { | 
|  | 331 | return -1; | 
|  | 332 | } | 
|  | 333 | } | 
|  | 334 |  | 
|  | 335 | NBLog::EntryIterator NBLog::HistogramEntry::copyWithAuthor( | 
|  | 336 | std::unique_ptr<audio_utils_fifo_writer> &dst, int author) const { | 
|  | 337 | // Current histogram entry has {type, length, struct HistTsEntry, length}. | 
|  | 338 | // We now want {type, length, struct HistTsEntryWithAuthor, length} | 
|  | 339 | uint8_t buffer[Entry::kOverhead + sizeof(HistTsEntryWithAuthor)]; | 
|  | 340 | // Copy content until the point we want to add the author | 
|  | 341 | memcpy(buffer, mEntry, sizeof(entry) + sizeof(HistTsEntry)); | 
|  | 342 | // Copy the author | 
|  | 343 | *(int*) (buffer + sizeof(entry) + sizeof(HistTsEntry)) = author; | 
|  | 344 | // Update lengths | 
|  | 345 | buffer[offsetof(entry, length)] = sizeof(HistTsEntryWithAuthor); | 
|  | 346 | buffer[sizeof(buffer) + Entry::kPreviousLengthOffset] = sizeof(HistTsEntryWithAuthor); | 
|  | 347 | // Write new buffer into FIFO | 
|  | 348 | dst->write(buffer, sizeof(buffer)); | 
|  | 349 | return EntryIterator(mEntry).next(); | 
|  | 350 | } | 
|  | 351 |  | 
|  | 352 | // --------------------------------------------------------------------------- | 
|  | 353 |  | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 354 | #if 0   // FIXME see note in NBLog.h | 
|  | 355 | NBLog::Timeline::Timeline(size_t size, void *shared) | 
|  | 356 | : mSize(roundup(size)), mOwn(shared == NULL), | 
|  | 357 | mShared((Shared *) (mOwn ? new char[sharedSize(size)] : shared)) | 
|  | 358 | { | 
|  | 359 | new (mShared) Shared; | 
|  | 360 | } | 
|  | 361 |  | 
|  | 362 | NBLog::Timeline::~Timeline() | 
|  | 363 | { | 
|  | 364 | mShared->~Shared(); | 
|  | 365 | if (mOwn) { | 
|  | 366 | delete[] (char *) mShared; | 
|  | 367 | } | 
|  | 368 | } | 
|  | 369 | #endif | 
|  | 370 |  | 
|  | 371 | /*static*/ | 
|  | 372 | size_t NBLog::Timeline::sharedSize(size_t size) | 
|  | 373 | { | 
| Glenn Kasten | ed99c2b | 2016-12-12 08:31:24 -0800 | [diff] [blame] | 374 | // TODO fifo now supports non-power-of-2 buffer sizes, so could remove the roundup | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 375 | return sizeof(Shared) + roundup(size); | 
|  | 376 | } | 
|  | 377 |  | 
|  | 378 | // --------------------------------------------------------------------------- | 
|  | 379 |  | 
|  | 380 | NBLog::Writer::Writer() | 
| Nicolas Roulet | c20cb50 | 2017-02-01 12:35:24 -0800 | [diff] [blame] | 381 | : mShared(NULL), mFifo(NULL), mFifoWriter(NULL), mEnabled(false), mPidTag(NULL), mPidTagSize(0) | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 382 | { | 
|  | 383 | } | 
|  | 384 |  | 
| Glenn Kasten | 535e161 | 2016-12-05 12:19:36 -0800 | [diff] [blame] | 385 | NBLog::Writer::Writer(void *shared, size_t size) | 
|  | 386 | : mShared((Shared *) shared), | 
|  | 387 | mFifo(mShared != NULL ? | 
|  | 388 | new audio_utils_fifo(size, sizeof(uint8_t), | 
|  | 389 | mShared->mBuffer, mShared->mRear, NULL /*throttlesFront*/) : NULL), | 
|  | 390 | mFifoWriter(mFifo != NULL ? new audio_utils_fifo_writer(*mFifo) : NULL), | 
|  | 391 | mEnabled(mFifoWriter != NULL) | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 392 | { | 
| Nicolas Roulet | c20cb50 | 2017-02-01 12:35:24 -0800 | [diff] [blame] | 393 | // caching pid and process name | 
|  | 394 | pid_t id = ::getpid(); | 
|  | 395 | char procName[16]; | 
|  | 396 | int status = prctl(PR_GET_NAME, procName); | 
|  | 397 | if (status) {  // error getting process name | 
|  | 398 | procName[0] = '\0'; | 
|  | 399 | } | 
|  | 400 | size_t length = strlen(procName); | 
|  | 401 | mPidTagSize = length + sizeof(pid_t); | 
|  | 402 | mPidTag = new char[mPidTagSize]; | 
|  | 403 | memcpy(mPidTag, &id, sizeof(pid_t)); | 
|  | 404 | memcpy(mPidTag + sizeof(pid_t), procName, length); | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 405 | } | 
|  | 406 |  | 
| Glenn Kasten | 535e161 | 2016-12-05 12:19:36 -0800 | [diff] [blame] | 407 | NBLog::Writer::Writer(const sp<IMemory>& iMemory, size_t size) | 
|  | 408 | : Writer(iMemory != 0 ? (Shared *) iMemory->pointer() : NULL, size) | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 409 | { | 
| Glenn Kasten | 535e161 | 2016-12-05 12:19:36 -0800 | [diff] [blame] | 410 | mIMemory = iMemory; | 
|  | 411 | } | 
|  | 412 |  | 
|  | 413 | NBLog::Writer::~Writer() | 
|  | 414 | { | 
|  | 415 | delete mFifoWriter; | 
|  | 416 | delete mFifo; | 
| Nicolas Roulet | c20cb50 | 2017-02-01 12:35:24 -0800 | [diff] [blame] | 417 | delete[] mPidTag; | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 418 | } | 
|  | 419 |  | 
|  | 420 | void NBLog::Writer::log(const char *string) | 
|  | 421 | { | 
|  | 422 | if (!mEnabled) { | 
|  | 423 | return; | 
|  | 424 | } | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 425 | LOG_ALWAYS_FATAL_IF(string == NULL, "Attempted to log NULL string"); | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 426 | size_t length = strlen(string); | 
| Glenn Kasten | 535e161 | 2016-12-05 12:19:36 -0800 | [diff] [blame] | 427 | if (length > Entry::kMaxLength) { | 
|  | 428 | length = Entry::kMaxLength; | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 429 | } | 
|  | 430 | log(EVENT_STRING, string, length); | 
|  | 431 | } | 
|  | 432 |  | 
|  | 433 | void NBLog::Writer::logf(const char *fmt, ...) | 
|  | 434 | { | 
|  | 435 | if (!mEnabled) { | 
|  | 436 | return; | 
|  | 437 | } | 
|  | 438 | va_list ap; | 
|  | 439 | va_start(ap, fmt); | 
|  | 440 | Writer::logvf(fmt, ap);     // the Writer:: is needed to avoid virtual dispatch for LockedWriter | 
|  | 441 | va_end(ap); | 
|  | 442 | } | 
|  | 443 |  | 
|  | 444 | void NBLog::Writer::logvf(const char *fmt, va_list ap) | 
|  | 445 | { | 
|  | 446 | if (!mEnabled) { | 
|  | 447 | return; | 
|  | 448 | } | 
| Glenn Kasten | 535e161 | 2016-12-05 12:19:36 -0800 | [diff] [blame] | 449 | char buffer[Entry::kMaxLength + 1 /*NUL*/]; | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 450 | int length = vsnprintf(buffer, sizeof(buffer), fmt, ap); | 
|  | 451 | if (length >= (int) sizeof(buffer)) { | 
|  | 452 | length = sizeof(buffer) - 1; | 
|  | 453 | // NUL termination is not required | 
|  | 454 | // buffer[length] = '\0'; | 
|  | 455 | } | 
|  | 456 | if (length >= 0) { | 
|  | 457 | log(EVENT_STRING, buffer, length); | 
|  | 458 | } | 
|  | 459 | } | 
|  | 460 |  | 
|  | 461 | void NBLog::Writer::logTimestamp() | 
|  | 462 | { | 
|  | 463 | if (!mEnabled) { | 
|  | 464 | return; | 
|  | 465 | } | 
| Nicolas Roulet | f42f156 | 2017-03-30 19:16:22 -0700 | [diff] [blame] | 466 | int64_t ts = get_monotonic_ns(); | 
|  | 467 | if (ts > 0) { | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 468 | log(EVENT_TIMESTAMP, &ts, sizeof(ts)); | 
| Nicolas Roulet | f42f156 | 2017-03-30 19:16:22 -0700 | [diff] [blame] | 469 | } else { | 
|  | 470 | ALOGE("Failed to get timestamp"); | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 471 | } | 
|  | 472 | } | 
|  | 473 |  | 
| Nicolas Roulet | f42f156 | 2017-03-30 19:16:22 -0700 | [diff] [blame] | 474 | void NBLog::Writer::logTimestamp(const int64_t ts) | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 475 | { | 
|  | 476 | if (!mEnabled) { | 
|  | 477 | return; | 
|  | 478 | } | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 479 | log(EVENT_TIMESTAMP, &ts, sizeof(ts)); | 
|  | 480 | } | 
|  | 481 |  | 
|  | 482 | void NBLog::Writer::logInteger(const int x) | 
|  | 483 | { | 
|  | 484 | if (!mEnabled) { | 
|  | 485 | return; | 
|  | 486 | } | 
|  | 487 | log(EVENT_INTEGER, &x, sizeof(x)); | 
|  | 488 | } | 
|  | 489 |  | 
|  | 490 | void NBLog::Writer::logFloat(const float x) | 
|  | 491 | { | 
|  | 492 | if (!mEnabled) { | 
|  | 493 | return; | 
|  | 494 | } | 
|  | 495 | log(EVENT_FLOAT, &x, sizeof(x)); | 
|  | 496 | } | 
|  | 497 |  | 
|  | 498 | void NBLog::Writer::logPID() | 
|  | 499 | { | 
|  | 500 | if (!mEnabled) { | 
|  | 501 | return; | 
|  | 502 | } | 
| Nicolas Roulet | c20cb50 | 2017-02-01 12:35:24 -0800 | [diff] [blame] | 503 | log(EVENT_PID, mPidTag, mPidTagSize); | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 504 | } | 
|  | 505 |  | 
|  | 506 | void NBLog::Writer::logStart(const char *fmt) | 
|  | 507 | { | 
|  | 508 | if (!mEnabled) { | 
|  | 509 | return; | 
|  | 510 | } | 
|  | 511 | size_t length = strlen(fmt); | 
|  | 512 | if (length > Entry::kMaxLength) { | 
|  | 513 | length = Entry::kMaxLength; | 
|  | 514 | } | 
|  | 515 | log(EVENT_START_FMT, fmt, length); | 
|  | 516 | } | 
|  | 517 |  | 
|  | 518 | void NBLog::Writer::logEnd() | 
|  | 519 | { | 
|  | 520 | if (!mEnabled) { | 
|  | 521 | return; | 
|  | 522 | } | 
|  | 523 | Entry entry = Entry(EVENT_END_FMT, NULL, 0); | 
|  | 524 | log(&entry, true); | 
|  | 525 | } | 
|  | 526 |  | 
| Nicolas Roulet | bd0c6b4 | 2017-03-16 13:54:23 -0700 | [diff] [blame] | 527 | void NBLog::Writer::logHash(log_hash_t hash) | 
|  | 528 | { | 
|  | 529 | if (!mEnabled) { | 
|  | 530 | return; | 
|  | 531 | } | 
|  | 532 | log(EVENT_HASH, &hash, sizeof(hash)); | 
|  | 533 | } | 
|  | 534 |  | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 535 | void NBLog::Writer::logHistTS(log_hash_t hash) | 
|  | 536 | { | 
|  | 537 | if (!mEnabled) { | 
|  | 538 | return; | 
|  | 539 | } | 
|  | 540 | HistTsEntry data; | 
|  | 541 | data.hash = hash; | 
| Nicolas Roulet | f42f156 | 2017-03-30 19:16:22 -0700 | [diff] [blame] | 542 | data.ts = get_monotonic_ns(); | 
|  | 543 | if (data.ts > 0) { | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 544 | log(EVENT_HISTOGRAM_ENTRY_TS, &data, sizeof(data)); | 
|  | 545 | } else { | 
| Nicolas Roulet | f42f156 | 2017-03-30 19:16:22 -0700 | [diff] [blame] | 546 | ALOGE("Failed to get timestamp"); | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 547 | } | 
|  | 548 | } | 
|  | 549 |  | 
|  | 550 | void NBLog::Writer::logHistFlush(log_hash_t hash) | 
|  | 551 | { | 
|  | 552 | if (!mEnabled) { | 
|  | 553 | return; | 
|  | 554 | } | 
|  | 555 | HistTsEntry data; | 
|  | 556 | data.hash = hash; | 
| Nicolas Roulet | f42f156 | 2017-03-30 19:16:22 -0700 | [diff] [blame] | 557 | data.ts = get_monotonic_ns(); | 
|  | 558 | if (data.ts > 0) { | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 559 | log(EVENT_HISTOGRAM_FLUSH, &data, sizeof(data)); | 
|  | 560 | } else { | 
| Nicolas Roulet | f42f156 | 2017-03-30 19:16:22 -0700 | [diff] [blame] | 561 | ALOGE("Failed to get timestamp"); | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 562 | } | 
|  | 563 | } | 
|  | 564 |  | 
| Nicolas Roulet | bd0c6b4 | 2017-03-16 13:54:23 -0700 | [diff] [blame] | 565 | void NBLog::Writer::logFormat(const char *fmt, log_hash_t hash, ...) | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 566 | { | 
|  | 567 | if (!mEnabled) { | 
|  | 568 | return; | 
|  | 569 | } | 
|  | 570 |  | 
|  | 571 | va_list ap; | 
| Nicolas Roulet | bd0c6b4 | 2017-03-16 13:54:23 -0700 | [diff] [blame] | 572 | va_start(ap, hash); | 
|  | 573 | Writer::logVFormat(fmt, hash, ap); | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 574 | va_end(ap); | 
|  | 575 | } | 
|  | 576 |  | 
| Nicolas Roulet | bd0c6b4 | 2017-03-16 13:54:23 -0700 | [diff] [blame] | 577 | void NBLog::Writer::logVFormat(const char *fmt, log_hash_t hash, va_list argp) | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 578 | { | 
|  | 579 | if (!mEnabled) { | 
|  | 580 | return; | 
|  | 581 | } | 
|  | 582 | Writer::logStart(fmt); | 
|  | 583 | int i; | 
|  | 584 | double f; | 
|  | 585 | char* s; | 
| Nicolas Roulet | f42f156 | 2017-03-30 19:16:22 -0700 | [diff] [blame] | 586 | int64_t t; | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 587 | Writer::logTimestamp(); | 
| Nicolas Roulet | bd0c6b4 | 2017-03-16 13:54:23 -0700 | [diff] [blame] | 588 | Writer::logHash(hash); | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 589 | for (const char *p = fmt; *p != '\0'; p++) { | 
|  | 590 | // TODO: implement more complex formatting such as %.3f | 
|  | 591 | if (*p != '%') { | 
|  | 592 | continue; | 
|  | 593 | } | 
|  | 594 | switch(*++p) { | 
|  | 595 | case 's': // string | 
|  | 596 | s = va_arg(argp, char *); | 
|  | 597 | Writer::log(s); | 
|  | 598 | break; | 
|  | 599 |  | 
|  | 600 | case 't': // timestamp | 
| Nicolas Roulet | f42f156 | 2017-03-30 19:16:22 -0700 | [diff] [blame] | 601 | t = va_arg(argp, int64_t); | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 602 | Writer::logTimestamp(t); | 
|  | 603 | break; | 
|  | 604 |  | 
|  | 605 | case 'd': // integer | 
|  | 606 | i = va_arg(argp, int); | 
|  | 607 | Writer::logInteger(i); | 
|  | 608 | break; | 
|  | 609 |  | 
|  | 610 | case 'f': // float | 
|  | 611 | f = va_arg(argp, double); // float arguments are promoted to double in vararg lists | 
|  | 612 | Writer::logFloat((float)f); | 
|  | 613 | break; | 
|  | 614 |  | 
|  | 615 | case 'p': // pid | 
|  | 616 | Writer::logPID(); | 
|  | 617 | break; | 
|  | 618 |  | 
|  | 619 | // the "%\0" case finishes parsing | 
|  | 620 | case '\0': | 
|  | 621 | --p; | 
|  | 622 | break; | 
|  | 623 |  | 
| Nicolas Roulet | c20cb50 | 2017-02-01 12:35:24 -0800 | [diff] [blame] | 624 | case '%': | 
|  | 625 | break; | 
|  | 626 |  | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 627 | default: | 
|  | 628 | ALOGW("NBLog Writer parsed invalid format specifier: %c", *p); | 
|  | 629 | break; | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 630 | } | 
|  | 631 | } | 
|  | 632 | Writer::logEnd(); | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 633 | } | 
|  | 634 |  | 
|  | 635 | void NBLog::Writer::log(Event event, const void *data, size_t length) | 
|  | 636 | { | 
|  | 637 | if (!mEnabled) { | 
|  | 638 | return; | 
|  | 639 | } | 
| Glenn Kasten | 535e161 | 2016-12-05 12:19:36 -0800 | [diff] [blame] | 640 | if (data == NULL || length > Entry::kMaxLength) { | 
|  | 641 | // TODO Perhaps it makes sense to display truncated data or at least a | 
|  | 642 | //      message that the data is too long?  The current behavior can create | 
|  | 643 | //      a confusion for a programmer debugging their code. | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 644 | return; | 
|  | 645 | } | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 646 | // Ignore if invalid event | 
|  | 647 | if (event == EVENT_RESERVED || event >= EVENT_UPPER_BOUND) { | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 648 | return; | 
|  | 649 | } | 
| Sanna Catherine de Treville Wager | 2d1631e | 2017-05-30 16:12:36 -0700 | [diff] [blame] | 650 | Entry etr(event, data, length); | 
|  | 651 | log(&etr, true /*trusted*/); | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 652 | } | 
|  | 653 |  | 
| Sanna Catherine de Treville Wager | 2d1631e | 2017-05-30 16:12:36 -0700 | [diff] [blame] | 654 | void NBLog::Writer::log(const NBLog::Entry *etr, bool trusted) | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 655 | { | 
|  | 656 | if (!mEnabled) { | 
|  | 657 | return; | 
|  | 658 | } | 
|  | 659 | if (!trusted) { | 
| Sanna Catherine de Treville Wager | 2d1631e | 2017-05-30 16:12:36 -0700 | [diff] [blame] | 660 | log(etr->mEvent, etr->mData, etr->mLength); | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 661 | return; | 
|  | 662 | } | 
| Sanna Catherine de Treville Wager | 2d1631e | 2017-05-30 16:12:36 -0700 | [diff] [blame] | 663 | size_t need = etr->mLength + Entry::kOverhead;    // mEvent, mLength, data[mLength], mLength | 
|  | 664 | // need = number of bytes written to FIFO | 
| Glenn Kasten | 535e161 | 2016-12-05 12:19:36 -0800 | [diff] [blame] | 665 |  | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 666 | // FIXME optimize this using memcpy for the data part of the Entry. | 
|  | 667 | // The Entry could have a method copyTo(ptr, offset, size) to optimize the copy. | 
| Sanna Catherine de Treville Wager | 2d1631e | 2017-05-30 16:12:36 -0700 | [diff] [blame] | 668 | // checks size of a single log Entry: type, length, data pointer and ending | 
| Glenn Kasten | 535e161 | 2016-12-05 12:19:36 -0800 | [diff] [blame] | 669 | uint8_t temp[Entry::kMaxLength + Entry::kOverhead]; | 
| Sanna Catherine de Treville Wager | 2d1631e | 2017-05-30 16:12:36 -0700 | [diff] [blame] | 670 | // write this data to temp array | 
| Glenn Kasten | 535e161 | 2016-12-05 12:19:36 -0800 | [diff] [blame] | 671 | for (size_t i = 0; i < need; i++) { | 
| Sanna Catherine de Treville Wager | 2d1631e | 2017-05-30 16:12:36 -0700 | [diff] [blame] | 672 | temp[i] = etr->copyEntryDataAt(i); | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 673 | } | 
| Sanna Catherine de Treville Wager | 2d1631e | 2017-05-30 16:12:36 -0700 | [diff] [blame] | 674 | // write to circular buffer | 
| Glenn Kasten | 535e161 | 2016-12-05 12:19:36 -0800 | [diff] [blame] | 675 | mFifoWriter->write(temp, need); | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 676 | } | 
|  | 677 |  | 
|  | 678 | bool NBLog::Writer::isEnabled() const | 
|  | 679 | { | 
|  | 680 | return mEnabled; | 
|  | 681 | } | 
|  | 682 |  | 
|  | 683 | bool NBLog::Writer::setEnabled(bool enabled) | 
|  | 684 | { | 
|  | 685 | bool old = mEnabled; | 
|  | 686 | mEnabled = enabled && mShared != NULL; | 
|  | 687 | return old; | 
|  | 688 | } | 
|  | 689 |  | 
|  | 690 | // --------------------------------------------------------------------------- | 
|  | 691 |  | 
|  | 692 | NBLog::LockedWriter::LockedWriter() | 
|  | 693 | : Writer() | 
|  | 694 | { | 
|  | 695 | } | 
|  | 696 |  | 
| Glenn Kasten | 535e161 | 2016-12-05 12:19:36 -0800 | [diff] [blame] | 697 | NBLog::LockedWriter::LockedWriter(void *shared, size_t size) | 
|  | 698 | : Writer(shared, size) | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 699 | { | 
|  | 700 | } | 
|  | 701 |  | 
|  | 702 | void NBLog::LockedWriter::log(const char *string) | 
|  | 703 | { | 
|  | 704 | Mutex::Autolock _l(mLock); | 
|  | 705 | Writer::log(string); | 
|  | 706 | } | 
|  | 707 |  | 
|  | 708 | void NBLog::LockedWriter::logf(const char *fmt, ...) | 
|  | 709 | { | 
|  | 710 | // FIXME should not take the lock until after formatting is done | 
|  | 711 | Mutex::Autolock _l(mLock); | 
|  | 712 | va_list ap; | 
|  | 713 | va_start(ap, fmt); | 
|  | 714 | Writer::logvf(fmt, ap); | 
|  | 715 | va_end(ap); | 
|  | 716 | } | 
|  | 717 |  | 
|  | 718 | void NBLog::LockedWriter::logvf(const char *fmt, va_list ap) | 
|  | 719 | { | 
|  | 720 | // FIXME should not take the lock until after formatting is done | 
|  | 721 | Mutex::Autolock _l(mLock); | 
|  | 722 | Writer::logvf(fmt, ap); | 
|  | 723 | } | 
|  | 724 |  | 
|  | 725 | void NBLog::LockedWriter::logTimestamp() | 
|  | 726 | { | 
|  | 727 | // FIXME should not take the lock until after the clock_gettime() syscall | 
|  | 728 | Mutex::Autolock _l(mLock); | 
|  | 729 | Writer::logTimestamp(); | 
|  | 730 | } | 
|  | 731 |  | 
| Nicolas Roulet | f42f156 | 2017-03-30 19:16:22 -0700 | [diff] [blame] | 732 | void NBLog::LockedWriter::logTimestamp(const int64_t ts) | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 733 | { | 
|  | 734 | Mutex::Autolock _l(mLock); | 
|  | 735 | Writer::logTimestamp(ts); | 
|  | 736 | } | 
|  | 737 |  | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 738 | void NBLog::LockedWriter::logInteger(const int x) | 
|  | 739 | { | 
|  | 740 | Mutex::Autolock _l(mLock); | 
|  | 741 | Writer::logInteger(x); | 
|  | 742 | } | 
|  | 743 |  | 
|  | 744 | void NBLog::LockedWriter::logFloat(const float x) | 
|  | 745 | { | 
|  | 746 | Mutex::Autolock _l(mLock); | 
|  | 747 | Writer::logFloat(x); | 
|  | 748 | } | 
|  | 749 |  | 
|  | 750 | void NBLog::LockedWriter::logPID() | 
|  | 751 | { | 
|  | 752 | Mutex::Autolock _l(mLock); | 
|  | 753 | Writer::logPID(); | 
|  | 754 | } | 
|  | 755 |  | 
|  | 756 | void NBLog::LockedWriter::logStart(const char *fmt) | 
|  | 757 | { | 
|  | 758 | Mutex::Autolock _l(mLock); | 
|  | 759 | Writer::logStart(fmt); | 
|  | 760 | } | 
|  | 761 |  | 
|  | 762 |  | 
|  | 763 | void NBLog::LockedWriter::logEnd() | 
|  | 764 | { | 
|  | 765 | Mutex::Autolock _l(mLock); | 
|  | 766 | Writer::logEnd(); | 
|  | 767 | } | 
|  | 768 |  | 
| Nicolas Roulet | bd0c6b4 | 2017-03-16 13:54:23 -0700 | [diff] [blame] | 769 | void NBLog::LockedWriter::logHash(log_hash_t hash) | 
|  | 770 | { | 
|  | 771 | Mutex::Autolock _l(mLock); | 
|  | 772 | Writer::logHash(hash); | 
|  | 773 | } | 
|  | 774 |  | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 775 | bool NBLog::LockedWriter::isEnabled() const | 
|  | 776 | { | 
|  | 777 | Mutex::Autolock _l(mLock); | 
|  | 778 | return Writer::isEnabled(); | 
|  | 779 | } | 
|  | 780 |  | 
|  | 781 | bool NBLog::LockedWriter::setEnabled(bool enabled) | 
|  | 782 | { | 
|  | 783 | Mutex::Autolock _l(mLock); | 
|  | 784 | return Writer::setEnabled(enabled); | 
|  | 785 | } | 
|  | 786 |  | 
|  | 787 | // --------------------------------------------------------------------------- | 
|  | 788 |  | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 789 | const std::set<NBLog::Event> NBLog::Reader::startingTypes {NBLog::Event::EVENT_START_FMT, | 
|  | 790 | NBLog::Event::EVENT_HISTOGRAM_ENTRY_TS}; | 
|  | 791 | const std::set<NBLog::Event> NBLog::Reader::endingTypes   {NBLog::Event::EVENT_END_FMT, | 
|  | 792 | NBLog::Event::EVENT_HISTOGRAM_ENTRY_TS, | 
|  | 793 | NBLog::Event::EVENT_HISTOGRAM_FLUSH}; | 
| Glenn Kasten | 535e161 | 2016-12-05 12:19:36 -0800 | [diff] [blame] | 794 | NBLog::Reader::Reader(const void *shared, size_t size) | 
|  | 795 | : mShared((/*const*/ Shared *) shared), /*mIMemory*/ | 
|  | 796 | mFd(-1), mIndent(0), | 
|  | 797 | mFifo(mShared != NULL ? | 
|  | 798 | new audio_utils_fifo(size, sizeof(uint8_t), | 
|  | 799 | mShared->mBuffer, mShared->mRear, NULL /*throttlesFront*/) : NULL), | 
| Sanna Catherine de Treville Wager | 201079a | 2017-05-18 16:36:29 -0700 | [diff] [blame] | 800 | mFifoReader(mFifo != NULL ? new audio_utils_fifo_reader(*mFifo) : NULL), | 
|  | 801 | findGlitch(false) | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 802 | { | 
|  | 803 | } | 
|  | 804 |  | 
| Glenn Kasten | 535e161 | 2016-12-05 12:19:36 -0800 | [diff] [blame] | 805 | NBLog::Reader::Reader(const sp<IMemory>& iMemory, size_t size) | 
|  | 806 | : Reader(iMemory != 0 ? (Shared *) iMemory->pointer() : NULL, size) | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 807 | { | 
| Glenn Kasten | 535e161 | 2016-12-05 12:19:36 -0800 | [diff] [blame] | 808 | mIMemory = iMemory; | 
|  | 809 | } | 
|  | 810 |  | 
|  | 811 | NBLog::Reader::~Reader() | 
|  | 812 | { | 
|  | 813 | delete mFifoReader; | 
|  | 814 | delete mFifo; | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 815 | } | 
|  | 816 |  | 
| Sanna Catherine de Treville Wager | 201079a | 2017-05-18 16:36:29 -0700 | [diff] [blame] | 817 | inline static int deltaMs(int64_t ns1, int64_t ns2) { | 
|  | 818 | return (ns2 - ns1) / (1000 * 1000); | 
|  | 819 | } | 
|  | 820 |  | 
|  | 821 | // Produces a log warning if the timing of recent buffer periods caused a glitch | 
|  | 822 | // Computes sum of running window of three buffer periods | 
|  | 823 | // Checks whether the buffer periods leave enough CPU time for the next one | 
|  | 824 | // e.g. if a buffer period is expected to be 4 ms and a buffer requires 3 ms of CPU time, | 
|  | 825 | // here are some glitch cases: | 
|  | 826 | // 4 + 4 + 6 ; 5 + 4 + 5; 2 + 2 + 10 | 
|  | 827 | // TODO: develop this code to track changes in histogram distribution in addition | 
|  | 828 | // to / instead of glitches | 
|  | 829 | void NBLog::Reader::alertIfGlitch(const std::vector<int64_t> &samples) { | 
|  | 830 | //TODO: measure kPeriodLen and kRatio from the data as they may change. | 
|  | 831 | static const int kPeriodLen = 4; // current period length is ideally 4 ms | 
|  | 832 | static const double kRatio = 0.75; // estimate of CPU time as ratio of period length | 
|  | 833 | // DAC processing time for 4 ms buffer | 
|  | 834 | static const int kPeriodTime = static_cast<int>(round(kPeriodLen * kRatio)); | 
|  | 835 | static const int kNumBuff = 3; // number of buffers considered in local history | 
|  | 836 | std::deque<int> periods(kNumBuff, kPeriodLen); | 
|  | 837 | for (size_t i = 2; i < samples.size(); ++i) { // skip first time entry | 
|  | 838 | periods.push_front(deltaMs(samples[i - 1], samples[i])); | 
|  | 839 | periods.pop_back(); | 
|  | 840 | // TODO: check that all glitch cases are covered | 
|  | 841 | if (std::accumulate(periods.begin(), periods.end(), 0) > kNumBuff * kPeriodLen + | 
|  | 842 | kPeriodLen - kPeriodTime) { | 
|  | 843 | ALOGW("A glitch occurred"); | 
|  | 844 | periods.assign(kNumBuff, kPeriodLen); | 
|  | 845 | } | 
|  | 846 | } | 
|  | 847 | return; | 
|  | 848 | } | 
|  | 849 |  | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 850 | const uint8_t *NBLog::Reader::findLastEntryOfTypes(const uint8_t *front, const uint8_t *back, | 
|  | 851 | const std::set<Event> &types) { | 
| Nicolas Roulet | 6ea1d7e | 2017-02-14 16:17:31 -0800 | [diff] [blame] | 852 | while (back + Entry::kPreviousLengthOffset >= front) { | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 853 | const uint8_t *prev = back - back[Entry::kPreviousLengthOffset] - Entry::kOverhead; | 
|  | 854 | if (prev < front || prev + prev[offsetof(entry, length)] + | 
| Nicolas Roulet | 6ea1d7e | 2017-02-14 16:17:31 -0800 | [diff] [blame] | 855 | Entry::kOverhead != back) { | 
|  | 856 |  | 
|  | 857 | // prev points to an out of limits or inconsistent entry | 
|  | 858 | return nullptr; | 
|  | 859 | } | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 860 | if (types.find((const Event) prev[offsetof(entry, type)]) != types.end()) { | 
| Nicolas Roulet | 6ea1d7e | 2017-02-14 16:17:31 -0800 | [diff] [blame] | 861 | return prev; | 
|  | 862 | } | 
|  | 863 | back = prev; | 
|  | 864 | } | 
|  | 865 | return nullptr; // no entry found | 
|  | 866 | } | 
|  | 867 |  | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 868 | std::unique_ptr<NBLog::Reader::Snapshot> NBLog::Reader::getSnapshot() | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 869 | { | 
| Glenn Kasten | 535e161 | 2016-12-05 12:19:36 -0800 | [diff] [blame] | 870 | if (mFifoReader == NULL) { | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 871 | return std::unique_ptr<NBLog::Reader::Snapshot>(new Snapshot()); | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 872 | } | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 873 | // make a copy to avoid race condition with writer | 
| Glenn Kasten | 535e161 | 2016-12-05 12:19:36 -0800 | [diff] [blame] | 874 | size_t capacity = mFifo->capacity(); | 
|  | 875 |  | 
| Nicolas Roulet | 6ea1d7e | 2017-02-14 16:17:31 -0800 | [diff] [blame] | 876 | // This emulates the behaviour of audio_utils_fifo_reader::read, but without incrementing the | 
|  | 877 | // reader index. The index is incremented after handling corruption, to after the last complete | 
|  | 878 | // entry of the buffer | 
|  | 879 | size_t lost; | 
|  | 880 | audio_utils_iovec iovec[2]; | 
|  | 881 | ssize_t availToRead = mFifoReader->obtain(iovec, capacity, NULL /*timeout*/, &lost); | 
|  | 882 | if (availToRead <= 0) { | 
|  | 883 | return std::unique_ptr<NBLog::Reader::Snapshot>(new Snapshot()); | 
|  | 884 | } | 
| Glenn Kasten | 535e161 | 2016-12-05 12:19:36 -0800 | [diff] [blame] | 885 |  | 
| Nicolas Roulet | 6ea1d7e | 2017-02-14 16:17:31 -0800 | [diff] [blame] | 886 | std::unique_ptr<Snapshot> snapshot(new Snapshot(availToRead)); | 
|  | 887 | memcpy(snapshot->mData, (const char *) mFifo->buffer() + iovec[0].mOffset, iovec[0].mLength); | 
|  | 888 | if (iovec[1].mLength > 0) { | 
|  | 889 | memcpy(snapshot->mData + (iovec[0].mLength), | 
|  | 890 | (const char *) mFifo->buffer() + iovec[1].mOffset, iovec[1].mLength); | 
|  | 891 | } | 
|  | 892 |  | 
|  | 893 | // Handle corrupted buffer | 
|  | 894 | // Potentially, a buffer has corrupted data on both beginning (due to overflow) and end | 
|  | 895 | // (due to incomplete format entry). But even if the end format entry is incomplete, | 
|  | 896 | // it ends in a complete entry (which is not an END_FMT). So is safe to traverse backwards. | 
|  | 897 | // TODO: handle client corruption (in the middle of a buffer) | 
|  | 898 |  | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 899 | const uint8_t *back = snapshot->mData + availToRead; | 
|  | 900 | const uint8_t *front = snapshot->mData; | 
| Nicolas Roulet | 6ea1d7e | 2017-02-14 16:17:31 -0800 | [diff] [blame] | 901 |  | 
|  | 902 | // Find last END_FMT. <back> is sitting on an entry which might be the middle of a FormatEntry. | 
|  | 903 | // We go backwards until we find an EVENT_END_FMT. | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 904 | const uint8_t *lastEnd = findLastEntryOfTypes(front, back, endingTypes); | 
| Nicolas Roulet | 6ea1d7e | 2017-02-14 16:17:31 -0800 | [diff] [blame] | 905 | if (lastEnd == nullptr) { | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 906 | snapshot->mEnd = snapshot->mBegin = EntryIterator(front); | 
| Nicolas Roulet | 6ea1d7e | 2017-02-14 16:17:31 -0800 | [diff] [blame] | 907 | } else { | 
|  | 908 | // end of snapshot points to after last END_FMT entry | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 909 | snapshot->mEnd = EntryIterator(lastEnd).next(); | 
| Nicolas Roulet | 6ea1d7e | 2017-02-14 16:17:31 -0800 | [diff] [blame] | 910 | // find first START_FMT | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 911 | const uint8_t *firstStart = nullptr; | 
|  | 912 | const uint8_t *firstStartTmp = snapshot->mEnd; | 
|  | 913 | while ((firstStartTmp = findLastEntryOfTypes(front, firstStartTmp, startingTypes)) | 
| Nicolas Roulet | 6ea1d7e | 2017-02-14 16:17:31 -0800 | [diff] [blame] | 914 | != nullptr) { | 
|  | 915 | firstStart = firstStartTmp; | 
|  | 916 | } | 
|  | 917 | // firstStart is null if no START_FMT entry was found before lastEnd | 
|  | 918 | if (firstStart == nullptr) { | 
|  | 919 | snapshot->mBegin = snapshot->mEnd; | 
|  | 920 | } else { | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 921 | snapshot->mBegin = EntryIterator(firstStart); | 
| Nicolas Roulet | 6ea1d7e | 2017-02-14 16:17:31 -0800 | [diff] [blame] | 922 | } | 
|  | 923 | } | 
|  | 924 |  | 
|  | 925 | // advance fifo reader index to after last entry read. | 
|  | 926 | mFifoReader->release(snapshot->mEnd - front); | 
|  | 927 |  | 
|  | 928 | snapshot->mLost = lost; | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 929 | return snapshot; | 
| Nicolas Roulet | 6ea1d7e | 2017-02-14 16:17:31 -0800 | [diff] [blame] | 930 |  | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 931 | } | 
|  | 932 |  | 
| Sanna Catherine de Treville Wager | 697a8a5 | 2017-06-01 09:49:05 -0700 | [diff] [blame] | 933 | // writes sample deltas to file, either truncating or appending | 
|  | 934 | inline void writeHistToFile(const std::vector<int64_t> &samples, bool append) { | 
|  | 935 | // name of file on audioserver | 
|  | 936 | static const char* const kName = (char *)"/data/misc/audioserver/sample_results.txt"; | 
|  | 937 | // stores deltas between the samples | 
|  | 938 | std::vector<int64_t> intervals; | 
|  | 939 | if (samples.size() == 0) return; | 
|  | 940 | for (size_t i = 1; i < samples.size(); ++i) { | 
|  | 941 | intervals.push_back(deltaMs(samples[i - 1], samples[i])); | 
|  | 942 | } | 
|  | 943 | // Deletes maximum value in a histogram. Temp quick fix. | 
|  | 944 | // FIXME: need to find root cause of approx. 35th element from the end | 
|  | 945 | // consistently being an outlier in the first histogram of a flush | 
|  | 946 | // ALOGW("%" PRId64 "before", (int64_t) *(std::max_element(intervals.begin(), intervals.end()))); | 
|  | 947 | intervals.erase(std::max_element(intervals.begin(), intervals.end())); | 
|  | 948 | // ALOGW("%" PRId64 "after", (int64_t) *(std::max_element(intervals.begin(), intervals.end()))); | 
|  | 949 | std::ofstream ofs; | 
|  | 950 | ofs.open(kName, append ? std::ios::app : std::ios::trunc); | 
|  | 951 | if (!ofs) { | 
|  | 952 | ALOGW("couldn't open file %s", kName); | 
|  | 953 | return; | 
|  | 954 | } | 
|  | 955 | for (size_t i = 0; i < intervals.size(); ++i) { | 
|  | 956 | ofs << intervals[i] << "\n"; | 
|  | 957 | } | 
|  | 958 | ofs.close(); | 
|  | 959 | } | 
|  | 960 |  | 
| Sanna Catherine de Treville Wager | 9484bae | 2017-06-15 14:39:44 -0700 | [diff] [blame^] | 961 | // converts a time series into a map. key: buffer period length. value: count | 
|  | 962 | static std::map<int, int> buildBuckets(const std::vector<int64_t> &samples) { | 
|  | 963 | // TODO allow buckets of variable resolution | 
|  | 964 | std::map<int, int> buckets; | 
|  | 965 | for (size_t i = 1; i < samples.size(); ++i) { | 
|  | 966 | ++buckets[deltaMs(samples[i - 1], samples[i])]; | 
|  | 967 | } | 
|  | 968 | return buckets; | 
|  | 969 | } | 
|  | 970 |  | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 971 | void NBLog::Reader::dump(int fd, size_t indent, NBLog::Reader::Snapshot &snapshot) | 
|  | 972 | { | 
| Sanna Catherine de Treville Wager | 9484bae | 2017-06-15 14:39:44 -0700 | [diff] [blame^] | 973 | //  CallStack cs(LOG_TAG); | 
| Glenn Kasten | 4e01ef6 | 2013-07-11 14:29:59 -0700 | [diff] [blame] | 974 | mFd = fd; | 
|  | 975 | mIndent = indent; | 
|  | 976 | String8 timestamp, body; | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 977 | size_t lost = snapshot.lost() + (snapshot.begin() - EntryIterator(snapshot.data())); | 
| Glenn Kasten | c02c961 | 2013-10-15 09:25:11 -0700 | [diff] [blame] | 978 | if (lost > 0) { | 
| Glenn Kasten | 95d287d | 2014-04-28 14:11:45 -0700 | [diff] [blame] | 979 | body.appendFormat("warning: lost %zu bytes worth of events", lost); | 
| Glenn Kasten | 4e01ef6 | 2013-07-11 14:29:59 -0700 | [diff] [blame] | 980 | // TODO timestamp empty here, only other choice to wait for the first timestamp event in the | 
| Sanna Catherine de Treville Wager | 2d1631e | 2017-05-30 16:12:36 -0700 | [diff] [blame] | 981 | //      log to push it out.  Consider keeping the timestamp/body between calls to copyEntryDataAt(). | 
| Glenn Kasten | 4e01ef6 | 2013-07-11 14:29:59 -0700 | [diff] [blame] | 982 | dumpLine(timestamp, body); | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 983 | } | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 984 |  | 
| Nicolas Roulet | 6ea1d7e | 2017-02-14 16:17:31 -0800 | [diff] [blame] | 985 | for (auto entry = snapshot.begin(); entry != snapshot.end();) { | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 986 | switch (entry->type) { | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 987 | case EVENT_START_FMT: | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 988 | entry = handleFormat(FormatEntry(entry), ×tamp, &body); | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 989 | break; | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 990 | case EVENT_HISTOGRAM_ENTRY_TS: { | 
|  | 991 | HistTsEntryWithAuthor *data = (HistTsEntryWithAuthor *) (entry->data); | 
|  | 992 | // TODO This memcpies are here to avoid unaligned memory access crash. | 
|  | 993 | // There's probably a more efficient way to do it | 
|  | 994 | log_hash_t hash; | 
|  | 995 | memcpy(&hash, &(data->hash), sizeof(hash)); | 
| Nicolas Roulet | ad82aa6 | 2017-04-03 19:15:20 -0700 | [diff] [blame] | 996 | int64_t ts; | 
|  | 997 | memcpy(&ts, &data->ts, sizeof(ts)); | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 998 | const std::pair<log_hash_t, int> key(hash, data->author); | 
| Nicolas Roulet | ad82aa6 | 2017-04-03 19:15:20 -0700 | [diff] [blame] | 999 | // TODO might want to filter excessively high outliers, which are usually caused | 
|  | 1000 | // by the thread being inactive. | 
|  | 1001 | mHists[key].push_back(ts); | 
| Sanna Catherine de Treville Wager | 9484bae | 2017-06-15 14:39:44 -0700 | [diff] [blame^] | 1002 | // store time series data for each reader in order to bucket it once there | 
|  | 1003 | // is enough data. Then, it is written to recentHists as a histogram. | 
|  | 1004 | mTimeStampSeries[data->author].push_back(ts); | 
|  | 1005 | // if length of the time series has reached kShortHistSize samples, | 
|  | 1006 | // compute its histogram, append this to mRecentHists and erase the time series | 
|  | 1007 | if (mTimeStampSeries[data->author].size() >= kShortHistSize) { | 
|  | 1008 | mRecentHists.emplace_front(data->author, | 
|  | 1009 | buildBuckets(mTimeStampSeries[data->author])); | 
|  | 1010 | // do not let mRecentHists exceed capacity | 
|  | 1011 | // TODO: turn the FIFO queue into a circular buffer | 
|  | 1012 | if (mRecentHists.size() >= kRecentHistsCapacity) { | 
|  | 1013 | mRecentHists.pop_back(); | 
|  | 1014 | } | 
|  | 1015 | mTimeStampSeries.erase(data->author); | 
|  | 1016 | } | 
|  | 1017 | // if an element in mHists has not grown for a long time, delete | 
|  | 1018 | // TODO copy histogram data only to mRecentHistsBuffer and pop oldest | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 1019 | ++entry; | 
|  | 1020 | break; | 
|  | 1021 | } | 
| Nicolas Roulet | ad82aa6 | 2017-04-03 19:15:20 -0700 | [diff] [blame] | 1022 | case EVENT_HISTOGRAM_FLUSH: { | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 1023 | ++entry; | 
|  | 1024 | break; | 
| Nicolas Roulet | ad82aa6 | 2017-04-03 19:15:20 -0700 | [diff] [blame] | 1025 | } | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 1026 | case EVENT_END_FMT: | 
|  | 1027 | body.appendFormat("warning: got to end format event"); | 
| Nicolas Roulet | 6ea1d7e | 2017-02-14 16:17:31 -0800 | [diff] [blame] | 1028 | ++entry; | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 1029 | break; | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 1030 | case EVENT_RESERVED: | 
|  | 1031 | default: | 
| Nicolas Roulet | 6ea1d7e | 2017-02-14 16:17:31 -0800 | [diff] [blame] | 1032 | body.appendFormat("warning: unexpected event %d", entry->type); | 
|  | 1033 | ++entry; | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 1034 | break; | 
|  | 1035 | } | 
| Sanna Catherine de Treville Wager | 9484bae | 2017-06-15 14:39:44 -0700 | [diff] [blame^] | 1036 | // if (!body.isEmpty()) { | 
|  | 1037 | //    dumpLine(timestamp, body); | 
|  | 1038 | // } | 
|  | 1039 | } | 
|  | 1040 | reportPerformance(&body, mRecentHists); | 
|  | 1041 | if (!body.isEmpty()) { | 
|  | 1042 | dumpLine(timestamp, body); | 
| Glenn Kasten | 4e01ef6 | 2013-07-11 14:29:59 -0700 | [diff] [blame] | 1043 | } | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 1044 | } | 
|  | 1045 |  | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 1046 | void NBLog::Reader::dump(int fd, size_t indent) | 
|  | 1047 | { | 
|  | 1048 | // get a snapshot, dump it | 
|  | 1049 | std::unique_ptr<Snapshot> snap = getSnapshot(); | 
|  | 1050 | dump(fd, indent, *snap); | 
|  | 1051 | } | 
|  | 1052 |  | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 1053 | void NBLog::Reader::dumpLine(const String8 ×tamp, String8 &body) | 
| Glenn Kasten | 4e01ef6 | 2013-07-11 14:29:59 -0700 | [diff] [blame] | 1054 | { | 
|  | 1055 | if (mFd >= 0) { | 
| Elliott Hughes | 8b5f642 | 2014-05-22 01:22:06 -0700 | [diff] [blame] | 1056 | dprintf(mFd, "%.*s%s %s\n", mIndent, "", timestamp.string(), body.string()); | 
| Glenn Kasten | 4e01ef6 | 2013-07-11 14:29:59 -0700 | [diff] [blame] | 1057 | } else { | 
|  | 1058 | ALOGI("%.*s%s %s", mIndent, "", timestamp.string(), body.string()); | 
|  | 1059 | } | 
|  | 1060 | body.clear(); | 
|  | 1061 | } | 
|  | 1062 |  | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 1063 | bool NBLog::Reader::isIMemory(const sp<IMemory>& iMemory) const | 
|  | 1064 | { | 
| Glenn Kasten | 481fb67 | 2013-09-30 14:39:28 -0700 | [diff] [blame] | 1065 | return iMemory != 0 && mIMemory != 0 && iMemory->pointer() == mIMemory->pointer(); | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 1066 | } | 
|  | 1067 |  | 
| Sanna Catherine de Treville Wager | 201079a | 2017-05-18 16:36:29 -0700 | [diff] [blame] | 1068 | void NBLog::Reader::setFindGlitch(bool s) | 
|  | 1069 | { | 
|  | 1070 | findGlitch = s; | 
|  | 1071 | } | 
|  | 1072 |  | 
|  | 1073 | bool NBLog::Reader::isFindGlitch() const | 
|  | 1074 | { | 
|  | 1075 | return findGlitch; | 
|  | 1076 | } | 
|  | 1077 |  | 
| Glenn Kasten | 1c44627 | 2017-04-07 09:49:07 -0700 | [diff] [blame] | 1078 | // --------------------------------------------------------------------------- | 
|  | 1079 |  | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 1080 | void NBLog::appendTimestamp(String8 *body, const void *data) { | 
| Nicolas Roulet | f42f156 | 2017-03-30 19:16:22 -0700 | [diff] [blame] | 1081 | int64_t ts; | 
|  | 1082 | memcpy(&ts, data, sizeof(ts)); | 
|  | 1083 | body->appendFormat("[%d.%03d]", (int) (ts / (1000 * 1000 * 1000)), | 
|  | 1084 | (int) ((ts / (1000 * 1000)) % 1000)); | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 1085 | } | 
|  | 1086 |  | 
|  | 1087 | void NBLog::appendInt(String8 *body, const void *data) { | 
|  | 1088 | int x = *((int*) data); | 
|  | 1089 | body->appendFormat("<%d>", x); | 
|  | 1090 | } | 
|  | 1091 |  | 
|  | 1092 | void NBLog::appendFloat(String8 *body, const void *data) { | 
|  | 1093 | float f; | 
|  | 1094 | memcpy(&f, data, sizeof(float)); | 
|  | 1095 | body->appendFormat("<%f>", f); | 
|  | 1096 | } | 
|  | 1097 |  | 
| Nicolas Roulet | c20cb50 | 2017-02-01 12:35:24 -0800 | [diff] [blame] | 1098 | void NBLog::appendPID(String8 *body, const void* data, size_t length) { | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 1099 | pid_t id = *((pid_t*) data); | 
| Nicolas Roulet | c20cb50 | 2017-02-01 12:35:24 -0800 | [diff] [blame] | 1100 | char * name = &((char*) data)[sizeof(pid_t)]; | 
|  | 1101 | body->appendFormat("<PID: %d, name: %.*s>", id, (int) (length - sizeof(pid_t)), name); | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 1102 | } | 
|  | 1103 |  | 
| Nicolas Roulet | f42f156 | 2017-03-30 19:16:22 -0700 | [diff] [blame] | 1104 | String8 NBLog::bufferDump(const uint8_t *buffer, size_t size) | 
| Nicolas Roulet | 2aedf37 | 2017-03-29 11:27:03 -0700 | [diff] [blame] | 1105 | { | 
|  | 1106 | String8 str; | 
|  | 1107 | str.append("[ "); | 
|  | 1108 | for(size_t i = 0; i < size; i++) | 
|  | 1109 | { | 
| Nicolas Roulet | f42f156 | 2017-03-30 19:16:22 -0700 | [diff] [blame] | 1110 | str.appendFormat("%d ", buffer[i]); | 
| Nicolas Roulet | 2aedf37 | 2017-03-29 11:27:03 -0700 | [diff] [blame] | 1111 | } | 
|  | 1112 | str.append("]"); | 
|  | 1113 | return str; | 
|  | 1114 | } | 
|  | 1115 |  | 
| Nicolas Roulet | f42f156 | 2017-03-30 19:16:22 -0700 | [diff] [blame] | 1116 | String8 NBLog::bufferDump(const EntryIterator &it) | 
| Nicolas Roulet | 2aedf37 | 2017-03-29 11:27:03 -0700 | [diff] [blame] | 1117 | { | 
| Nicolas Roulet | f42f156 | 2017-03-30 19:16:22 -0700 | [diff] [blame] | 1118 | return bufferDump(it, it->length + Entry::kOverhead); | 
| Nicolas Roulet | 2aedf37 | 2017-03-29 11:27:03 -0700 | [diff] [blame] | 1119 | } | 
|  | 1120 |  | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 1121 | NBLog::EntryIterator NBLog::Reader::handleFormat(const FormatEntry &fmtEntry, | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 1122 | String8 *timestamp, | 
|  | 1123 | String8 *body) { | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 1124 | // log timestamp | 
| Nicolas Roulet | f42f156 | 2017-03-30 19:16:22 -0700 | [diff] [blame] | 1125 | int64_t ts = fmtEntry.timestamp(); | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 1126 | timestamp->clear(); | 
| Nicolas Roulet | f42f156 | 2017-03-30 19:16:22 -0700 | [diff] [blame] | 1127 | timestamp->appendFormat("[%d.%03d]", (int) (ts / (1000 * 1000 * 1000)), | 
|  | 1128 | (int) ((ts / (1000 * 1000)) % 1000)); | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 1129 |  | 
| Nicolas Roulet | bd0c6b4 | 2017-03-16 13:54:23 -0700 | [diff] [blame] | 1130 | // log unique hash | 
|  | 1131 | log_hash_t hash = fmtEntry.hash(); | 
|  | 1132 | // print only lower 16bit of hash as hex and line as int to reduce spam in the log | 
|  | 1133 | body->appendFormat("%.4X-%d ", (int)(hash >> 16) & 0xFFFF, (int) hash & 0xFFFF); | 
|  | 1134 |  | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 1135 | // log author (if present) | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 1136 | handleAuthor(fmtEntry, body); | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 1137 |  | 
|  | 1138 | // log string | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 1139 | NBLog::EntryIterator arg = fmtEntry.args(); | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 1140 |  | 
|  | 1141 | const char* fmt = fmtEntry.formatString(); | 
|  | 1142 | size_t fmt_length = fmtEntry.formatStringLength(); | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 1143 |  | 
|  | 1144 | for (size_t fmt_offset = 0; fmt_offset < fmt_length; ++fmt_offset) { | 
|  | 1145 | if (fmt[fmt_offset] != '%') { | 
|  | 1146 | body->append(&fmt[fmt_offset], 1); // TODO optimize to write consecutive strings at once | 
|  | 1147 | continue; | 
|  | 1148 | } | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 1149 | // case "%%"" | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 1150 | if (fmt[++fmt_offset] == '%') { | 
|  | 1151 | body->append("%"); | 
|  | 1152 | continue; | 
|  | 1153 | } | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 1154 | // case "%\0" | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 1155 | if (fmt_offset == fmt_length) { | 
|  | 1156 | continue; | 
|  | 1157 | } | 
|  | 1158 |  | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 1159 | NBLog::Event event = (NBLog::Event) arg->type; | 
|  | 1160 | size_t length = arg->length; | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 1161 |  | 
|  | 1162 | // TODO check length for event type is correct | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 1163 |  | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 1164 | if (event == EVENT_END_FMT) { | 
|  | 1165 | break; | 
|  | 1166 | } | 
|  | 1167 |  | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 1168 | // TODO: implement more complex formatting such as %.3f | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 1169 | const uint8_t *datum = arg->data; // pointer to the current event args | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 1170 | switch(fmt[fmt_offset]) | 
|  | 1171 | { | 
|  | 1172 | case 's': // string | 
| Nicolas Roulet | 4da7820 | 2017-02-03 12:53:39 -0800 | [diff] [blame] | 1173 | ALOGW_IF(event != EVENT_STRING, | 
|  | 1174 | "NBLog Reader incompatible event for string specifier: %d", event); | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 1175 | body->append((const char*) datum, length); | 
|  | 1176 | break; | 
|  | 1177 |  | 
|  | 1178 | case 't': // timestamp | 
| Nicolas Roulet | 4da7820 | 2017-02-03 12:53:39 -0800 | [diff] [blame] | 1179 | ALOGW_IF(event != EVENT_TIMESTAMP, | 
|  | 1180 | "NBLog Reader incompatible event for timestamp specifier: %d", event); | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 1181 | appendTimestamp(body, datum); | 
|  | 1182 | break; | 
|  | 1183 |  | 
|  | 1184 | case 'd': // integer | 
| Nicolas Roulet | 4da7820 | 2017-02-03 12:53:39 -0800 | [diff] [blame] | 1185 | ALOGW_IF(event != EVENT_INTEGER, | 
|  | 1186 | "NBLog Reader incompatible event for integer specifier: %d", event); | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 1187 | appendInt(body, datum); | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 1188 | break; | 
|  | 1189 |  | 
|  | 1190 | case 'f': // float | 
| Nicolas Roulet | 4da7820 | 2017-02-03 12:53:39 -0800 | [diff] [blame] | 1191 | ALOGW_IF(event != EVENT_FLOAT, | 
|  | 1192 | "NBLog Reader incompatible event for float specifier: %d", event); | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 1193 | appendFloat(body, datum); | 
|  | 1194 | break; | 
|  | 1195 |  | 
|  | 1196 | case 'p': // pid | 
| Nicolas Roulet | 4da7820 | 2017-02-03 12:53:39 -0800 | [diff] [blame] | 1197 | ALOGW_IF(event != EVENT_PID, | 
|  | 1198 | "NBLog Reader incompatible event for pid specifier: %d", event); | 
| Nicolas Roulet | c20cb50 | 2017-02-01 12:35:24 -0800 | [diff] [blame] | 1199 | appendPID(body, datum, length); | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 1200 | break; | 
|  | 1201 |  | 
|  | 1202 | default: | 
|  | 1203 | ALOGW("NBLog Reader encountered unknown character %c", fmt[fmt_offset]); | 
|  | 1204 | } | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 1205 | ++arg; | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 1206 | } | 
| Nicolas Roulet | cd5dd01 | 2017-02-13 12:09:28 -0800 | [diff] [blame] | 1207 | ALOGW_IF(arg->type != EVENT_END_FMT, "Expected end of format, got %d", arg->type); | 
|  | 1208 | ++arg; | 
|  | 1209 | return arg; | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 1210 | } | 
|  | 1211 |  | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 1212 | static int widthOf(int x) { | 
|  | 1213 | int width = 0; | 
|  | 1214 | while (x > 0) { | 
|  | 1215 | ++width; | 
|  | 1216 | x /= 10; | 
|  | 1217 | } | 
|  | 1218 | return width; | 
|  | 1219 | } | 
|  | 1220 |  | 
| Nicolas Roulet | 4f03349 | 2017-04-03 19:17:03 -0700 | [diff] [blame] | 1221 | static inline uint32_t log2(uint32_t x) { | 
|  | 1222 | // This works for x > 0 | 
|  | 1223 | return 31 - __builtin_clz(x); | 
|  | 1224 | } | 
|  | 1225 |  | 
| Sanna Catherine de Treville Wager | 9484bae | 2017-06-15 14:39:44 -0700 | [diff] [blame^] | 1226 | // TODO create a subclass of Reader for this and related work | 
|  | 1227 | void NBLog::Reader::reportPerformance(String8 *body, | 
|  | 1228 | const std::deque<std::pair | 
|  | 1229 | <int, short_histogram>> &shortHists, | 
|  | 1230 | int maxHeight) { | 
|  | 1231 | if (shortHists.size() < 1) { | 
|  | 1232 | return; | 
|  | 1233 | } | 
|  | 1234 | // this is temporary code, which only prints out one histogram | 
|  | 1235 | // of all data stored in buffer. The data is not erased, only overwritten. | 
|  | 1236 | // TODO: more elaborate data analysis | 
|  | 1237 | std::map<int, int> buckets; | 
|  | 1238 | for (const auto &shortHist: shortHists) { | 
|  | 1239 | for (const auto &countPair : shortHist.second) { | 
|  | 1240 | buckets[countPair.first] += countPair.second; | 
|  | 1241 | } | 
|  | 1242 | } | 
|  | 1243 |  | 
|  | 1244 | // underscores and spaces length corresponds to maximum width of histogram | 
|  | 1245 | static const int kLen = 40; | 
|  | 1246 | std::string underscores(kLen, '_'); | 
|  | 1247 | std::string spaces(kLen, ' '); | 
|  | 1248 |  | 
|  | 1249 | auto it = buckets.begin(); | 
|  | 1250 | int maxDelta = it->first; | 
|  | 1251 | int maxCount = it->second; | 
|  | 1252 | // Compute maximum values | 
|  | 1253 | while (++it != buckets.end()) { | 
|  | 1254 | if (it->first > maxDelta) { | 
|  | 1255 | maxDelta = it->first; | 
|  | 1256 | } | 
|  | 1257 | if (it->second > maxCount) { | 
|  | 1258 | maxCount = it->second; | 
|  | 1259 | } | 
|  | 1260 | } | 
|  | 1261 | int height = log2(maxCount) + 1; // maxCount > 0, safe to call log2 | 
|  | 1262 | const int leftPadding = widthOf(1 << height); | 
|  | 1263 | const int colWidth = std::max(std::max(widthOf(maxDelta) + 1, 3), leftPadding + 2); | 
|  | 1264 | int scalingFactor = 1; | 
|  | 1265 | // scale data if it exceeds maximum height | 
|  | 1266 | if (height > maxHeight) { | 
|  | 1267 | scalingFactor = (height + maxHeight) / maxHeight; | 
|  | 1268 | height /= scalingFactor; | 
|  | 1269 | } | 
|  | 1270 | body->appendFormat("\n%*s", leftPadding + 11, "Occurrences"); | 
|  | 1271 | // write histogram label line with bucket values | 
|  | 1272 | body->appendFormat("\n%s", " "); | 
|  | 1273 | body->appendFormat("%*s", leftPadding, " "); | 
|  | 1274 | for (auto const &x : buckets) { | 
|  | 1275 | body->appendFormat("%*d", colWidth, x.second); | 
|  | 1276 | } | 
|  | 1277 | // write histogram ascii art | 
|  | 1278 | body->appendFormat("\n%s", " "); | 
|  | 1279 | for (int row = height * scalingFactor; row >= 0; row -= scalingFactor) { | 
|  | 1280 | const int value = 1 << row; | 
|  | 1281 | body->appendFormat("%.*s", leftPadding, spaces.c_str()); | 
|  | 1282 | for (auto const &x : buckets) { | 
|  | 1283 | body->appendFormat("%.*s%s", colWidth - 1, spaces.c_str(), x.second < value ? " " : "|"); | 
|  | 1284 | } | 
|  | 1285 | body->appendFormat("\n%s", " "); | 
|  | 1286 | } | 
|  | 1287 | // print x-axis | 
|  | 1288 | const int columns = static_cast<int>(buckets.size()); | 
|  | 1289 | body->appendFormat("%*c", leftPadding, ' '); | 
|  | 1290 | body->appendFormat("%.*s", (columns + 1) * colWidth, underscores.c_str()); | 
|  | 1291 | body->appendFormat("\n%s", " "); | 
|  | 1292 |  | 
|  | 1293 | // write footer with bucket labels | 
|  | 1294 | body->appendFormat("%*s", leftPadding, " "); | 
|  | 1295 | for (auto const &x : buckets) { | 
|  | 1296 | body->appendFormat("%*d", colWidth, x.first); | 
|  | 1297 | } | 
|  | 1298 | body->appendFormat("%.*s%s", colWidth, spaces.c_str(), "ms\n"); | 
|  | 1299 |  | 
|  | 1300 | } | 
|  | 1301 |  | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 1302 | // TODO put this function in separate file. Make it return a std::string instead of modifying body | 
| Nicolas Roulet | ad82aa6 | 2017-04-03 19:15:20 -0700 | [diff] [blame] | 1303 | void NBLog::Reader::drawHistogram(String8 *body, | 
|  | 1304 | const std::vector<int64_t> &samples, | 
| Nicolas Roulet | 4f03349 | 2017-04-03 19:17:03 -0700 | [diff] [blame] | 1305 | bool logScale, | 
| Nicolas Roulet | ad82aa6 | 2017-04-03 19:15:20 -0700 | [diff] [blame] | 1306 | int indent, | 
|  | 1307 | int maxHeight) { | 
| Sanna Catherine de Treville Wager | cced674 | 2017-05-10 14:42:54 -0700 | [diff] [blame] | 1308 | // this avoids some corner cases | 
| Nicolas Roulet | 4f03349 | 2017-04-03 19:17:03 -0700 | [diff] [blame] | 1309 | if (samples.size() <= 1) { | 
|  | 1310 | return; | 
|  | 1311 | } | 
| Sanna Catherine de Treville Wager | 2d1631e | 2017-05-30 16:12:36 -0700 | [diff] [blame] | 1312 | // temp code for debugging the outlier timestamp | 
|  | 1313 | const int kMaxMs = 100; | 
|  | 1314 | for (size_t i = 1; i < samples.size()-1; ++i) { | 
|  | 1315 | const int currDelta = deltaMs(samples[i - 1], samples[i]); | 
|  | 1316 | if (currDelta > kMaxMs) { | 
|  | 1317 | body->appendFormat("\nlocation: %zu, size: %zu, pos from end: %zu, %d\t", i, | 
|  | 1318 | samples.size(), samples.size() - i, currDelta); | 
|  | 1319 | } | 
|  | 1320 | } | 
|  | 1321 | // FIXME: as can be seen when printing the values, the outlier timestamps typically occur | 
|  | 1322 | // in the first histogram 35 to 38 indices from the end (most often 35). | 
|  | 1323 | // TODO: build histogram buckets earlier and discard timestamps to save memory | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 1324 | std::map<int, int> buckets = buildBuckets(samples); | 
| Nicolas Roulet | 4f03349 | 2017-04-03 19:17:03 -0700 | [diff] [blame] | 1325 | // TODO consider changing all ints to uint32_t or uint64_t | 
| Sanna Catherine de Treville Wager | cced674 | 2017-05-10 14:42:54 -0700 | [diff] [blame] | 1326 |  | 
|  | 1327 | // underscores and spaces length corresponds to maximum width of histogram | 
|  | 1328 | static const int kLen = 40; | 
| Sanna Catherine de Treville Wager | 9484bae | 2017-06-15 14:39:44 -0700 | [diff] [blame^] | 1329 | std::string underscores(kLen, '_'); | 
| Sanna Catherine de Treville Wager | cced674 | 2017-05-10 14:42:54 -0700 | [diff] [blame] | 1330 | std::string spaces(kLen, ' '); | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 1331 |  | 
|  | 1332 | auto it = buckets.begin(); | 
| Sanna Catherine de Treville Wager | cced674 | 2017-05-10 14:42:54 -0700 | [diff] [blame] | 1333 | int maxDelta = it->first; | 
|  | 1334 | int maxCount = it->second; | 
| Nicolas Roulet | ad82aa6 | 2017-04-03 19:15:20 -0700 | [diff] [blame] | 1335 | // Compute maximum values | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 1336 | while (++it != buckets.end()) { | 
| Sanna Catherine de Treville Wager | cced674 | 2017-05-10 14:42:54 -0700 | [diff] [blame] | 1337 | if (it->first > maxDelta) { | 
|  | 1338 | maxDelta = it->first; | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 1339 | } | 
| Sanna Catherine de Treville Wager | cced674 | 2017-05-10 14:42:54 -0700 | [diff] [blame] | 1340 | if (it->second > maxCount) { | 
|  | 1341 | maxCount = it->second; | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 1342 | } | 
|  | 1343 | } | 
| Sanna Catherine de Treville Wager | cced674 | 2017-05-10 14:42:54 -0700 | [diff] [blame] | 1344 | int height = logScale ? log2(maxCount) + 1 : maxCount; // maxCount > 0, safe to call log2 | 
| Sanna Catherine de Treville Wager | 9484bae | 2017-06-15 14:39:44 -0700 | [diff] [blame^] | 1345 | const int leftPadding = widthOf(logScale ? 1 << height : maxCount); | 
| Sanna Catherine de Treville Wager | cced674 | 2017-05-10 14:42:54 -0700 | [diff] [blame] | 1346 | const int colWidth = std::max(std::max(widthOf(maxDelta) + 1, 3), leftPadding + 2); | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 1347 | int scalingFactor = 1; | 
| Nicolas Roulet | ad82aa6 | 2017-04-03 19:15:20 -0700 | [diff] [blame] | 1348 | // scale data if it exceeds maximum height | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 1349 | if (height > maxHeight) { | 
|  | 1350 | scalingFactor = (height + maxHeight) / maxHeight; | 
|  | 1351 | height /= scalingFactor; | 
|  | 1352 | } | 
| Sanna Catherine de Treville Wager | cced674 | 2017-05-10 14:42:54 -0700 | [diff] [blame] | 1353 | body->appendFormat("\n%*s", leftPadding + 11, "Occurrences"); | 
|  | 1354 | // write histogram label line with bucket values | 
| Nicolas Roulet | ad82aa6 | 2017-04-03 19:15:20 -0700 | [diff] [blame] | 1355 | body->appendFormat("\n%*s", indent, " "); | 
| Sanna Catherine de Treville Wager | cced674 | 2017-05-10 14:42:54 -0700 | [diff] [blame] | 1356 | body->appendFormat("%*s", leftPadding, " "); | 
|  | 1357 | for (auto const &x : buckets) { | 
|  | 1358 | body->appendFormat("%*d", colWidth, x.second); | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 1359 | } | 
| Nicolas Roulet | ad82aa6 | 2017-04-03 19:15:20 -0700 | [diff] [blame] | 1360 | // write histogram ascii art | 
|  | 1361 | body->appendFormat("\n%*s", indent, " "); | 
| Sanna Catherine de Treville Wager | cced674 | 2017-05-10 14:42:54 -0700 | [diff] [blame] | 1362 | for (int row = height * scalingFactor; row >= 0; row -= scalingFactor) { | 
|  | 1363 | const int value = logScale ? (1 << row) : row; | 
|  | 1364 | body->appendFormat("%.*s", leftPadding, spaces.c_str()); | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 1365 | for (auto const &x : buckets) { | 
| Sanna Catherine de Treville Wager | cced674 | 2017-05-10 14:42:54 -0700 | [diff] [blame] | 1366 | body->appendFormat("%.*s%s", colWidth - 1, spaces.c_str(), x.second < value ? " " : "|"); | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 1367 | } | 
| Nicolas Roulet | ad82aa6 | 2017-04-03 19:15:20 -0700 | [diff] [blame] | 1368 | body->appendFormat("\n%*s", indent, " "); | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 1369 | } | 
| Sanna Catherine de Treville Wager | cced674 | 2017-05-10 14:42:54 -0700 | [diff] [blame] | 1370 | // print x-axis | 
|  | 1371 | const int columns = static_cast<int>(buckets.size()); | 
|  | 1372 | body->appendFormat("%*c", leftPadding, ' '); | 
|  | 1373 | body->appendFormat("%.*s", (columns + 1) * colWidth, underscores.c_str()); | 
|  | 1374 | body->appendFormat("\n%*s", indent, " "); | 
|  | 1375 |  | 
| Nicolas Roulet | ad82aa6 | 2017-04-03 19:15:20 -0700 | [diff] [blame] | 1376 | // write footer with bucket labels | 
| Sanna Catherine de Treville Wager | cced674 | 2017-05-10 14:42:54 -0700 | [diff] [blame] | 1377 | body->appendFormat("%*s", leftPadding, " "); | 
|  | 1378 | for (auto const &x : buckets) { | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 1379 | body->appendFormat("%*d", colWidth, x.first); | 
|  | 1380 | } | 
| Sanna Catherine de Treville Wager | cced674 | 2017-05-10 14:42:54 -0700 | [diff] [blame] | 1381 | body->appendFormat("%.*s%s", colWidth, spaces.c_str(), "ms\n"); | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 1382 | } | 
|  | 1383 |  | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 1384 | NBLog::Merger::Merger(const void *shared, size_t size): | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 1385 | mShared((Shared *) shared), | 
|  | 1386 | mFifo(mShared != NULL ? | 
|  | 1387 | new audio_utils_fifo(size, sizeof(uint8_t), | 
|  | 1388 | mShared->mBuffer, mShared->mRear, NULL /*throttlesFront*/) : NULL), | 
|  | 1389 | mFifoWriter(mFifo != NULL ? new audio_utils_fifo_writer(*mFifo) : NULL) | 
|  | 1390 | {} | 
|  | 1391 |  | 
|  | 1392 | void NBLog::Merger::addReader(const NBLog::NamedReader &reader) { | 
| Glenn Kasten | 1c44627 | 2017-04-07 09:49:07 -0700 | [diff] [blame] | 1393 | // FIXME This is called by binder thread in MediaLogService::registerWriter | 
|  | 1394 | //       but the access to shared variable mNamedReaders is not yet protected by a lock. | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 1395 | mNamedReaders.push_back(reader); | 
|  | 1396 | } | 
|  | 1397 |  | 
|  | 1398 | // items placed in priority queue during merge | 
|  | 1399 | // composed by a timestamp and the index of the snapshot where the timestamp came from | 
|  | 1400 | struct MergeItem | 
|  | 1401 | { | 
| Nicolas Roulet | f42f156 | 2017-03-30 19:16:22 -0700 | [diff] [blame] | 1402 | int64_t ts; | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 1403 | int index; | 
| Nicolas Roulet | f42f156 | 2017-03-30 19:16:22 -0700 | [diff] [blame] | 1404 | MergeItem(int64_t ts, int index): ts(ts), index(index) {} | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 1405 | }; | 
|  | 1406 |  | 
|  | 1407 | // operators needed for priority queue in merge | 
| Nicolas Roulet | f42f156 | 2017-03-30 19:16:22 -0700 | [diff] [blame] | 1408 | // bool operator>(const int64_t &t1, const int64_t &t2) { | 
|  | 1409 | //     return t1.tv_sec > t2.tv_sec || (t1.tv_sec == t2.tv_sec && t1.tv_nsec > t2.tv_nsec); | 
|  | 1410 | // } | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 1411 |  | 
|  | 1412 | bool operator>(const struct MergeItem &i1, const struct MergeItem &i2) { | 
| Nicolas Roulet | f42f156 | 2017-03-30 19:16:22 -0700 | [diff] [blame] | 1413 | return i1.ts > i2.ts || (i1.ts == i2.ts && i1.index > i2.index); | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 1414 | } | 
|  | 1415 |  | 
|  | 1416 | // Merge registered readers, sorted by timestamp | 
|  | 1417 | void NBLog::Merger::merge() { | 
| Glenn Kasten | 1c44627 | 2017-04-07 09:49:07 -0700 | [diff] [blame] | 1418 | // FIXME This is called by merge thread | 
|  | 1419 | //       but the access to shared variable mNamedReaders is not yet protected by a lock. | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 1420 | int nLogs = mNamedReaders.size(); | 
|  | 1421 | std::vector<std::unique_ptr<NBLog::Reader::Snapshot>> snapshots(nLogs); | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 1422 | std::vector<NBLog::EntryIterator> offsets(nLogs); | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 1423 | for (int i = 0; i < nLogs; ++i) { | 
|  | 1424 | snapshots[i] = mNamedReaders[i].reader()->getSnapshot(); | 
| Nicolas Roulet | 6ea1d7e | 2017-02-14 16:17:31 -0800 | [diff] [blame] | 1425 | offsets[i] = snapshots[i]->begin(); | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 1426 | } | 
|  | 1427 | // initialize offsets | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 1428 | // TODO custom heap implementation could allow to update top, improving performance | 
|  | 1429 | // for bursty buffers | 
|  | 1430 | std::priority_queue<MergeItem, std::vector<MergeItem>, std::greater<MergeItem>> timestamps; | 
|  | 1431 | for (int i = 0; i < nLogs; ++i) | 
|  | 1432 | { | 
| Nicolas Roulet | 6ea1d7e | 2017-02-14 16:17:31 -0800 | [diff] [blame] | 1433 | if (offsets[i] != snapshots[i]->end()) { | 
| Nicolas Roulet | f42f156 | 2017-03-30 19:16:22 -0700 | [diff] [blame] | 1434 | int64_t ts = AbstractEntry::buildEntry(offsets[i])->timestamp(); | 
| Nicolas Roulet | 6ea1d7e | 2017-02-14 16:17:31 -0800 | [diff] [blame] | 1435 | timestamps.emplace(ts, i); | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 1436 | } | 
|  | 1437 | } | 
|  | 1438 |  | 
|  | 1439 | while (!timestamps.empty()) { | 
|  | 1440 | // find minimum timestamp | 
|  | 1441 | int index = timestamps.top().index; | 
| Nicolas Roulet | 6ea1d7e | 2017-02-14 16:17:31 -0800 | [diff] [blame] | 1442 | // copy it to the log, increasing offset | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 1443 | offsets[index] = AbstractEntry::buildEntry(offsets[index])->copyWithAuthor(mFifoWriter, | 
|  | 1444 | index); | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 1445 | // update data structures | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 1446 | timestamps.pop(); | 
| Nicolas Roulet | 6ea1d7e | 2017-02-14 16:17:31 -0800 | [diff] [blame] | 1447 | if (offsets[index] != snapshots[index]->end()) { | 
| Nicolas Roulet | f42f156 | 2017-03-30 19:16:22 -0700 | [diff] [blame] | 1448 | int64_t ts = AbstractEntry::buildEntry(offsets[index])->timestamp(); | 
| Nicolas Roulet | 6ea1d7e | 2017-02-14 16:17:31 -0800 | [diff] [blame] | 1449 | timestamps.emplace(ts, index); | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 1450 | } | 
|  | 1451 | } | 
|  | 1452 | } | 
|  | 1453 |  | 
| Glenn Kasten | 1c44627 | 2017-04-07 09:49:07 -0700 | [diff] [blame] | 1454 | const std::vector<NBLog::NamedReader>& NBLog::Merger::getNamedReaders() const { | 
|  | 1455 | // FIXME This is returning a reference to a shared variable that needs a lock | 
|  | 1456 | return mNamedReaders; | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 1457 | } | 
|  | 1458 |  | 
| Glenn Kasten | 1c44627 | 2017-04-07 09:49:07 -0700 | [diff] [blame] | 1459 | // --------------------------------------------------------------------------- | 
|  | 1460 |  | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 1461 | NBLog::MergeReader::MergeReader(const void *shared, size_t size, Merger &merger) | 
|  | 1462 | : Reader(shared, size), mNamedReaders(merger.getNamedReaders()) {} | 
|  | 1463 |  | 
| Nicolas Roulet | 537ad7d | 2017-03-21 16:24:30 -0700 | [diff] [blame] | 1464 | void NBLog::MergeReader::handleAuthor(const NBLog::AbstractEntry &entry, String8 *body) { | 
|  | 1465 | int author = entry.author(); | 
| Glenn Kasten | 1c44627 | 2017-04-07 09:49:07 -0700 | [diff] [blame] | 1466 | // FIXME Needs a lock | 
|  | 1467 | const char* name = mNamedReaders[author].name(); | 
| Nicolas Roulet | 40a4498 | 2017-02-03 13:39:57 -0800 | [diff] [blame] | 1468 | body->appendFormat("%s: ", name); | 
| Nicolas Roulet | fe1e144 | 2017-01-30 12:02:03 -0800 | [diff] [blame] | 1469 | } | 
|  | 1470 |  | 
| Glenn Kasten | 1c44627 | 2017-04-07 09:49:07 -0700 | [diff] [blame] | 1471 | // --------------------------------------------------------------------------- | 
|  | 1472 |  | 
| Nicolas Roulet | dcdfaec | 2017-02-14 10:18:39 -0800 | [diff] [blame] | 1473 | NBLog::MergeThread::MergeThread(NBLog::Merger &merger) | 
|  | 1474 | : mMerger(merger), | 
|  | 1475 | mTimeoutUs(0) {} | 
|  | 1476 |  | 
|  | 1477 | NBLog::MergeThread::~MergeThread() { | 
|  | 1478 | // set exit flag, set timeout to 0 to force threadLoop to exit and wait for the thread to join | 
|  | 1479 | requestExit(); | 
|  | 1480 | setTimeoutUs(0); | 
|  | 1481 | join(); | 
|  | 1482 | } | 
|  | 1483 |  | 
|  | 1484 | bool NBLog::MergeThread::threadLoop() { | 
|  | 1485 | bool doMerge; | 
|  | 1486 | { | 
|  | 1487 | AutoMutex _l(mMutex); | 
|  | 1488 | // If mTimeoutUs is negative, wait on the condition variable until it's positive. | 
|  | 1489 | // If it's positive, wait kThreadSleepPeriodUs and then merge | 
|  | 1490 | nsecs_t waitTime = mTimeoutUs > 0 ? kThreadSleepPeriodUs * 1000 : LLONG_MAX; | 
|  | 1491 | mCond.waitRelative(mMutex, waitTime); | 
|  | 1492 | doMerge = mTimeoutUs > 0; | 
|  | 1493 | mTimeoutUs -= kThreadSleepPeriodUs; | 
|  | 1494 | } | 
|  | 1495 | if (doMerge) { | 
|  | 1496 | mMerger.merge(); | 
|  | 1497 | } | 
|  | 1498 | return true; | 
|  | 1499 | } | 
|  | 1500 |  | 
|  | 1501 | void NBLog::MergeThread::wakeup() { | 
|  | 1502 | setTimeoutUs(kThreadWakeupPeriodUs); | 
|  | 1503 | } | 
|  | 1504 |  | 
|  | 1505 | void NBLog::MergeThread::setTimeoutUs(int time) { | 
|  | 1506 | AutoMutex _l(mMutex); | 
|  | 1507 | mTimeoutUs = time; | 
|  | 1508 | mCond.signal(); | 
|  | 1509 | } | 
|  | 1510 |  | 
| Glenn Kasten | 11d8dfc | 2013-01-14 14:53:13 -0800 | [diff] [blame] | 1511 | }   // namespace android |