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

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

Merge changes I10d5387a,If73be46d

* changes:
  NBLog add log scale to histograms
  NBLog improve Histograms:
parents 7f2c3e45 4f03349c
Loading
Loading
Loading
Loading
+70 −33
Original line number Diff line number Diff line
@@ -30,7 +30,6 @@
#include <utils/Log.h>
#include <utils/String8.h>

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

@@ -848,8 +847,6 @@ 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) {
@@ -928,31 +925,38 @@ void NBLog::Reader::dump(int fd, size_t indent, NBLog::Reader::Snapshot &snapsho
            // There's probably a more efficient way to do it
            log_hash_t hash;
            memcpy(&hash, &(data->hash), sizeof(hash));
            int64_t ts;
            memcpy(&ts, &data->ts, sizeof(ts));
            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);
            mHists[key].push_back(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();
        case EVENT_HISTOGRAM_FLUSH: {
            HistogramEntry histEntry(entry);
            // Log timestamp
            int64_t ts = histEntry.timestamp();
            timestamp.clear();
            timestamp.appendFormat("[%d.%03d]", (int) (ts / (1000 * 1000 * 1000)),
                            (int) ((ts / (1000 * 1000)) % 1000));
            // Log histograms
            body.appendFormat("Histogram flush - ");
            handleAuthor(histEntry, &body);
            body.appendFormat("\n");
            for (auto hist = mHists.begin(); hist != mHists.end();) {
                if (hist->first.second == histEntry.author()) {
                    body.appendFormat("Histogram %X", (int)hist->first.first);
                    drawHistogram(&body, hist->second, true/*logScale*/, indent + timestamp.size());
                    hist = mHists.erase(hist);
                } else {
                    ++hist;
                }
            }
            ++entry;
            break;
        }
        case EVENT_END_FMT:
            body.appendFormat("warning: got to end format event");
            ++entry;
@@ -1137,25 +1141,53 @@ static int widthOf(int x) {
    return width;
}

static std::map<int, int> buildBuckets(const std::vector<int> &samples) {
static std::map<int, int> buildBuckets(const std::vector<int64_t> &samples) {
    // TODO allow buckets of variable resolution
    std::map<int, int> buckets;
    for (int x : samples) {
        ++buckets[x];
    for (size_t i = 1; i < samples.size(); ++i) {
        ++buckets[deltaMs(samples[i - 1], samples[i])];
    }
    return buckets;
}

static inline uint32_t log2(uint32_t x) {
    // This works for x > 0
    return 31 - __builtin_clz(x);
}

// 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) {
/*
Example output:
[54.234] Histogram flush - AudioOut_D:
Histogram 33640BF1
            [ 1][ 1][ 1][ 3][54][69][ 1][ 2][ 1]
        64|                      []
        32|                  []  []
        16|                  []  []
         8|                  []  []
         4|                  []  []
         2|______________[]__[]__[]______[]____
              4   5   6   8   9  10  11  13  15
Notice that all values that fall in the same row have the same height (65 and 127 are displayed
identically). That's why exact counts are added at the top.
*/
void NBLog::Reader::drawHistogram(String8 *body,
                                  const std::vector<int64_t> &samples,
                                  bool logScale,
                                  int indent,
                                  int maxHeight) {
    if (samples.size() <= 1) {
        return;
    }
    std::map<int, int> buckets = buildBuckets(samples);
    // TODO add option for log scale
    // TODO consider changing all ints to uint32_t or uint64_t
    static const char *underscores = "________________";
    static const char *spaces = "                ";

    auto it = buckets.begin();
    int maxLabel = it->first;
    int maxVal = it->second;
    // Compute maximum values
    while (++it != buckets.end()) {
        if (it->first > maxLabel) {
            maxLabel = it->first;
@@ -1164,31 +1196,36 @@ void NBLog::Reader::drawHistogram(String8 *body, const std::vector<int> &samples
            maxVal = it->second;
        }
    }
    int height = maxVal;
    int height = (logScale) ? log2(maxVal) + 1 : maxVal; // maxVal > 0, safe to call log2
    int leftPadding = widthOf(maxVal);
    int colWidth = std::max(std::max(widthOf(maxLabel) + 1, 3), leftPadding + 2);
    int scalingFactor = 1;
    // scale data if it exceeds maximum height
    if (height > maxHeight) {
        scalingFactor = (height + maxHeight) / maxHeight;
        height /= scalingFactor;
    }
    body->appendFormat("\n");
    // write header line with bucket values
    body->appendFormat("\n%*s", indent, " ");
    body->appendFormat("%*s", leftPadding + 2, " ");
    for (auto const &x : buckets)
    {
        body->appendFormat("[%*d]", colWidth - 2, x.second);
    }
    body->appendFormat("\n");
    // write histogram ascii art
    body->appendFormat("\n%*s", indent, " ");
    for (int row = height * scalingFactor; row > 0; row -= scalingFactor)
    {
        body->appendFormat("%*d|", leftPadding, row);
        int value = ((logScale) ? (1 << row) : row);
        body->appendFormat("%*u|", leftPadding, value);
        for (auto const &x : buckets) {
            body->appendFormat("%.*s%s", colWidth - 2,
                   (row == scalingFactor) ? underscores : spaces,
                   x.second < row ? ((row == scalingFactor) ? "__" : "  ") : "[]");
                   (row <= scalingFactor) ? underscores : spaces,
                   x.second < value ? ((row <= scalingFactor) ? "__" : "  ") : "[]");
        }
        body->appendFormat("\n");
        body->appendFormat("\n%*s", indent, " ");
    }
    // write footer with bucket labels
    body->appendFormat("%*s", leftPadding + 1, " ");
    for (auto const &x : buckets)
    {
+5 −1
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include <utils/Mutex.h>
#include <utils/threads.h>

#include <map>
#include <set>
#include <vector>

@@ -454,6 +455,8 @@ private:
    audio_utils_fifo_reader * const mFifoReader;    // used to read from FIFO,
                                                    // non-NULL unless constructor fails

    std::map<std::pair<log_hash_t, int>, std::vector<int64_t>> mHists;

    void    dumpLine(const String8& timestamp, String8& body);

    EntryIterator   handleFormat(const FormatEntry &fmtEntry,
@@ -462,7 +465,8 @@ private:
    // dummy method for handling absent author entry
    virtual void handleAuthor(const AbstractEntry &fmtEntry, String8 *body) {}

    static void drawHistogram(String8 *body, const std::vector<int> &samples, int maxHeight = 10);
    static void drawHistogram(String8 *body, const std::vector<int64_t> &samples,
                              bool logScale, int indent = 0, int maxHeight = 10);

    // Searches for the last entry of type <type> in the range [front, back)
    // back has to be entry-aligned. Returns nullptr if none enconuntered.