LogStatistics.cpp revision fdabe7287609af29793bb6ca15712582ba082521
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) {
55        return true;
56    }
57    if (pid == gone) {
58        return true;
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
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 = Pids.erase(it);
126    }
127}
128
129void UidStatistics::add(unsigned short size, pid_t pid) {
130    mSizes += size;
131    ++mElements;
132
133    PidStatistics *p;
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 = (last != it) && (p->getPid() == p->gone);
145    p = new PidStatistics(pid, pidToName(pid));
146    if (insert) {
147        Pids.insert(last, p);
148    } else {
149        Pids.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                Pids.erase(it);
167                it = end();
168                --it;
169                if (it == end()) {
170                    p = new PidStatistics(p->gone);
171                    Pids.push_back(p);
172                } else {
173                    p = *it;
174                    if (p->getPid() != p->gone) {
175                        p = new PidStatistics(p->gone);
176                        Pids.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                    Pids.erase(it);
198                    Pids.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        : start(CLOCK_MONOTONIC) {
388    log_id_for_each(i) {
389        mSizes[i] = 0;
390        mElements[i] = 0;
391    }
392
393    dgram_qlen_statistics = false;
394    for(unsigned short bucket = 0; dgram_qlen(bucket); ++bucket) {
395        mMinimum[bucket].tv_sec = (uint32_t)-1;
396        mMinimum[bucket].tv_nsec = 999999999UL;
397    }
398}
399
400//   Each bucket below represents a dgram_qlen of log messages. By
401//   finding the minimum period of time from start to finish
402//   of each dgram_qlen, 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 dgram_qlen 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 dgram_qlen:
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 dgram_qlen 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::dgram_qlen(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 == LONG_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            sz.appendFormat((sizes != sizesTotal) ? "%zu/%zu" : "%zu",
675                            sizes, sizesTotal);
676
677            android::String8 pd("");
678            pd.appendFormat("%u%c", pid, p->pidGone() ? '?' : ' ');
679
680            string.appendFormat("\n%-7s%-*s %-7s%s",
681                                line ? "" : android_log_id_to_name(i),
682                                spaces_total, sz.string(), pd.string(),
683                                name ? name : "");
684        }
685
686        pids.clear();
687    }
688
689    if (dgram_qlen_statistics) {
690        const unsigned short spaces_time = 6;
691        const unsigned long long max_seconds = 100000;
692        spaces = 0;
693        string.append("\n\nMinimum time between log events per dgram_qlen:\n");
694        for(unsigned short i = 0; dgram_qlen(i); ++i) {
695            oldLength = string.length();
696            if (spaces < 0) {
697                spaces = 0;
698            }
699            string.appendFormat("%*s%u", spaces, "", dgram_qlen(i));
700            spaces += spaces_time + oldLength - string.length();
701        }
702        string.append("\n");
703        spaces = 0;
704        unsigned short n;
705        for(unsigned short i = 0; (n = dgram_qlen(i)); ++i) {
706            unsigned long long duration = minimum(i);
707            if (duration) {
708                duration /= n;
709                if (duration >= (NS_PER_SEC * max_seconds)) {
710                    duration = NS_PER_SEC * (max_seconds - 1);
711                }
712                oldLength = string.length();
713                if (spaces < 0) {
714                    spaces = 0;
715                }
716                string.appendFormat("%*s", spaces, "");
717                if (duration >= (NS_PER_SEC * 10)) {
718                    string.appendFormat("%llu",
719                        (duration + (NS_PER_SEC / 2))
720                            / NS_PER_SEC);
721                } else if (duration >= (NS_PER_SEC / (1000 / 10))) {
722                    string.appendFormat("%llum",
723                        (duration + (NS_PER_SEC / 2 / 1000))
724                            / (NS_PER_SEC / 1000));
725                } else if (duration >= (NS_PER_SEC / (1000000 / 10))) {
726                    string.appendFormat("%lluu",
727                         (duration + (NS_PER_SEC / 2 / 1000000))
728                             / (NS_PER_SEC / 1000000));
729                } else {
730                    string.appendFormat("%llun", duration);
731                }
732                spaces -= string.length() - oldLength;
733            }
734            spaces += spaces_time;
735        }
736    }
737
738    log_id_for_each(i) {
739        if (!(logMask & (1 << i))) {
740            continue;
741        }
742
743        header = false;
744        bool first = true;
745
746        UidStatisticsCollection::iterator ut;
747        for(ut = id(i).begin(); ut != id(i).end(); ++ut) {
748            UidStatistics *up = *ut;
749            if ((uid != AID_ROOT) && (uid != up->getUid())) {
750                continue;
751            }
752
753            PidStatisticsCollection::iterator pt = up->begin();
754            if (pt == up->end()) {
755                continue;
756            }
757
758            android::String8 intermediate;
759
760            if (!header) {
761                // header below tuned to match spaces_total and spaces_current
762                spaces = 0;
763                intermediate = string.format("%s: UID/PID Total size/num",
764                                             android_log_id_to_name(i));
765                string.appendFormat("\n\n%-31sNow          "
766                                         "UID/PID[?]  Total              Now",
767                                    intermediate.string());
768                intermediate.clear();
769                header = true;
770            }
771
772            bool oneline = ++pt == up->end();
773            --pt;
774
775            if (!oneline) {
776                first = true;
777            } else if (!first && (spaces > 0)) {
778                string.appendFormat("%*s", spaces, "");
779            }
780            spaces = 0;
781
782            uid_t u = up->getUid();
783            PidStatistics *pp = *pt;
784            pid_t p = pp->getPid();
785
786            intermediate = string.format(oneline
787                                             ? ((p == PidStatistics::gone)
788                                                 ? "%d/?"
789                                                 : "%d/%d%c")
790                                             : "%d",
791                                         u, p, pp->pidGone() ? '?' : '\0');
792            string.appendFormat(first ? "\n%-12s" : "%-12s",
793                                intermediate.string());
794            intermediate.clear();
795
796            size_t elsTotal = up->elementsTotal();
797            oldLength = string.length();
798            string.appendFormat("%zu/%zu", up->sizesTotal(), elsTotal);
799            spaces += spaces_total + oldLength - string.length();
800
801            size_t els = up->elements();
802            if (els == elsTotal) {
803                if (spaces < 0) {
804                    spaces = 0;
805                }
806                string.appendFormat("%*s=", spaces, "");
807                spaces = -1;
808            } else if (els) {
809                oldLength = string.length();
810                if (spaces < 0) {
811                    spaces = 0;
812                }
813                string.appendFormat("%*s%zu/%zu", spaces, "", up->sizes(), els);
814                spaces -= string.length() - oldLength;
815            }
816            spaces += spaces_current;
817
818            first = !first;
819
820            if (oneline) {
821                continue;
822            }
823
824            size_t gone_szs = 0;
825            size_t gone_els = 0;
826
827            for(; pt != up->end(); ++pt) {
828                pp = *pt;
829                p = pp->getPid();
830
831                // If a PID no longer has any current logs, and is not
832                // active anymore, skip & report totals for gone.
833                elsTotal = pp->elementsTotal();
834                size_t szsTotal = pp->sizesTotal();
835                if (p == pp->gone) {
836                    gone_szs += szsTotal;
837                    gone_els += elsTotal;
838                    continue;
839                }
840                els = pp->elements();
841                bool gone = pp->pidGone();
842                if (gone && (els == 0)) {
843                    // ToDo: garbage collection: move this statistical bucket
844                    //       from its current UID/PID to UID/? (races and
845                    //       wrap around are our achilles heel). Below is
846                    //       merely lipservice to catch PIDs that were still
847                    //       around when the stats were pruned to zero.
848                    gone_szs += szsTotal;
849                    gone_els += elsTotal;
850                    continue;
851                }
852
853                if (!first && (spaces > 0)) {
854                    string.appendFormat("%*s", spaces, "");
855                }
856                spaces = 0;
857
858                intermediate = string.format(gone ? "%d/%d?" : "%d/%d", u, p);
859                string.appendFormat(first ? "\n%-12s" : "%-12s",
860                                    intermediate.string());
861                intermediate.clear();
862
863                oldLength = string.length();
864                string.appendFormat("%zu/%zu", szsTotal, elsTotal);
865                spaces += spaces_total + oldLength - string.length();
866
867                if (els == elsTotal) {
868                    if (spaces < 0) {
869                        spaces = 0;
870                    }
871                    string.appendFormat("%*s=", spaces, "");
872                    spaces = -1;
873                } else if (els) {
874                    oldLength = string.length();
875                    if (spaces < 0) {
876                        spaces = 0;
877                    }
878                    string.appendFormat("%*s%zu/%zu", spaces, "",
879                                        pp->sizes(), els);
880                    spaces -= string.length() - oldLength;
881                }
882                spaces += spaces_current;
883
884                first = !first;
885            }
886
887            if (gone_els) {
888                if (!first && (spaces > 0)) {
889                    string.appendFormat("%*s", spaces, "");
890                }
891
892                intermediate = string.format("%d/?", u);
893                string.appendFormat(first ? "\n%-12s" : "%-12s",
894                                    intermediate.string());
895                intermediate.clear();
896
897                spaces = spaces_total + spaces_current;
898
899                oldLength = string.length();
900                string.appendFormat("%zu/%zu", gone_szs, gone_els);
901                spaces -= string.length() - oldLength;
902
903                first = !first;
904            }
905        }
906    }
907
908    *buf = strdup(string.string());
909}
910
911uid_t LogStatistics::pidToUid(pid_t pid) {
912    log_id_for_each(i) {
913        LidStatistics &l = id(i);
914        UidStatisticsCollection::iterator iu;
915        for (iu = l.begin(); iu != l.end(); ++iu) {
916            UidStatistics &u = *(*iu);
917            PidStatisticsCollection::iterator ip;
918            for (ip = u.begin(); ip != u.end(); ++ip) {
919                if ((*ip)->getPid() == pid) {
920                    return u.getUid();
921                }
922            }
923        }
924    }
925    return getuid(); // associate this with the logger
926}
927