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