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 27 PidStatistics::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 38 PidStatistics::PidStatistics(const PidStatistics ©) 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 49 PidStatistics::~PidStatistics() { 50 free(name); 51 } 52 53 bool 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 67 void PidStatistics::setName(char *new_name) { 68 free(name); 69 name = new_name; 70 } 71 72 void PidStatistics::add(unsigned short size) { 73 mSizesTotal += size; 74 ++mElementsTotal; 75 mSizes += size; 76 ++mElements; 77 } 78 79 bool PidStatistics::subtract(unsigned short size) { 80 mSizes -= size; 81 --mElements; 82 return (mElements == 0) && pidGone(); 83 } 84 85 void 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 char *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 114 UidStatistics::UidStatistics(uid_t uid) 115 : uid(uid) 116 , mSizes(0) 117 , mElements(0) { 118 Pids.clear(); 119 } 120 121 UidStatistics::~UidStatistics() { 122 PidStatisticsCollection::iterator it; 123 for (it = begin(); it != end();) { 124 delete (*it); 125 it = Pids.erase(it); 126 } 127 } 128 129 void 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 154 void 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 186 void 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 209 size_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 224 size_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 239 size_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 251 size_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 263 LidStatistics::LidStatistics() { 264 Uids.clear(); 265 } 266 267 LidStatistics::~LidStatistics() { 268 UidStatisticsCollection::iterator it; 269 for (it = begin(); it != end();) { 270 delete (*it); 271 it = Uids.erase(it); 272 } 273 } 274 275 void 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 304 void 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 315 void 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 338 size_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 350 size_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 362 size_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 374 size_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 386 LogStatistics::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 = mMinimum[bucket].tv_sec_max; 396 mMinimum[bucket].tv_nsec = mMinimum[bucket].tv_nsec_max; 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 430 const unsigned short LogStatistics::mBuckets[] = { 431 1, 2, 3, 5, 10, 20, 30, 50, 100, 200, 300, 400, 500, 600 432 }; 433 434 unsigned 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 441 unsigned 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 448 void 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 454 void 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 461 void 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 468 size_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 479 size_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 490 size_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 501 size_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 512 void 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 911 uid_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