Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0b9e7831 authored by Nicolas Roulet's avatar Nicolas Roulet Committed by Android (Google) Code Review
Browse files

Merge changes I19384478,I71c4df28,I2e0b48c6,I7fb9b899

* changes:
  NBLog change timespec to int64_t nanoseconds
  NBLog: add buffer hex dump
  Add histogram entries (timestamp and flush)
  Add unique IDs to log entry
parents 4f3de352 f42f1568
Loading
Loading
Loading
Loading
+326 −84
Original line number Diff line number Diff line
@@ -30,7 +30,9 @@
#include <utils/Log.h>
#include <utils/String8.h>

#include <map>
#include <queue>
#include <utility>

namespace android {

@@ -51,12 +53,25 @@ int NBLog::Entry::readAt(size_t offset) const

// ---------------------------------------------------------------------------

NBLog::FormatEntry::FormatEntry(const uint8_t *entry) : mEntry(entry) {
    ALOGW_IF(entry[offsetof(struct entry, type)] != EVENT_START_FMT,
        "Created format entry with invalid event type %d", entry[offsetof(struct entry, type)]);
/*static*/
std::unique_ptr<NBLog::AbstractEntry> NBLog::AbstractEntry::buildEntry(const uint8_t *ptr) {
    uint8_t type = EntryIterator(ptr)->type;
    switch (type) {
    case EVENT_START_FMT:
        return std::make_unique<FormatEntry>(FormatEntry(ptr));
    case EVENT_HISTOGRAM_FLUSH:
    case EVENT_HISTOGRAM_ENTRY_TS:
        return std::make_unique<HistogramEntry>(HistogramEntry(ptr));
    default:
        ALOGW("Tried to create AbstractEntry of type %d", type);
        return nullptr;
    }
}

NBLog::AbstractEntry::AbstractEntry(const uint8_t *entry) : mEntry(entry) {
}

NBLog::FormatEntry::FormatEntry(const NBLog::FormatEntry::iterator &it) : FormatEntry(it.ptr) {}
// ---------------------------------------------------------------------------

const char *NBLog::FormatEntry::formatString() const {
    return (const char*) mEntry + offsetof(entry, data);
@@ -66,12 +81,14 @@ size_t NBLog::FormatEntry::formatStringLength() const {
    return mEntry[offsetof(entry, length)];
}

NBLog::FormatEntry::iterator NBLog::FormatEntry::args() const {
NBLog::EntryIterator NBLog::FormatEntry::args() const {
    auto it = begin();
    // skip start fmt
    ++it;
    // skip timestamp
    ++it;
    // skip hash
    ++it;
    // Skip author if present
    if (it->type == EVENT_AUTHOR) {
        ++it;
@@ -79,19 +96,33 @@ NBLog::FormatEntry::iterator NBLog::FormatEntry::args() const {
    return it;
}

timespec NBLog::FormatEntry::timestamp() const {
int64_t NBLog::FormatEntry::timestamp() const {
    auto it = begin();
    // skip start fmt
    ++it;
    return it.payload<timespec>();
    return it.payload<int64_t>();
}

pid_t NBLog::FormatEntry::author() const {
NBLog::log_hash_t NBLog::FormatEntry::hash() const {
    auto it = begin();
    // skip start fmt
    ++it;
    // skip timestamp
    ++it;
    // unaligned 64-bit read not supported
    log_hash_t hash;
    memcpy(&hash, it->data, sizeof(hash));
    return hash;
}

int NBLog::FormatEntry::author() const {
    auto it = begin();
    // skip start fmt
    ++it;
    // skip timestamp
    ++it;
    // skip hash
    ++it;
    // if there is an author entry, return it, return -1 otherwise
    if (it->type == EVENT_AUTHOR) {
        return it.payload<int>();
@@ -99,13 +130,15 @@ pid_t NBLog::FormatEntry::author() const {
    return -1;
}

NBLog::FormatEntry::iterator NBLog::FormatEntry::copyWithAuthor(
NBLog::EntryIterator NBLog::FormatEntry::copyWithAuthor(
        std::unique_ptr<audio_utils_fifo_writer> &dst, int author) const {
    auto it = begin();
    // copy fmt start entry
    it.copyTo(dst);
    // copy timestamp
    (++it).copyTo(dst);
    // copy hash
    (++it).copyTo(dst);
    // insert author entry
    size_t authorEntrySize = NBLog::Entry::kOverhead + sizeof(author);
    uint8_t authorEntry[authorEntrySize];
@@ -124,71 +157,107 @@ NBLog::FormatEntry::iterator NBLog::FormatEntry::copyWithAuthor(
    return it;
}

void NBLog::FormatEntry::iterator::copyTo(std::unique_ptr<audio_utils_fifo_writer> &dst) const {
void NBLog::EntryIterator::copyTo(std::unique_ptr<audio_utils_fifo_writer> &dst) const {
    size_t length = ptr[offsetof(entry, length)] + NBLog::Entry::kOverhead;
    dst->write(ptr, length);
}

void NBLog::FormatEntry::iterator::copyData(uint8_t *dst) const {
void NBLog::EntryIterator::copyData(uint8_t *dst) const {
    memcpy((void*) dst, ptr + offsetof(entry, data), ptr[offsetof(entry, length)]);
}

NBLog::FormatEntry::iterator NBLog::FormatEntry::begin() const {
    return iterator(mEntry);
NBLog::EntryIterator NBLog::FormatEntry::begin() const {
    return EntryIterator(mEntry);
}

NBLog::FormatEntry::iterator::iterator()
NBLog::EntryIterator::EntryIterator()
    : ptr(nullptr) {}

NBLog::FormatEntry::iterator::iterator(const uint8_t *entry)
NBLog::EntryIterator::EntryIterator(const uint8_t *entry)
    : ptr(entry) {}

NBLog::FormatEntry::iterator::iterator(const NBLog::FormatEntry::iterator &other)
NBLog::EntryIterator::EntryIterator(const NBLog::EntryIterator &other)
    : ptr(other.ptr) {}

const NBLog::FormatEntry::entry& NBLog::FormatEntry::iterator::operator*() const {
const NBLog::entry& NBLog::EntryIterator::operator*() const {
    return *(entry*) ptr;
}

const NBLog::FormatEntry::entry* NBLog::FormatEntry::iterator::operator->() const {
const NBLog::entry* NBLog::EntryIterator::operator->() const {
    return (entry*) ptr;
}

NBLog::FormatEntry::iterator& NBLog::FormatEntry::iterator::operator++() {
NBLog::EntryIterator& NBLog::EntryIterator::operator++() {
    ptr += ptr[offsetof(entry, length)] + NBLog::Entry::kOverhead;
    return *this;
}

NBLog::FormatEntry::iterator& NBLog::FormatEntry::iterator::operator--() {
NBLog::EntryIterator& NBLog::EntryIterator::operator--() {
    ptr -= ptr[NBLog::Entry::kPreviousLengthOffset] + NBLog::Entry::kOverhead;
    return *this;
}

NBLog::FormatEntry::iterator NBLog::FormatEntry::iterator::next() const {
    iterator aux(*this);
NBLog::EntryIterator NBLog::EntryIterator::next() const {
    EntryIterator aux(*this);
    return ++aux;
}

NBLog::FormatEntry::iterator NBLog::FormatEntry::iterator::prev() const {
    iterator aux(*this);
NBLog::EntryIterator NBLog::EntryIterator::prev() const {
    EntryIterator aux(*this);
    return --aux;
}

int NBLog::FormatEntry::iterator::operator-(const NBLog::FormatEntry::iterator &other) const {
int NBLog::EntryIterator::operator-(const NBLog::EntryIterator &other) const {
    return ptr - other.ptr;
}

bool NBLog::FormatEntry::iterator::operator!=(const iterator &other) const {
bool NBLog::EntryIterator::operator!=(const EntryIterator &other) const {
    return ptr != other.ptr;
}

bool NBLog::FormatEntry::iterator::hasConsistentLength() const {
bool NBLog::EntryIterator::hasConsistentLength() const {
    return ptr[offsetof(entry, length)] == ptr[ptr[offsetof(entry, length)] +
        NBLog::Entry::kOverhead + NBLog::Entry::kPreviousLengthOffset];
}

// ---------------------------------------------------------------------------

int64_t NBLog::HistogramEntry::timestamp() const {
    return EntryIterator(mEntry).payload<HistTsEntry>().ts;
}

NBLog::log_hash_t NBLog::HistogramEntry::hash() const {
    return EntryIterator(mEntry).payload<HistTsEntry>().hash;
}

int NBLog::HistogramEntry::author() const {
    EntryIterator it(mEntry);
    if (it->length == sizeof(HistTsEntryWithAuthor)) {
        return it.payload<HistTsEntryWithAuthor>().author;
    } else {
        return -1;
    }
}

NBLog::EntryIterator NBLog::HistogramEntry::copyWithAuthor(
        std::unique_ptr<audio_utils_fifo_writer> &dst, int author) const {
    // Current histogram entry has {type, length, struct HistTsEntry, length}.
    // We now want {type, length, struct HistTsEntryWithAuthor, length}
    uint8_t buffer[Entry::kOverhead + sizeof(HistTsEntryWithAuthor)];
    // Copy content until the point we want to add the author
    memcpy(buffer, mEntry, sizeof(entry) + sizeof(HistTsEntry));
    // Copy the author
    *(int*) (buffer + sizeof(entry) + sizeof(HistTsEntry)) = author;
    // Update lengths
    buffer[offsetof(entry, length)] = sizeof(HistTsEntryWithAuthor);
    buffer[sizeof(buffer) + Entry::kPreviousLengthOffset] = sizeof(HistTsEntryWithAuthor);
    // Write new buffer into FIFO
    dst->write(buffer, sizeof(buffer));
    return EntryIterator(mEntry).next();
}

// ---------------------------------------------------------------------------

#if 0   // FIXME see note in NBLog.h
NBLog::Timeline::Timeline(size_t size, void *shared)
    : mSize(roundup(size)), mOwn(shared == NULL),
@@ -301,13 +370,15 @@ void NBLog::Writer::logTimestamp()
    if (!mEnabled) {
        return;
    }
    struct timespec ts;
    if (!clock_gettime(CLOCK_MONOTONIC, &ts)) {
    int64_t ts = get_monotonic_ns();
    if (ts > 0) {
        log(EVENT_TIMESTAMP, &ts, sizeof(ts));
    } else {
        ALOGE("Failed to get timestamp");
    }
}

void NBLog::Writer::logTimestamp(const struct timespec &ts)
void NBLog::Writer::logTimestamp(const int64_t ts)
{
    if (!mEnabled) {
        return;
@@ -360,19 +431,57 @@ void NBLog::Writer::logEnd()
    log(&entry, true);
}

void NBLog::Writer::logFormat(const char *fmt, ...)
void NBLog::Writer::logHash(log_hash_t hash)
{
    if (!mEnabled) {
        return;
    }
    log(EVENT_HASH, &hash, sizeof(hash));
}

void NBLog::Writer::logHistTS(log_hash_t hash)
{
    if (!mEnabled) {
        return;
    }
    HistTsEntry data;
    data.hash = hash;
    data.ts = get_monotonic_ns();
    if (data.ts > 0) {
        log(EVENT_HISTOGRAM_ENTRY_TS, &data, sizeof(data));
    } else {
        ALOGE("Failed to get timestamp");
    }
}

void NBLog::Writer::logHistFlush(log_hash_t hash)
{
    if (!mEnabled) {
        return;
    }
    HistTsEntry data;
    data.hash = hash;
    data.ts = get_monotonic_ns();
    if (data.ts > 0) {
        log(EVENT_HISTOGRAM_FLUSH, &data, sizeof(data));
    } else {
        ALOGE("Failed to get timestamp");
    }
}

void NBLog::Writer::logFormat(const char *fmt, log_hash_t hash, ...)
{
    if (!mEnabled) {
        return;
    }

    va_list ap;
    va_start(ap, fmt);
    Writer::logVFormat(fmt, ap);
    va_start(ap, hash);
    Writer::logVFormat(fmt, hash, ap);
    va_end(ap);
}

void NBLog::Writer::logVFormat(const char *fmt, va_list argp)
void NBLog::Writer::logVFormat(const char *fmt, log_hash_t hash, va_list argp)
{
    if (!mEnabled) {
        return;
@@ -381,8 +490,9 @@ void NBLog::Writer::logVFormat(const char *fmt, va_list argp)
    int i;
    double f;
    char* s;
    struct timespec t;
    int64_t t;
    Writer::logTimestamp();
    Writer::logHash(hash);
    for (const char *p = fmt; *p != '\0'; p++) {
        // TODO: implement more complex formatting such as %.3f
        if (*p != '%') {
@@ -395,7 +505,7 @@ void NBLog::Writer::logVFormat(const char *fmt, va_list argp)
            break;

        case 't': // timestamp
            t = va_arg(argp, struct timespec);
            t = va_arg(argp, int64_t);
            Writer::logTimestamp(t);
            break;

@@ -440,16 +550,8 @@ void NBLog::Writer::log(Event event, const void *data, size_t length)
        //      a confusion for a programmer debugging their code.
        return;
    }
    switch (event) {
    case EVENT_STRING:
    case EVENT_TIMESTAMP:
    case EVENT_INTEGER:
    case EVENT_FLOAT:
    case EVENT_PID:
    case EVENT_START_FMT:
        break;
    case EVENT_RESERVED:
    default:
    // Ignore if invalid event
    if (event == EVENT_RESERVED || event >= EVENT_UPPER_BOUND) {
        return;
    }
    Entry entry(event, data, length);
@@ -531,7 +633,7 @@ void NBLog::LockedWriter::logTimestamp()
    Writer::logTimestamp();
}

void NBLog::LockedWriter::logTimestamp(const struct timespec &ts)
void NBLog::LockedWriter::logTimestamp(const int64_t ts)
{
    Mutex::Autolock _l(mLock);
    Writer::logTimestamp(ts);
@@ -568,6 +670,12 @@ void NBLog::LockedWriter::logEnd()
    Writer::logEnd();
}

void NBLog::LockedWriter::logHash(log_hash_t hash)
{
    Mutex::Autolock _l(mLock);
    Writer::logHash(hash);
}

bool NBLog::LockedWriter::isEnabled() const
{
    Mutex::Autolock _l(mLock);
@@ -582,6 +690,11 @@ bool NBLog::LockedWriter::setEnabled(bool enabled)

// ---------------------------------------------------------------------------

const std::set<NBLog::Event> NBLog::Reader::startingTypes {NBLog::Event::EVENT_START_FMT,
                                                           NBLog::Event::EVENT_HISTOGRAM_ENTRY_TS};
const std::set<NBLog::Event> NBLog::Reader::endingTypes   {NBLog::Event::EVENT_END_FMT,
                                                           NBLog::Event::EVENT_HISTOGRAM_ENTRY_TS,
                                                           NBLog::Event::EVENT_HISTOGRAM_FLUSH};
NBLog::Reader::Reader(const void *shared, size_t size)
    : mShared((/*const*/ Shared *) shared), /*mIMemory*/
      mFd(-1), mIndent(0),
@@ -604,16 +717,17 @@ NBLog::Reader::~Reader()
    delete mFifo;
}

uint8_t *NBLog::Reader::findLastEntryOfType(uint8_t *front, uint8_t *back, uint8_t type) {
const uint8_t *NBLog::Reader::findLastEntryOfTypes(const uint8_t *front, const uint8_t *back,
                                            const std::set<Event> &types) {
    while (back + Entry::kPreviousLengthOffset >= front) {
        uint8_t *prev = back - back[Entry::kPreviousLengthOffset] - Entry::kOverhead;
        if (prev < front || prev + prev[offsetof(FormatEntry::entry, length)] +
        const uint8_t *prev = back - back[Entry::kPreviousLengthOffset] - Entry::kOverhead;
        if (prev < front || prev + prev[offsetof(entry, length)] +
                            Entry::kOverhead != back) {

            // prev points to an out of limits or inconsistent entry
            return nullptr;
        }
        if (prev[offsetof(FormatEntry::entry, type)] == type) {
        if (types.find((const Event) prev[offsetof(entry, type)]) != types.end()) {
            return prev;
        }
        back = prev;
@@ -652,21 +766,21 @@ std::unique_ptr<NBLog::Reader::Snapshot> NBLog::Reader::getSnapshot()
    // it ends in a complete entry (which is not an END_FMT). So is safe to traverse backwards.
    // TODO: handle client corruption (in the middle of a buffer)

    uint8_t *back = snapshot->mData + availToRead;
    uint8_t *front = snapshot->mData;
    const uint8_t *back = snapshot->mData + availToRead;
    const uint8_t *front = snapshot->mData;

    // Find last END_FMT. <back> is sitting on an entry which might be the middle of a FormatEntry.
    // We go backwards until we find an EVENT_END_FMT.
    uint8_t *lastEnd = findLastEntryOfType(front, back, EVENT_END_FMT);
    const uint8_t *lastEnd = findLastEntryOfTypes(front, back, endingTypes);
    if (lastEnd == nullptr) {
        snapshot->mEnd = snapshot->mBegin = FormatEntry::iterator(front);
        snapshot->mEnd = snapshot->mBegin = EntryIterator(front);
    } else {
        // end of snapshot points to after last END_FMT entry
        snapshot->mEnd = FormatEntry::iterator(lastEnd + Entry::kOverhead);
        snapshot->mEnd = EntryIterator(lastEnd).next();
        // find first START_FMT
        uint8_t *firstStart = nullptr;
        uint8_t *firstStartTmp = lastEnd;
        while ((firstStartTmp = findLastEntryOfType(front, firstStartTmp, EVENT_START_FMT))
        const uint8_t *firstStart = nullptr;
        const uint8_t *firstStartTmp = snapshot->mEnd;
        while ((firstStartTmp = findLastEntryOfTypes(front, firstStartTmp, startingTypes))
                != nullptr) {
            firstStart = firstStartTmp;
        }
@@ -674,7 +788,7 @@ std::unique_ptr<NBLog::Reader::Snapshot> NBLog::Reader::getSnapshot()
        if (firstStart == nullptr) {
            snapshot->mBegin = snapshot->mEnd;
        } else {
            snapshot->mBegin = FormatEntry::iterator(firstStart);
            snapshot->mBegin = EntryIterator(firstStart);
        }
    }

@@ -686,6 +800,10 @@ std::unique_ptr<NBLog::Reader::Snapshot> NBLog::Reader::getSnapshot()

}

inline static int deltaMs(int64_t t1, int64_t t2) {
    return (t2 - t1) / (1000 * 1000);
}

void NBLog::Reader::dump(int fd, size_t indent, NBLog::Reader::Snapshot &snapshot)
{
#if 0
@@ -712,7 +830,7 @@ void NBLog::Reader::dump(int fd, size_t indent, NBLog::Reader::Snapshot &snapsho
    mFd = fd;
    mIndent = indent;
    String8 timestamp, body;
    size_t lost = snapshot.lost() + (snapshot.begin() - FormatEntry::iterator(snapshot.data()));
    size_t lost = snapshot.lost() + (snapshot.begin() - EntryIterator(snapshot.data()));
    if (lost > 0) {
        body.appendFormat("warning: lost %zu bytes worth of events", lost);
        // TODO timestamp empty here, only other choice to wait for the first timestamp event in the
@@ -730,6 +848,9 @@ void NBLog::Reader::dump(int fd, size_t indent, NBLog::Reader::Snapshot &snapsho
    }
    bool deferredTimestamp = false;
#endif
    std::map<std::pair<log_hash_t, int>, std::vector<int>> hists;
    std::map<std::pair<log_hash_t, int>, int64_t*> lastTSs;

    for (auto entry = snapshot.begin(); entry != snapshot.end();) {
        switch (entry->type) {
#if 0
@@ -801,6 +922,37 @@ void NBLog::Reader::dump(int fd, size_t indent, NBLog::Reader::Snapshot &snapsho
            // right now, this is the only supported case
            entry = handleFormat(FormatEntry(entry), &timestamp, &body);
            break;
        case EVENT_HISTOGRAM_ENTRY_TS: {
            HistTsEntryWithAuthor *data = (HistTsEntryWithAuthor *) (entry->data);
            // TODO This memcpies are here to avoid unaligned memory access crash.
            // There's probably a more efficient way to do it
            log_hash_t hash;
            memcpy(&hash, &(data->hash), sizeof(hash));
            const std::pair<log_hash_t, int> key(hash, data->author);
            if (lastTSs[key] != nullptr) {
                int64_t ts1;
                memcpy(&ts1, lastTSs[key], sizeof(ts1));
                int64_t ts2;
                memcpy(&ts2, &data->ts, sizeof(ts2));
                // TODO might want to filter excessively high outliers, which are usually caused
                // by the thread being inactive.
                hists[key].push_back(deltaMs(ts1, ts2));
            }
            lastTSs[key] = &(data->ts);
            ++entry;
            break;
        }
        case EVENT_HISTOGRAM_FLUSH:
            body.appendFormat("Histograms:\n");
            for (auto const &hist : hists) {
                body.appendFormat("Histogram %X - ", (int)hist.first.first);
                handleAuthor(HistogramEntry(entry), &body);
                drawHistogram(&body, hist.second);
            }
            hists.clear();
            lastTSs.clear();
            ++entry;
            break;
        case EVENT_END_FMT:
            body.appendFormat("warning: got to end format event");
            ++entry;
@@ -845,10 +997,10 @@ bool NBLog::Reader::isIMemory(const sp<IMemory>& iMemory) const
}

void NBLog::appendTimestamp(String8 *body, const void *data) {
    struct timespec ts;
    memcpy(&ts, data, sizeof(struct timespec));
    body->appendFormat("[%d.%03d]", (int) ts.tv_sec,
                    (int) (ts.tv_nsec / 1000000));
    int64_t ts;
    memcpy(&ts, data, sizeof(ts));
    body->appendFormat("[%d.%03d]", (int) (ts / (1000 * 1000 * 1000)),
                    (int) ((ts / (1000 * 1000)) % 1000));
}

void NBLog::appendInt(String8 *body, const void *data) {
@@ -868,20 +1020,42 @@ void NBLog::appendPID(String8 *body, const void* data, size_t length) {
    body->appendFormat("<PID: %d, name: %.*s>", id, (int) (length - sizeof(pid_t)), name);
}

NBLog::FormatEntry::iterator NBLog::Reader::handleFormat(const FormatEntry &fmtEntry,
String8 NBLog::bufferDump(const uint8_t *buffer, size_t size)
{
    String8 str;
    str.append("[ ");
    for(size_t i = 0; i < size; i++)
    {
        str.appendFormat("%d ", buffer[i]);
    }
    str.append("]");
    return str;
}

String8 NBLog::bufferDump(const EntryIterator &it)
{
    return bufferDump(it, it->length + Entry::kOverhead);
}

NBLog::EntryIterator NBLog::Reader::handleFormat(const FormatEntry &fmtEntry,
                                                         String8 *timestamp,
                                                         String8 *body) {
    // log timestamp
    struct timespec ts = fmtEntry.timestamp();
    int64_t ts = fmtEntry.timestamp();
    timestamp->clear();
    timestamp->appendFormat("[%d.%03d]", (int) ts.tv_sec,
                    (int) (ts.tv_nsec / 1000000));
    timestamp->appendFormat("[%d.%03d]", (int) (ts / (1000 * 1000 * 1000)),
                    (int) ((ts / (1000 * 1000)) % 1000));

    // log unique hash
    log_hash_t hash = fmtEntry.hash();
    // print only lower 16bit of hash as hex and line as int to reduce spam in the log
    body->appendFormat("%.4X-%d ", (int)(hash >> 16) & 0xFFFF, (int) hash & 0xFFFF);

    // log author (if present)
    handleAuthor(fmtEntry, body);

    // log string
    NBLog::FormatEntry::iterator arg = fmtEntry.args();
    NBLog::EntryIterator arg = fmtEntry.args();

    const char* fmt = fmtEntry.formatString();
    size_t fmt_length = fmtEntry.formatStringLength();
@@ -954,6 +1128,75 @@ NBLog::FormatEntry::iterator NBLog::Reader::handleFormat(const FormatEntry &fmtE
    return arg;
}

static int widthOf(int x) {
    int width = 0;
    while (x > 0) {
        ++width;
        x /= 10;
    }
    return width;
}

static std::map<int, int> buildBuckets(const std::vector<int> &samples) {
    // TODO allow buckets of variable resolution
    std::map<int, int> buckets;
    for (int x : samples) {
        ++buckets[x];
    }
    return buckets;
}

// TODO put this function in separate file. Make it return a std::string instead of modifying body
void NBLog::Reader::drawHistogram(String8 *body, const std::vector<int> &samples, int maxHeight) {
    std::map<int, int> buckets = buildBuckets(samples);
    // TODO add option for log scale
    static const char *underscores = "________________";
    static const char *spaces = "                ";

    auto it = buckets.begin();
    int maxLabel = it->first;
    int maxVal = it->second;
    while (++it != buckets.end()) {
        if (it->first > maxLabel) {
            maxLabel = it->first;
        }
        if (it->second > maxVal) {
            maxVal = it->second;
        }
    }
    int height = maxVal;
    int leftPadding = widthOf(maxVal);
    int colWidth = std::max(std::max(widthOf(maxLabel) + 1, 3), leftPadding + 2);
    int scalingFactor = 1;
    if (height > maxHeight) {
        scalingFactor = (height + maxHeight) / maxHeight;
        height /= scalingFactor;
    }
    body->appendFormat("\n");
    body->appendFormat("%*s", leftPadding + 2, " ");
    for (auto const &x : buckets)
    {
        body->appendFormat("[%*d]", colWidth - 2, x.second);
    }
    body->appendFormat("\n");
    for (int row = height * scalingFactor; row > 0; row -= scalingFactor)
    {
        body->appendFormat("%*d|", leftPadding, row);
        for (auto const &x : buckets) {
            body->appendFormat("%.*s%s", colWidth - 2,
                   (row == scalingFactor) ? underscores : spaces,
                   x.second < row ? ((row == scalingFactor) ? "__" : "  ") : "[]");
        }
        body->appendFormat("\n");
    }
    body->appendFormat("%*s", leftPadding + 1, " ");
    for (auto const &x : buckets)
    {
        body->appendFormat("%*d", colWidth, x.first);
    }
    body->appendFormat("\n");
}

// ---------------------------------------------------------------------------

NBLog::Merger::Merger(const void *shared, size_t size):
@@ -973,26 +1216,25 @@ void NBLog::Merger::addReader(const NBLog::NamedReader &reader) {
// composed by a timestamp and the index of the snapshot where the timestamp came from
struct MergeItem
{
    struct timespec ts;
    int64_t ts;
    int index;
    MergeItem(struct timespec ts, int index): ts(ts), index(index) {}
    MergeItem(int64_t ts, int index): ts(ts), index(index) {}
};

// operators needed for priority queue in merge
bool operator>(const struct timespec &t1, const struct timespec &t2) {
    return t1.tv_sec > t2.tv_sec || (t1.tv_sec == t2.tv_sec && t1.tv_nsec > t2.tv_nsec);
}
// bool operator>(const int64_t &t1, const int64_t &t2) {
//     return t1.tv_sec > t2.tv_sec || (t1.tv_sec == t2.tv_sec && t1.tv_nsec > t2.tv_nsec);
// }

bool operator>(const struct MergeItem &i1, const struct MergeItem &i2) {
    return i1.ts > i2.ts ||
        (i1.ts.tv_sec == i2.ts.tv_sec && i1.ts.tv_nsec == i2.ts.tv_nsec && i1.index > i2.index);
    return i1.ts > i2.ts || (i1.ts == i2.ts && i1.index > i2.index);
}

// Merge registered readers, sorted by timestamp
void NBLog::Merger::merge() {
    int nLogs = mNamedReaders.size();
    std::vector<std::unique_ptr<NBLog::Reader::Snapshot>> snapshots(nLogs);
    std::vector<NBLog::FormatEntry::iterator> offsets(nLogs);
    std::vector<NBLog::EntryIterator> offsets(nLogs);
    for (int i = 0; i < nLogs; ++i) {
        snapshots[i] = mNamedReaders[i].reader()->getSnapshot();
        offsets[i] = snapshots[i]->begin();
@@ -1004,7 +1246,7 @@ void NBLog::Merger::merge() {
    for (int i = 0; i < nLogs; ++i)
    {
        if (offsets[i] != snapshots[i]->end()) {
            timespec ts = FormatEntry(offsets[i]).timestamp();
            int64_t ts = AbstractEntry::buildEntry(offsets[i])->timestamp();
            timestamps.emplace(ts, i);
        }
    }
@@ -1013,11 +1255,12 @@ void NBLog::Merger::merge() {
        // find minimum timestamp
        int index = timestamps.top().index;
        // copy it to the log, increasing offset
        offsets[index] = FormatEntry(offsets[index]).copyWithAuthor(mFifoWriter, index);
        offsets[index] = AbstractEntry::buildEntry(offsets[index])->copyWithAuthor(mFifoWriter,
                                                                                   index);
        // update data structures
        timestamps.pop();
        if (offsets[index] != snapshots[index]->end()) {
            timespec ts = FormatEntry(offsets[index]).timestamp();
            int64_t ts = AbstractEntry::buildEntry(offsets[index])->timestamp();
            timestamps.emplace(ts, index);
        }
    }
@@ -1030,11 +1273,10 @@ const std::vector<NBLog::NamedReader> *NBLog::Merger::getNamedReaders() const {
NBLog::MergeReader::MergeReader(const void *shared, size_t size, Merger &merger)
    : Reader(shared, size), mNamedReaders(merger.getNamedReaders()) {}

size_t NBLog::MergeReader::handleAuthor(const NBLog::FormatEntry &fmtEntry, String8 *body) {
    int author = fmtEntry.author();
void NBLog::MergeReader::handleAuthor(const NBLog::AbstractEntry &entry, String8 *body) {
    int author = entry.author();
    const char* name = (*mNamedReaders)[author].name();
    body->appendFormat("%s: ", name);
    return NBLog::Entry::kOverhead + sizeof(author);
}

NBLog::MergeThread::MergeThread(NBLog::Merger &merger)
+169 −79

File changed.

Preview size limit exceeded, changes collapsed.

+66 −1

File changed.

Preview size limit exceeded, changes collapsed.

+2 −1
Original line number Diff line number Diff line
@@ -49,7 +49,8 @@ private:
    // Internal dump
    static const int kDumpLockRetries = 50;
    static const int kDumpLockSleepUs = 20000;
    static const size_t kMergeBufferSize = 16 * 1024; // TODO determine good value for this
    // Size of merge buffer, in bytes
    static const size_t kMergeBufferSize = 64 * 1024; // TODO determine good value for this
    static bool dumpTryLock(Mutex& mutex);

    Mutex               mLock;