LogStatistics.cpp revision f48ea7c8dcfbf2220ececccb1f4fb2f42df9048c
1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <fcntl.h>
18#include <stdarg.h>
19#include <time.h>
20
21#include <log/logger.h>
22#include <private/android_filesystem_config.h>
23#include <utils/String8.h>
24
25#include "LogStatistics.h"
26
27PidStatistics::PidStatistics(pid_t pid, char *name)
28        : pid(pid)
29        , mSizesTotal(0)
30        , mElementsTotal(0)
31        , mSizes(0)
32        , mElements(0)
33        , name(name)
34        , mGone(false)
35{ }
36
37#ifdef DO_NOT_ERROR_IF_PIDSTATISTICS_USES_A_COPY_CONSTRUCTOR
38PidStatistics::PidStatistics(const PidStatistics &copy)
39        : pid(copy->pid)
40        , name(copy->name ? strdup(copy->name) : NULL)
41        , mSizesTotal(copy->mSizesTotal)
42        , mElementsTotal(copy->mElementsTotal)
43        , mSizes(copy->mSizes)
44        , mElements(copy->mElements)
45        , mGone(copy->mGone)
46{ }
47#endif
48
49PidStatistics::~PidStatistics() {
50    free(name);
51}
52
53bool PidStatistics::pidGone() {
54    if (mGone || (pid == gone)) {
55        return true;
56    }
57    if (pid == 0) {
58        return false;
59    }
60    if (kill(pid, 0) && (errno != EPERM)) {
61        mGone = true;
62        return true;
63    }
64    return false;
65}
66
67void PidStatistics::setName(char *new_name) {
68    free(name);
69    name = new_name;
70}
71
72void PidStatistics::add(unsigned short size) {
73    mSizesTotal += size;
74    ++mElementsTotal;
75    mSizes += size;
76    ++mElements;
77}
78
79bool PidStatistics::subtract(unsigned short size) {
80    mSizes -= size;
81    --mElements;
82    return (mElements == 0) && pidGone();
83}
84
85void PidStatistics::addTotal(size_t size, size_t element) {
86    if (pid == gone) {
87        mSizesTotal += size;
88        mElementsTotal += element;
89    }
90}
91
92// must call free to release return value
93//  If only we could sniff our own logs for:
94//   <time> <pid> <pid> E AndroidRuntime: Process: <name>, PID: <pid>
95//  which debuggerd prints as a process is crashing.
96char *PidStatistics::pidToName(pid_t pid) {
97    char *retval = NULL;
98    if (pid == 0) { // special case from auditd for kernel
99        retval = strdup("logd.auditd");
100    } else if (pid != gone) {
101        char buffer[512];
102        snprintf(buffer, sizeof(buffer), "/proc/%u/cmdline", pid);
103        int fd = open(buffer, O_RDONLY);
104        if (fd >= 0) {
105            ssize_t ret = read(fd, buffer, sizeof(buffer));
106            if (ret > 0) {
107                buffer[sizeof(buffer)-1] = '\0';
108                // frameworks intermediate state
109                if (strcmp(buffer, "<pre-initialized>")) {
110                    retval = strdup(buffer);
111                }
112            }
113            close(fd);
114        }
115    }
116    return retval;
117}
118
119UidStatistics::UidStatistics(uid_t uid)
120        : uid(uid)
121        , mSizes(0)
122        , mElements(0) {
123    Pids.clear();
124}
125
126UidStatistics::~UidStatistics() {
127    PidStatisticsCollection::iterator it;
128    for (it = begin(); it != end();) {
129        delete (*it);
130        it = erase(it);
131    }
132}
133
134void UidStatistics::add(unsigned short size, pid_t pid) {
135    mSizes += size;
136    ++mElements;
137
138    PidStatistics *p = NULL;
139    PidStatisticsCollection::iterator last;
140    PidStatisticsCollection::iterator it;
141    for (last = it = begin(); it != end(); last = it, ++it) {
142        p = *it;
143        if (pid == p->getPid()) {
144            p->add(size);
145            return;
146        }
147    }
148    // insert if the gone entry.
149    bool insert_before_last = (last != it) && p && (p->getPid() == p->gone);
150    p = new PidStatistics(pid, pidToName(pid));
151    if (insert_before_last) {
152        insert(last, p);
153    } else {
154        push_back(p);
155    }
156    p->add(size);
157}
158
159void UidStatistics::subtract(unsigned short size, pid_t pid) {
160    mSizes -= size;
161    --mElements;
162
163    PidStatisticsCollection::iterator it;
164    for (it = begin(); it != end(); ++it) {
165        PidStatistics *p = *it;
166        if (pid == p->getPid()) {
167            if (p->subtract(size)) {
168                size_t szsTotal = p->sizesTotal();
169                size_t elsTotal = p->elementsTotal();
170                delete p;
171                erase(it);
172                it = end();
173                --it;
174                if (it == end()) {
175                    p = new PidStatistics(p->gone);
176                    push_back(p);
177                } else {
178                    p = *it;
179                    if (p->getPid() != p->gone) {
180                        p = new PidStatistics(p->gone);
181                        push_back(p);
182                    }
183                }
184                p->addTotal(szsTotal, elsTotal);
185            }
186            return;
187        }
188    }
189}
190
191void UidStatistics::sort() {
192    for (bool pass = true; pass;) {
193        pass = false;
194        PidStatisticsCollection::iterator it = begin();
195        if (it != end()) {
196            PidStatisticsCollection::iterator lt = it;
197            PidStatistics *l = (*lt);
198            while (++it != end()) {
199                PidStatistics *n = (*it);
200                if ((n->getPid() != n->gone) && (n->sizes() > l->sizes())) {
201                    pass = true;
202                    erase(it);
203                    insert(lt, n);
204                    it = lt;
205                    n = l;
206                }
207                lt = it;
208                l = n;
209            }
210        }
211    }
212}
213
214size_t UidStatistics::sizes(pid_t pid) {
215    if (pid == pid_all) {
216        return sizes();
217    }
218
219    PidStatisticsCollection::iterator it;
220    for (it = begin(); it != end(); ++it) {
221        PidStatistics *p = *it;
222        if (pid == p->getPid()) {
223            return p->sizes();
224        }
225    }
226    return 0;
227}
228
229size_t UidStatistics::elements(pid_t pid) {
230    if (pid == pid_all) {
231        return elements();
232    }
233
234    PidStatisticsCollection::iterator it;
235    for (it = begin(); it != end(); ++it) {
236        PidStatistics *p = *it;
237        if (pid == p->getPid()) {
238            return p->elements();
239        }
240    }
241    return 0;
242}
243
244size_t UidStatistics::sizesTotal(pid_t pid) {
245    size_t sizes = 0;
246    PidStatisticsCollection::iterator it;
247    for (it = begin(); it != end(); ++it) {
248        PidStatistics *p = *it;
249        if ((pid == pid_all) || (pid == p->getPid())) {
250            sizes += p->sizesTotal();
251        }
252    }
253    return sizes;
254}
255
256size_t UidStatistics::elementsTotal(pid_t pid) {
257    size_t elements = 0;
258    PidStatisticsCollection::iterator it;
259    for (it = begin(); it != end(); ++it) {
260        PidStatistics *p = *it;
261        if ((pid == pid_all) || (pid == p->getPid())) {
262            elements += p->elementsTotal();
263        }
264    }
265    return elements;
266}
267
268LidStatistics::LidStatistics() {
269    Uids.clear();
270}
271
272LidStatistics::~LidStatistics() {
273    UidStatisticsCollection::iterator it;
274    for (it = begin(); it != end();) {
275        delete (*it);
276        it = Uids.erase(it);
277    }
278}
279
280void LidStatistics::add(unsigned short size, uid_t uid, pid_t pid) {
281    UidStatistics *u;
282    UidStatisticsCollection::iterator it;
283    UidStatisticsCollection::iterator last;
284
285    if (uid == (uid_t) -1) { // init
286        uid = (uid_t) AID_ROOT;
287    }
288
289    for (last = it = begin(); it != end(); last = it, ++it) {
290        u = *it;
291        if (uid == u->getUid()) {
292            u->add(size, pid);
293            if ((last != it) && ((*last)->sizesTotal() < u->sizesTotal())) {
294                Uids.erase(it);
295                Uids.insert(last, u);
296            }
297            return;
298        }
299    }
300    u = new UidStatistics(uid);
301    if ((last != it) && ((*last)->sizesTotal() < (size_t) size)) {
302        Uids.insert(last, u);
303    } else {
304        Uids.push_back(u);
305    }
306    u->add(size, pid);
307}
308
309void LidStatistics::subtract(unsigned short size, uid_t uid, pid_t pid) {
310    if (uid == (uid_t) -1) { // init
311        uid = (uid_t) AID_ROOT;
312    }
313
314    UidStatisticsCollection::iterator it;
315    for (it = begin(); it != end(); ++it) {
316        UidStatistics *u = *it;
317        if (uid == u->getUid()) {
318            u->subtract(size, pid);
319            return;
320        }
321    }
322}
323
324void LidStatistics::sort() {
325    for (bool pass = true; pass;) {
326        pass = false;
327        UidStatisticsCollection::iterator it = begin();
328        if (it != end()) {
329            UidStatisticsCollection::iterator lt = it;
330            UidStatistics *l = (*lt);
331            while (++it != end()) {
332                UidStatistics *n = (*it);
333                if (n->sizes() > l->sizes()) {
334                    pass = true;
335                    Uids.erase(it);
336                    Uids.insert(lt, n);
337                    it = lt;
338                    n = l;
339                }
340                lt = it;
341                l = n;
342            }
343        }
344    }
345}
346
347size_t LidStatistics::sizes(uid_t uid, pid_t pid) {
348    size_t sizes = 0;
349    UidStatisticsCollection::iterator it;
350    for (it = begin(); it != end(); ++it) {
351        UidStatistics *u = *it;
352        if ((uid == uid_all) || (uid == u->getUid())) {
353            sizes += u->sizes(pid);
354        }
355    }
356    return sizes;
357}
358
359size_t LidStatistics::elements(uid_t uid, pid_t pid) {
360    size_t elements = 0;
361    UidStatisticsCollection::iterator it;
362    for (it = begin(); it != end(); ++it) {
363        UidStatistics *u = *it;
364        if ((uid == uid_all) || (uid == u->getUid())) {
365            elements += u->elements(pid);
366        }
367    }
368    return elements;
369}
370
371size_t LidStatistics::sizesTotal(uid_t uid, pid_t pid) {
372    size_t sizes = 0;
373    UidStatisticsCollection::iterator it;
374    for (it = begin(); it != end(); ++it) {
375        UidStatistics *u = *it;
376        if ((uid == uid_all) || (uid == u->getUid())) {
377            sizes += u->sizesTotal(pid);
378        }
379    }
380    return sizes;
381}
382
383size_t LidStatistics::elementsTotal(uid_t uid, pid_t pid) {
384    size_t elements = 0;
385    UidStatisticsCollection::iterator it;
386    for (it = begin(); it != end(); ++it) {
387        UidStatistics *u = *it;
388        if ((uid == uid_all) || (uid == u->getUid())) {
389            elements += u->elementsTotal(pid);
390        }
391    }
392    return elements;
393}
394
395LogStatistics::LogStatistics()
396        : mStatistics(false)
397        , dgramQlenStatistics(false)
398        , start(CLOCK_MONOTONIC) {
399    log_id_for_each(i) {
400        mSizes[i] = 0;
401        mElements[i] = 0;
402    }
403
404    for(unsigned short bucket = 0; dgramQlen(bucket); ++bucket) {
405        mMinimum[bucket].tv_sec = mMinimum[bucket].tv_sec_max;
406        mMinimum[bucket].tv_nsec = mMinimum[bucket].tv_nsec_max;
407    }
408}
409
410//   Each bucket below represents a dgramQlen of log messages. By
411//   finding the minimum period of time from start to finish
412//   of each dgramQlen, we can get a performance expectation for
413//   the user space logger. The net result is that the period
414//   of time divided by the dgramQlen will give us the average time
415//   between log messages; at the point where the average time
416//   is greater than the throughput capability of the logger
417//   we will not longer require the benefits of the FIFO formed
418//   by max_dgram_qlen. We will also expect to see a very visible
419//   knee in the average time between log messages at this point,
420//   so we do not necessarily have to compare the rate against the
421//   measured performance (BM_log_maximum_retry) of the logger.
422//
423//   for example (reformatted):
424//
425//       Minimum time between log events per dgramQlen:
426//       1   2   3   5   10  20  30  50  100  200 300 400 500 600
427//       5u2 12u 13u 15u 16u 27u 30u 36u 407u 3m1 3m3 3m9 3m9 5m5
428//
429//   demonstrates a clear knee rising at 100, so this means that for this
430//   case max_dgram_qlen = 100 would be more than sufficient to handle the
431//   worst that the system could stuff into the logger. The
432//   BM_log_maximum_retry performance (derated by the log collection) on the
433//   same system was 33.2us so we would almost be fine with max_dgram_qlen = 50.
434//   BM_log_maxumum_retry with statistics off is roughly 20us, so
435//   max_dgram_qlen = 20 would work. We will be more than willing to have
436//   a large engineering margin so the rule of thumb that lead us to 100 is
437//   fine.
438//
439// bucket dgramQlen are tuned for /proc/sys/net/unix/max_dgram_qlen = 300
440const unsigned short LogStatistics::mBuckets[] = {
441    1, 2, 3, 5, 10, 20, 30, 50, 100, 200, 300, 400, 500, 600
442};
443
444unsigned short LogStatistics::dgramQlen(unsigned short bucket) {
445    if (bucket >= sizeof(mBuckets) / sizeof(mBuckets[0])) {
446        return 0;
447    }
448    return mBuckets[bucket];
449}
450
451unsigned long long LogStatistics::minimum(unsigned short bucket) {
452    if (mMinimum[bucket].tv_sec == mMinimum[bucket].tv_sec_max) {
453        return 0;
454    }
455    return mMinimum[bucket].nsec();
456}
457
458void LogStatistics::recordDiff(log_time diff, unsigned short bucket) {
459    if ((diff.tv_sec || diff.tv_nsec) && (mMinimum[bucket] > diff)) {
460        mMinimum[bucket] = diff;
461    }
462}
463
464void LogStatistics::add(unsigned short size,
465                        log_id_t log_id, uid_t uid, pid_t pid) {
466    mSizes[log_id] += size;
467    ++mElements[log_id];
468    if (!mStatistics) {
469        return;
470    }
471    id(log_id).add(size, uid, pid);
472}
473
474void LogStatistics::subtract(unsigned short size,
475                             log_id_t log_id, uid_t uid, pid_t pid) {
476    mSizes[log_id] -= size;
477    --mElements[log_id];
478    if (!mStatistics) {
479        return;
480    }
481    id(log_id).subtract(size, uid, pid);
482}
483
484size_t LogStatistics::sizes(log_id_t log_id, uid_t uid, pid_t pid) {
485    if (log_id != log_id_all) {
486        return id(log_id).sizes(uid, pid);
487    }
488    size_t sizes = 0;
489    log_id_for_each(i) {
490        sizes += id(i).sizes(uid, pid);
491    }
492    return sizes;
493}
494
495size_t LogStatistics::elements(log_id_t log_id, uid_t uid, pid_t pid) {
496    if (log_id != log_id_all) {
497        return id(log_id).elements(uid, pid);
498    }
499    size_t elements = 0;
500    log_id_for_each(i) {
501        elements += id(i).elements(uid, pid);
502    }
503    return elements;
504}
505
506size_t LogStatistics::sizesTotal(log_id_t log_id, uid_t uid, pid_t pid) {
507    if (log_id != log_id_all) {
508        return id(log_id).sizesTotal(uid, pid);
509    }
510    size_t sizes = 0;
511    log_id_for_each(i) {
512        sizes += id(i).sizesTotal(uid, pid);
513    }
514    return sizes;
515}
516
517size_t LogStatistics::elementsTotal(log_id_t log_id, uid_t uid, pid_t pid) {
518    if (log_id != log_id_all) {
519        return id(log_id).elementsTotal(uid, pid);
520    }
521    size_t elements = 0;
522    log_id_for_each(i) {
523        elements += id(i).elementsTotal(uid, pid);
524    }
525    return elements;
526}
527
528void LogStatistics::format(char **buf,
529                           uid_t uid, unsigned int logMask, log_time oldest) {
530    static const unsigned short spaces_current = 13;
531    static const unsigned short spaces_total = 19;
532
533    if (*buf) {
534        free(*buf);
535        *buf = NULL;
536    }
537
538    android::String8 string("        span -> size/num");
539    size_t oldLength;
540    short spaces = 2;
541
542    log_id_for_each(i) {
543        if (!(logMask & (1 << i))) {
544            continue;
545        }
546        oldLength = string.length();
547        if (spaces < 0) {
548            spaces = 0;
549        }
550        string.appendFormat("%*s%s", spaces, "", android_log_id_to_name(i));
551        spaces += spaces_total + oldLength - string.length();
552
553        LidStatistics &l = id(i);
554        l.sort();
555
556        UidStatisticsCollection::iterator iu;
557        for (iu = l.begin(); iu != l.end(); ++iu) {
558            (*iu)->sort();
559        }
560    }
561
562    spaces = 1;
563    log_time t(CLOCK_MONOTONIC);
564    unsigned long long d;
565    if (mStatistics) {
566        d = t.nsec() - start.nsec();
567        string.appendFormat("\nTotal%4llu:%02llu:%02llu.%09llu",
568                  d / NS_PER_SEC / 60 / 60, (d / NS_PER_SEC / 60) % 60,
569                  (d / NS_PER_SEC) % 60, d % NS_PER_SEC);
570
571        log_id_for_each(i) {
572            if (!(logMask & (1 << i))) {
573                continue;
574            }
575            oldLength = string.length();
576            if (spaces < 0) {
577                spaces = 0;
578            }
579            string.appendFormat("%*s%zu/%zu", spaces, "",
580                                sizesTotal(i), elementsTotal(i));
581            spaces += spaces_total + oldLength - string.length();
582        }
583        spaces = 1;
584    }
585
586    d = t.nsec() - oldest.nsec();
587    string.appendFormat("\nNow%6llu:%02llu:%02llu.%09llu",
588                  d / NS_PER_SEC / 60 / 60, (d / NS_PER_SEC / 60) % 60,
589                  (d / NS_PER_SEC) % 60, d % NS_PER_SEC);
590
591    log_id_for_each(i) {
592        if (!(logMask & (1 << i))) {
593            continue;
594        }
595
596        size_t els = elements(i);
597        if (els) {
598            oldLength = string.length();
599            if (spaces < 0) {
600                spaces = 0;
601            }
602            string.appendFormat("%*s%zu/%zu", spaces, "", sizes(i), els);
603            spaces -= string.length() - oldLength;
604        }
605        spaces += spaces_total;
606    }
607
608    // Construct list of worst spammers by Pid
609    static const unsigned char num_spammers = 10;
610    bool header = false;
611
612    log_id_for_each(i) {
613        if (!(logMask & (1 << i))) {
614            continue;
615        }
616
617        PidStatisticsCollection pids;
618        pids.clear();
619
620        LidStatistics &l = id(i);
621        UidStatisticsCollection::iterator iu;
622        for (iu = l.begin(); iu != l.end(); ++iu) {
623            UidStatistics &u = *(*iu);
624            PidStatisticsCollection::iterator ip;
625            for (ip = u.begin(); ip != u.end(); ++ip) {
626                PidStatistics *p = (*ip);
627                if (p->getPid() == p->gone) {
628                    break;
629                }
630
631                size_t mySizes = p->sizes();
632
633                PidStatisticsCollection::iterator q;
634                unsigned char num = 0;
635                for (q = pids.begin(); q != pids.end(); ++q) {
636                    if (mySizes > (*q)->sizes()) {
637                        pids.insert(q, p);
638                        break;
639                    }
640                    // do we need to traverse deeper in the list?
641                    if (++num > num_spammers) {
642                        break;
643                    }
644                }
645                if (q == pids.end()) {
646                   pids.push_back(p);
647                }
648            }
649        }
650
651        size_t threshold = sizes(i);
652        if (threshold < 65536) {
653            threshold = 65536;
654        }
655        threshold /= 100;
656
657        PidStatisticsCollection::iterator pt = pids.begin();
658
659        for(int line = 0;
660                (pt != pids.end()) && (line < num_spammers);
661                ++line, pt = pids.erase(pt)) {
662            PidStatistics *p = *pt;
663
664            size_t sizes = p->sizes();
665            if (sizes < threshold) {
666                break;
667            }
668
669            char *name = p->getName();
670            pid_t pid = p->getPid();
671            if (!name || !*name) {
672                name = pidToName(pid);
673                if (name) {
674                    if (*name) {
675                        p->setName(name);
676                    } else {
677                        free(name);
678                        name = NULL;
679                    }
680                }
681            }
682
683            if (!header) {
684                string.appendFormat("\n\nChattiest clients:\n"
685                                    "log id %-*s PID[?] name",
686                                    spaces_total, "size/total");
687                header = true;
688            }
689
690            size_t sizesTotal = p->sizesTotal();
691
692            android::String8 sz("");
693            if (sizes == sizesTotal) {
694                sz.appendFormat("%zu", sizes);
695            } else {
696                sz.appendFormat("%zu/%zu", sizes, sizesTotal);
697            }
698
699            android::String8 pd("");
700            pd.appendFormat("%u%c", pid, p->pidGone() ? '?' : ' ');
701
702            string.appendFormat("\n%-7s%-*s %-7s%s",
703                                line ? "" : android_log_id_to_name(i),
704                                spaces_total, sz.string(), pd.string(),
705                                name ? name : "");
706        }
707
708        pids.clear();
709    }
710
711    if (dgramQlenStatistics) {
712        const unsigned short spaces_time = 6;
713        const unsigned long long max_seconds = 100000;
714        spaces = 0;
715        string.append("\n\nMinimum time between log events per max_dgram_qlen:\n");
716        for(unsigned short i = 0; dgramQlen(i); ++i) {
717            oldLength = string.length();
718            if (spaces < 0) {
719                spaces = 0;
720            }
721            string.appendFormat("%*s%u", spaces, "", dgramQlen(i));
722            spaces += spaces_time + oldLength - string.length();
723        }
724        string.append("\n");
725        spaces = 0;
726        unsigned short n;
727        for(unsigned short i = 0; (n = dgramQlen(i)); ++i) {
728            unsigned long long duration = minimum(i);
729            if (duration) {
730                duration /= n;
731                if (duration >= (NS_PER_SEC * max_seconds)) {
732                    duration = NS_PER_SEC * (max_seconds - 1);
733                }
734                oldLength = string.length();
735                if (spaces < 0) {
736                    spaces = 0;
737                }
738                string.appendFormat("%*s", spaces, "");
739                if (duration >= (NS_PER_SEC * 10)) {
740                    string.appendFormat("%llu",
741                        (duration + (NS_PER_SEC / 2))
742                            / NS_PER_SEC);
743                } else if (duration >= (NS_PER_SEC / (1000 / 10))) {
744                    string.appendFormat("%llum",
745                        (duration + (NS_PER_SEC / 2 / 1000))
746                            / (NS_PER_SEC / 1000));
747                } else if (duration >= (NS_PER_SEC / (1000000 / 10))) {
748                    string.appendFormat("%lluu",
749                        (duration + (NS_PER_SEC / 2 / 1000000))
750                            / (NS_PER_SEC / 1000000));
751                } else {
752                    string.appendFormat("%llun", duration);
753                }
754                spaces -= string.length() - oldLength;
755            }
756            spaces += spaces_time;
757        }
758    }
759
760    log_id_for_each(i) {
761        if (!(logMask & (1 << i))) {
762            continue;
763        }
764
765        header = false;
766        bool first = true;
767
768        UidStatisticsCollection::iterator ut;
769        for(ut = id(i).begin(); ut != id(i).end(); ++ut) {
770            UidStatistics *up = *ut;
771            if ((uid != AID_ROOT) && (uid != up->getUid())) {
772                continue;
773            }
774
775            PidStatisticsCollection::iterator pt = up->begin();
776            if (pt == up->end()) {
777                continue;
778            }
779
780            android::String8 intermediate;
781
782            if (!header) {
783                // header below tuned to match spaces_total and spaces_current
784                spaces = 0;
785                intermediate = string.format("%s: UID/PID Total size/num",
786                                             android_log_id_to_name(i));
787                string.appendFormat("\n\n%-31sNow          "
788                                         "UID/PID[?]  Total              Now",
789                                    intermediate.string());
790                intermediate.clear();
791                header = true;
792            }
793
794            bool oneline = ++pt == up->end();
795            --pt;
796
797            if (!oneline) {
798                first = true;
799            } else if (!first && (spaces > 0)) {
800                string.appendFormat("%*s", spaces, "");
801            }
802            spaces = 0;
803
804            uid_t u = up->getUid();
805            PidStatistics *pp = *pt;
806            pid_t p = pp->getPid();
807
808            if (!oneline) {
809                intermediate = string.format("%d", u);
810            } else if (p == PidStatistics::gone) {
811                intermediate = string.format("%d/?", u);
812            } else if (pp->pidGone()) {
813                intermediate = string.format("%d/%d?", u, p);
814            } else {
815                intermediate = string.format("%d/%d", u, p);
816            }
817            string.appendFormat(first ? "\n%-12s" : "%-12s",
818                                intermediate.string());
819            intermediate.clear();
820
821            size_t elsTotal = up->elementsTotal();
822            oldLength = string.length();
823            string.appendFormat("%zu/%zu", up->sizesTotal(), elsTotal);
824            spaces += spaces_total + oldLength - string.length();
825
826            size_t els = up->elements();
827            if (els == elsTotal) {
828                if (spaces < 0) {
829                    spaces = 0;
830                }
831                string.appendFormat("%*s=", spaces, "");
832                spaces = -1;
833            } else if (els) {
834                oldLength = string.length();
835                if (spaces < 0) {
836                    spaces = 0;
837                }
838                string.appendFormat("%*s%zu/%zu", spaces, "", up->sizes(), els);
839                spaces -= string.length() - oldLength;
840            }
841            spaces += spaces_current;
842
843            first = !first;
844
845            if (oneline) {
846                continue;
847            }
848
849            size_t gone_szs = 0;
850            size_t gone_els = 0;
851
852            for(; pt != up->end(); ++pt) {
853                pp = *pt;
854                p = pp->getPid();
855
856                // If a PID no longer has any current logs, and is not
857                // active anymore, skip & report totals for gone.
858                elsTotal = pp->elementsTotal();
859                size_t szsTotal = pp->sizesTotal();
860                if (p == pp->gone) {
861                    gone_szs += szsTotal;
862                    gone_els += elsTotal;
863                    continue;
864                }
865                els = pp->elements();
866                bool gone = pp->pidGone();
867                if (gone && (els == 0)) {
868                    // ToDo: garbage collection: move this statistical bucket
869                    //       from its current UID/PID to UID/? (races and
870                    //       wrap around are our achilles heel). Below is
871                    //       merely lipservice to catch PIDs that were still
872                    //       around when the stats were pruned to zero.
873                    gone_szs += szsTotal;
874                    gone_els += elsTotal;
875                    continue;
876                }
877
878                if (!first && (spaces > 0)) {
879                    string.appendFormat("%*s", spaces, "");
880                }
881                spaces = 0;
882
883                intermediate = string.format(gone ? "%d/%d?" : "%d/%d", u, p);
884                string.appendFormat(first ? "\n%-12s" : "%-12s",
885                                    intermediate.string());
886                intermediate.clear();
887
888                oldLength = string.length();
889                string.appendFormat("%zu/%zu", szsTotal, elsTotal);
890                spaces += spaces_total + oldLength - string.length();
891
892                if (els == elsTotal) {
893                    if (spaces < 0) {
894                        spaces = 0;
895                    }
896                    string.appendFormat("%*s=", spaces, "");
897                    spaces = -1;
898                } else if (els) {
899                    oldLength = string.length();
900                    if (spaces < 0) {
901                        spaces = 0;
902                    }
903                    string.appendFormat("%*s%zu/%zu", spaces, "",
904                                        pp->sizes(), els);
905                    spaces -= string.length() - oldLength;
906                }
907                spaces += spaces_current;
908
909                first = !first;
910            }
911
912            if (gone_els) {
913                if (!first && (spaces > 0)) {
914                    string.appendFormat("%*s", spaces, "");
915                }
916
917                intermediate = string.format("%d/?", u);
918                string.appendFormat(first ? "\n%-12s" : "%-12s",
919                                    intermediate.string());
920                intermediate.clear();
921
922                spaces = spaces_total + spaces_current;
923
924                oldLength = string.length();
925                string.appendFormat("%zu/%zu", gone_szs, gone_els);
926                spaces -= string.length() - oldLength;
927
928                first = !first;
929            }
930        }
931    }
932
933    *buf = strdup(string.string());
934}
935
936uid_t LogStatistics::pidToUid(pid_t pid) {
937    log_id_for_each(i) {
938        LidStatistics &l = id(i);
939        UidStatisticsCollection::iterator iu;
940        for (iu = l.begin(); iu != l.end(); ++iu) {
941            UidStatistics &u = *(*iu);
942            PidStatisticsCollection::iterator ip;
943            for (ip = u.begin(); ip != u.end(); ++ip) {
944                if ((*ip)->getPid() == pid) {
945                    return u.getUid();
946                }
947            }
948        }
949    }
950    return getuid(); // associate this with the logger
951}
952