blob: 5622ee5b1b99f51a57dd6f799aa8416a1f4de2c0 [file] [log] [blame]
Mark Salyzyn34facab2014-02-06 14:48:50 -08001/*
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 <stdarg.h>
18#include <time.h>
19
20#include <log/logger.h>
21#include <private/android_filesystem_config.h>
22#include <utils/String8.h>
23
24#include "LogStatistics.h"
25
26PidStatistics::PidStatistics(pid_t pid)
27 : pid(pid)
28 , mSizesTotal(0)
29 , mElementsTotal(0)
30 , mSizes(0)
31 , mElements(0) { }
32
33void PidStatistics::add(unsigned short size) {
34 mSizesTotal += size;
35 ++mElementsTotal;
36 mSizes += size;
37 ++mElements;
38}
39
40bool PidStatistics::subtract(unsigned short size) {
41 mSizes -= size;
42 --mElements;
43 return mElements == 0 && kill(pid, 0);
44}
45
46void PidStatistics::addTotal(size_t size, size_t element) {
47 if (pid == gone) {
48 mSizesTotal += size;
49 mElementsTotal += element;
50 }
51}
52
53UidStatistics::UidStatistics(uid_t uid)
54 : uid(uid) {
55 Pids.clear();
56}
57
58UidStatistics::~UidStatistics() {
59 PidStatisticsCollection::iterator it;
60 for (it = begin(); it != end();) {
61 delete (*it);
62 it = Pids.erase(it);
63 }
64}
65
66void UidStatistics::add(unsigned short size, pid_t pid) {
67 PidStatistics *p;
68 PidStatisticsCollection::iterator last;
69 PidStatisticsCollection::iterator it;
70 for (last = it = begin(); it != end(); last = it, ++it) {
71 p = *it;
72 if (pid == p->getPid()) {
73 p->add(size);
74 // poor-man sort, bubble upwards if bigger than last
75 if ((last != it) && ((*last)->sizesTotal() < p->sizesTotal())) {
76 Pids.erase(it);
77 Pids.insert(last, p);
78 }
79 return;
80 }
81 }
82 // poor-man sort, insert if bigger than last or last is the gone entry.
83 bool insert = (last != it)
84 && ((p->getPid() == p->gone)
85 || ((*last)->sizesTotal() < (size_t) size));
86 p = new PidStatistics(pid);
87 if (insert) {
88 Pids.insert(last, p);
89 } else {
90 Pids.push_back(p);
91 }
92 p->add(size);
93}
94
95void UidStatistics::subtract(unsigned short size, pid_t pid) {
96 PidStatisticsCollection::iterator it;
97 for (it = begin(); it != end(); ++it) {
98 PidStatistics *p = *it;
99 if (pid == p->getPid()) {
100 if (p->subtract(size)) {
101 size_t szsTotal = p->sizesTotal();
102 size_t elsTotal = p->elementsTotal();
103 delete p;
104 Pids.erase(it);
105 it = end();
106 --it;
107 if (it == end()) {
108 p = new PidStatistics(p->gone);
109 Pids.push_back(p);
110 } else {
111 p = *it;
112 if (p->getPid() != p->gone) {
113 p = new PidStatistics(p->gone);
114 Pids.push_back(p);
115 }
116 }
117 p->addTotal(szsTotal, elsTotal);
118 }
119 return;
120 }
121 }
122}
123
124size_t UidStatistics::sizes(pid_t pid) {
125 size_t sizes = 0;
126 PidStatisticsCollection::iterator it;
127 for (it = begin(); it != end(); ++it) {
128 PidStatistics *p = *it;
129 if ((pid == pid_all) || (pid == p->getPid())) {
130 sizes += p->sizes();
131 }
132 }
133 return sizes;
134}
135
136size_t UidStatistics::elements(pid_t pid) {
137 size_t elements = 0;
138 PidStatisticsCollection::iterator it;
139 for (it = begin(); it != end(); ++it) {
140 PidStatistics *p = *it;
141 if ((pid == pid_all) || (pid == p->getPid())) {
142 elements += p->elements();
143 }
144 }
145 return elements;
146}
147
148size_t UidStatistics::sizesTotal(pid_t pid) {
149 size_t sizes = 0;
150 PidStatisticsCollection::iterator it;
151 for (it = begin(); it != end(); ++it) {
152 PidStatistics *p = *it;
153 if ((pid == pid_all) || (pid == p->getPid())) {
154 sizes += p->sizesTotal();
155 }
156 }
157 return sizes;
158}
159
160size_t UidStatistics::elementsTotal(pid_t pid) {
161 size_t elements = 0;
162 PidStatisticsCollection::iterator it;
163 for (it = begin(); it != end(); ++it) {
164 PidStatistics *p = *it;
165 if ((pid == pid_all) || (pid == p->getPid())) {
166 elements += p->elementsTotal();
167 }
168 }
169 return elements;
170}
171
172LidStatistics::LidStatistics() {
173 Uids.clear();
174}
175
176LidStatistics::~LidStatistics() {
177 UidStatisticsCollection::iterator it;
178 for (it = begin(); it != end();) {
179 delete (*it);
180 it = Uids.erase(it);
181 }
182}
183
184void LidStatistics::add(unsigned short size, uid_t uid, pid_t pid) {
185 UidStatistics *u;
186 UidStatisticsCollection::iterator it;
187 UidStatisticsCollection::iterator last;
188
189 if (uid == (uid_t) -1) { // init
190 uid = (uid_t) AID_ROOT;
191 }
192
193 for (last = it = begin(); it != end(); last = it, ++it) {
194 u = *it;
195 if (uid == u->getUid()) {
196 u->add(size, pid);
197 if ((last != it) && ((*last)->sizesTotal() < u->sizesTotal())) {
198 Uids.erase(it);
199 Uids.insert(last, u);
200 }
201 return;
202 }
203 }
204 u = new UidStatistics(uid);
205 if ((last != it) && ((*last)->sizesTotal() < (size_t) size)) {
206 Uids.insert(last, u);
207 } else {
208 Uids.push_back(u);
209 }
210 u->add(size, pid);
211}
212
213void LidStatistics::subtract(unsigned short size, uid_t uid, pid_t pid) {
214 UidStatisticsCollection::iterator it;
215 for (it = begin(); it != end(); ++it) {
216 UidStatistics *u = *it;
217 if (uid == u->getUid()) {
218 u->subtract(size, pid);
219 return;
220 }
221 }
222}
223
224size_t LidStatistics::sizes(uid_t uid, pid_t pid) {
225 size_t sizes = 0;
226 UidStatisticsCollection::iterator it;
227 for (it = begin(); it != end(); ++it) {
228 UidStatistics *u = *it;
229 if ((uid == uid_all) || (uid == u->getUid())) {
230 sizes += u->sizes(pid);
231 }
232 }
233 return sizes;
234}
235
236size_t LidStatistics::elements(uid_t uid, pid_t pid) {
237 size_t elements = 0;
238 UidStatisticsCollection::iterator it;
239 for (it = begin(); it != end(); ++it) {
240 UidStatistics *u = *it;
241 if ((uid == uid_all) || (uid == u->getUid())) {
242 elements += u->elements(pid);
243 }
244 }
245 return elements;
246}
247
248size_t LidStatistics::sizesTotal(uid_t uid, pid_t pid) {
249 size_t sizes = 0;
250 UidStatisticsCollection::iterator it;
251 for (it = begin(); it != end(); ++it) {
252 UidStatistics *u = *it;
253 if ((uid == uid_all) || (uid == u->getUid())) {
254 sizes += u->sizesTotal(pid);
255 }
256 }
257 return sizes;
258}
259
260size_t LidStatistics::elementsTotal(uid_t uid, pid_t pid) {
261 size_t elements = 0;
262 UidStatisticsCollection::iterator it;
263 for (it = begin(); it != end(); ++it) {
264 UidStatistics *u = *it;
265 if ((uid == uid_all) || (uid == u->getUid())) {
266 elements += u->elementsTotal(pid);
267 }
268 }
269 return elements;
270}
271
272LogStatistics::LogStatistics()
273 : start(CLOCK_MONOTONIC) {
274 log_id_for_each(i) {
275 mSizes[i] = 0;
276 mElements[i] = 0;
277 }
Mark Salyzyne457b742014-02-19 17:18:31 -0800278
279 dgram_qlen_statistics = false;
280 for(unsigned short bucket = 0; dgram_qlen(bucket); ++bucket) {
281 mMinimum[bucket].tv_sec = (uint32_t)-1;
282 mMinimum[bucket].tv_nsec = 999999999UL;
283 }
284}
285
286// Each bucket below represents a dgram_qlen of log messages. By
287// finding the minimum period of time from start to finish
288// of each dgram_qlen, we can get a performance expectation for
289// the user space logger. The net result is that the period
290// of time divided by the dgram_qlen will give us the average time
291// between log messages; at the point where the average time
292// is greater than the throughput capability of the logger
293// we will not longer require the benefits of the FIFO formed
294// by max_dgram_qlen. We will also expect to see a very visible
295// knee in the average time between log messages at this point,
296// so we do not necessarily have to compare the rate against the
297// measured performance (BM_log_maximum_retry) of the logger.
298//
299// for example (reformatted):
300//
301// Minimum time between log events per dgram_qlen:
302// 1 2 3 5 10 20 30 50 100 200 300 400 500 600
303// 5u2 12u 13u 15u 16u 27u 30u 36u 407u 3m1 3m3 3m9 3m9 5m5
304//
305// demonstrates a clear knee rising at 100, so this means that for this
306// case max_dgram_qlen = 100 would be more than sufficient to handle the
307// worst that the system could stuff into the logger. The
308// BM_log_maximum_retry performance (derated by the log collection) on the
309// same system was 33.2us so we would almost be fine with max_dgram_qlen = 50.
310// BM_log_maxumum_retry with statistics off is roughly 20us, so
311// max_dgram_qlen = 20 would work. We will be more than willing to have
312// a large engineering margin so the rule of thumb that lead us to 100 is
313// fine.
314//
315// bucket dgram_qlen are tuned for /proc/sys/net/unix/max_dgram_qlen = 300
316const unsigned short LogStatistics::mBuckets[] = {
317 1, 2, 3, 5, 10, 20, 30, 50, 100, 200, 300, 400, 500, 600
318};
319
320unsigned short LogStatistics::dgram_qlen(unsigned short bucket) {
321 if (bucket >= sizeof(mBuckets) / sizeof(mBuckets[0])) {
322 return 0;
323 }
324 return mBuckets[bucket];
325}
326
327unsigned long long LogStatistics::minimum(unsigned short bucket) {
328 if (mMinimum[bucket].tv_sec == LONG_MAX) {
329 return 0;
330 }
331 return mMinimum[bucket].nsec();
332}
333
334void LogStatistics::recordDiff(log_time diff, unsigned short bucket) {
335 if ((diff.tv_sec || diff.tv_nsec) && (mMinimum[bucket] > diff)) {
336 mMinimum[bucket] = diff;
337 }
Mark Salyzyn34facab2014-02-06 14:48:50 -0800338}
339
340void LogStatistics::add(unsigned short size,
341 log_id_t log_id, uid_t uid, pid_t pid) {
342 mSizes[log_id] += size;
343 ++mElements[log_id];
344 id(log_id).add(size, uid, pid);
345}
346
347void LogStatistics::subtract(unsigned short size,
348 log_id_t log_id, uid_t uid, pid_t pid) {
349 mSizes[log_id] -= size;
350 --mElements[log_id];
351 id(log_id).subtract(size, uid, pid);
352}
353
354size_t LogStatistics::sizes(log_id_t log_id, uid_t uid, pid_t pid) {
355 if (log_id != log_id_all) {
356 return id(log_id).sizes(uid, pid);
357 }
358 size_t sizes = 0;
359 log_id_for_each(i) {
360 sizes += id(i).sizes(uid, pid);
361 }
362 return sizes;
363}
364
365size_t LogStatistics::elements(log_id_t log_id, uid_t uid, pid_t pid) {
366 if (log_id != log_id_all) {
367 return id(log_id).elements(uid, pid);
368 }
369 size_t elements = 0;
370 log_id_for_each(i) {
371 elements += id(i).elements(uid, pid);
372 }
373 return elements;
374}
375
376size_t LogStatistics::sizesTotal(log_id_t log_id, uid_t uid, pid_t pid) {
377 if (log_id != log_id_all) {
378 return id(log_id).sizesTotal(uid, pid);
379 }
380 size_t sizes = 0;
381 log_id_for_each(i) {
382 sizes += id(i).sizesTotal(uid, pid);
383 }
384 return sizes;
385}
386
387size_t LogStatistics::elementsTotal(log_id_t log_id, uid_t uid, pid_t pid) {
388 if (log_id != log_id_all) {
389 return id(log_id).elementsTotal(uid, pid);
390 }
391 size_t elements = 0;
392 log_id_for_each(i) {
393 elements += id(i).elementsTotal(uid, pid);
394 }
395 return elements;
396}
397
Mark Salyzyndfa7a072014-02-11 12:29:31 -0800398void LogStatistics::format(char **buf,
399 uid_t uid, unsigned int logMask, log_time oldest) {
Mark Salyzyn34facab2014-02-06 14:48:50 -0800400 const unsigned short spaces_current = 13;
401 const unsigned short spaces_total = 19;
402
403 if (*buf) {
404 free(buf);
405 *buf = NULL;
406 }
407
408 android::String8 string(" span -> size/num");
409 size_t oldLength;
410 short spaces = 2;
411
412 log_id_for_each(i) {
413 if (logMask & (1 << i)) {
414 oldLength = string.length();
Mark Salyzyne457b742014-02-19 17:18:31 -0800415 if (spaces < 0) {
416 spaces = 0;
417 }
Mark Salyzyn34facab2014-02-06 14:48:50 -0800418 string.appendFormat("%*s%s", spaces, "", android_log_id_to_name(i));
419 spaces += spaces_total + oldLength - string.length();
420 }
421 }
422
423 spaces = 1;
424 log_time t(CLOCK_MONOTONIC);
425 unsigned long long d = t.nsec() - start.nsec();
426 string.appendFormat("\nTotal%4llu:%02llu:%02llu.%09llu",
427 d / NS_PER_SEC / 60 / 60, (d / NS_PER_SEC / 60) % 60,
428 (d / NS_PER_SEC) % 60, d % NS_PER_SEC);
429
430 log_id_for_each(i) {
431 if (!(logMask & (1 << i))) {
432 continue;
433 }
434 oldLength = string.length();
Mark Salyzyne457b742014-02-19 17:18:31 -0800435 if (spaces < 0) {
436 spaces = 0;
437 }
Mark Salyzyn34facab2014-02-06 14:48:50 -0800438 string.appendFormat("%*s%zu/%zu", spaces, "",
439 sizesTotal(i), elementsTotal(i));
440 spaces += spaces_total + oldLength - string.length();
441 }
442
443 spaces = 1;
444 d = t.nsec() - oldest.nsec();
445 string.appendFormat("\nNow%6llu:%02llu:%02llu.%09llu",
446 d / NS_PER_SEC / 60 / 60, (d / NS_PER_SEC / 60) % 60,
447 (d / NS_PER_SEC) % 60, d % NS_PER_SEC);
448
449 log_id_for_each(i) {
450 if (!(logMask & (1 << i))) {
451 continue;
452 }
453
454 size_t els = elements(i);
455 if (els) {
456 oldLength = string.length();
Mark Salyzyne457b742014-02-19 17:18:31 -0800457 if (spaces < 0) {
458 spaces = 0;
459 }
Mark Salyzyn34facab2014-02-06 14:48:50 -0800460 string.appendFormat("%*s%zu/%zu", spaces, "", sizes(i), els);
461 spaces -= string.length() - oldLength;
462 }
463 spaces += spaces_total;
464 }
465
Mark Salyzyne457b742014-02-19 17:18:31 -0800466 if (dgram_qlen_statistics) {
467 const unsigned short spaces_time = 6;
468 const unsigned long long max_seconds = 100000;
469 spaces = 0;
470 string.append("\n\nMinimum time between log events per dgram_qlen:\n");
471 for(unsigned short i = 0; dgram_qlen(i); ++i) {
472 oldLength = string.length();
473 if (spaces < 0) {
474 spaces = 0;
475 }
476 string.appendFormat("%*s%u", spaces, "", dgram_qlen(i));
477 spaces += spaces_time + oldLength - string.length();
478 }
479 string.append("\n");
480 spaces = 0;
481 unsigned short n;
482 for(unsigned short i = 0; (n = dgram_qlen(i)); ++i) {
483 unsigned long long duration = minimum(i);
484 if (duration) {
485 duration /= n;
486 if (duration >= (NS_PER_SEC * max_seconds)) {
487 duration = NS_PER_SEC * (max_seconds - 1);
488 }
489 oldLength = string.length();
490 if (spaces < 0) {
491 spaces = 0;
492 }
493 string.appendFormat("%*s", spaces, "");
494 if (duration >= (NS_PER_SEC * 10)) {
495 string.appendFormat("%llu",
496 (duration + (NS_PER_SEC / 2))
497 / NS_PER_SEC);
498 } else if (duration >= (NS_PER_SEC / (1000 / 10))) {
499 string.appendFormat("%llum",
500 (duration + (NS_PER_SEC / 2 / 1000))
501 / (NS_PER_SEC / 1000));
502 } else if (duration >= (NS_PER_SEC / (1000000 / 10))) {
503 string.appendFormat("%lluu",
504 (duration + (NS_PER_SEC / 2 / 1000000))
505 / (NS_PER_SEC / 1000000));
506 } else {
507 string.appendFormat("%llun", duration);
508 }
509 spaces -= string.length() - oldLength;
510 }
511 spaces += spaces_time;
512 }
513 }
514
Mark Salyzyn34facab2014-02-06 14:48:50 -0800515 log_id_for_each(i) {
516 if (!(logMask & (1 << i))) {
517 continue;
518 }
519
520 bool header = false;
521 bool first = true;
522
523 UidStatisticsCollection::iterator ut;
524 for(ut = id(i).begin(); ut != id(i).end(); ++ut) {
525 UidStatistics *up = *ut;
526 if ((uid != AID_ROOT) && (uid != up->getUid())) {
527 continue;
528 }
529
530 PidStatisticsCollection::iterator pt = up->begin();
531 if (pt == up->end()) {
532 continue;
533 }
534
535 android::String8 intermediate;
536
537 if (!header) {
538 // header below tuned to match spaces_total and spaces_current
539 spaces = 0;
540 intermediate = string.format("%s: UID/PID Total size/num",
541 android_log_id_to_name(i));
542 string.appendFormat("\n\n%-31sNow "
543 "UID/PID[?] Total Now",
544 intermediate.string());
545 intermediate.clear();
546 header = true;
547 }
548
549 bool oneline = ++pt == up->end();
550 --pt;
551
552 if (!oneline) {
553 first = true;
Mark Salyzyne457b742014-02-19 17:18:31 -0800554 } else if (!first && (spaces > 0)) {
Mark Salyzyn34facab2014-02-06 14:48:50 -0800555 string.appendFormat("%*s", spaces, "");
556 }
557 spaces = 0;
558
559 uid_t u = up->getUid();
560 pid_t p = (*pt)->getPid();
561
562 intermediate = string.format(oneline
563 ? ((p == PidStatistics::gone)
564 ? "%d/?"
565 : "%d/%d")
566 : "%d",
567 u, p);
568 string.appendFormat((first) ? "\n%-12s" : "%-12s",
569 intermediate.string());
570 intermediate.clear();
571
572 size_t elsTotal = up->elementsTotal();
573 oldLength = string.length();
574 string.appendFormat("%zu/%zu", up->sizesTotal(), elsTotal);
575 spaces += spaces_total + oldLength - string.length();
576
577 size_t els = up->elements();
578 if (els == elsTotal) {
Mark Salyzyne457b742014-02-19 17:18:31 -0800579 if (spaces < 0) {
580 spaces = 0;
581 }
Mark Salyzyn34facab2014-02-06 14:48:50 -0800582 string.appendFormat("%*s=", spaces, "");
583 spaces = -1;
584 } else if (els) {
585 oldLength = string.length();
Mark Salyzyne457b742014-02-19 17:18:31 -0800586 if (spaces < 0) {
587 spaces = 0;
588 }
Mark Salyzyn34facab2014-02-06 14:48:50 -0800589 string.appendFormat("%*s%zu/%zu", spaces, "", up->sizes(), els);
590 spaces -= string.length() - oldLength;
591 }
592 spaces += spaces_current;
593
594 first = !first;
595
596 if (oneline) {
597 continue;
598 }
599
600 size_t gone_szs = 0;
601 size_t gone_els = 0;
602
603 for(; pt != up->end(); ++pt) {
604 PidStatistics *pp = *pt;
605 pid_t p = pp->getPid();
606
607 // If a PID no longer has any current logs, and is not
608 // active anymore, skip & report totals for gone.
609 elsTotal = pp->elementsTotal();
610 size_t szsTotal = pp->sizesTotal();
611 if (p == pp->gone) {
612 gone_szs += szsTotal;
613 gone_els += elsTotal;
614 continue;
615 }
616 els = pp->elements();
617 bool gone = kill(p, 0);
618 if (gone && (els == 0)) {
619 // ToDo: garbage collection: move this statistical bucket
620 // from its current UID/PID to UID/? (races and
621 // wrap around are our achilles heel). Below is
622 // merely lipservice to catch PIDs that were still
623 // around when the stats were pruned to zero.
624 gone_szs += szsTotal;
625 gone_els += elsTotal;
626 continue;
627 }
628
Mark Salyzyne457b742014-02-19 17:18:31 -0800629 if (!first && (spaces > 0)) {
Mark Salyzyn34facab2014-02-06 14:48:50 -0800630 string.appendFormat("%*s", spaces, "");
631 }
632 spaces = 0;
633
634 intermediate = string.format((gone) ? "%d/%d?" : "%d/%d", u, p);
635 string.appendFormat((first) ? "\n%-12s" : "%-12s",
636 intermediate.string());
637 intermediate.clear();
638
639 oldLength = string.length();
640 string.appendFormat("%zu/%zu", szsTotal, elsTotal);
641 spaces += spaces_total + oldLength - string.length();
642
643 if (els == elsTotal) {
Mark Salyzyne457b742014-02-19 17:18:31 -0800644 if (spaces < 0) {
645 spaces = 0;
646 }
Mark Salyzyn34facab2014-02-06 14:48:50 -0800647 string.appendFormat("%*s=", spaces, "");
648 spaces = -1;
649 } else if (els) {
650 oldLength = string.length();
Mark Salyzyne457b742014-02-19 17:18:31 -0800651 if (spaces < 0) {
652 spaces = 0;
653 }
Mark Salyzyn34facab2014-02-06 14:48:50 -0800654 string.appendFormat("%*s%zu/%zu", spaces, "",
655 pp->sizes(), els);
656 spaces -= string.length() - oldLength;
657 }
658 spaces += spaces_current;
659
660 first = !first;
661 }
662
663 if (gone_els) {
Mark Salyzyne457b742014-02-19 17:18:31 -0800664 if (!first && (spaces > 0)) {
Mark Salyzyn34facab2014-02-06 14:48:50 -0800665 string.appendFormat("%*s", spaces, "");
666 }
667
668 intermediate = string.format("%d/?", u);
669 string.appendFormat((first) ? "\n%-12s" : "%-12s",
670 intermediate.string());
671 intermediate.clear();
672
673 spaces = spaces_total + spaces_current;
674
675 oldLength = string.length();
676 string.appendFormat("%zu/%zu", gone_szs, gone_els);
677 spaces -= string.length() - oldLength;
678
679 first = !first;
680 }
681 }
682 }
683
Mark Salyzyndfa7a072014-02-11 12:29:31 -0800684 *buf = strdup(string.string());
Mark Salyzyn34facab2014-02-06 14:48:50 -0800685}