LogStatistics.cpp revision 1fa079b2555a12a53a6d25956d4f371ace60edce
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 <pwd.h> 19#include <stdio.h> 20#include <string.h> 21#include <sys/types.h> 22#include <unistd.h> 23 24#include <list> 25 26#include <android/log.h> 27 28#include "LogStatistics.h" 29 30size_t LogStatistics::SizesTotal; 31 32LogStatistics::LogStatistics() : enable(false) { 33 log_id_for_each(id) { 34 mSizes[id] = 0; 35 mElements[id] = 0; 36 mDroppedElements[id] = 0; 37 mSizesTotal[id] = 0; 38 mElementsTotal[id] = 0; 39 } 40} 41 42namespace android { 43 44size_t sizesTotal() { return LogStatistics::sizesTotal(); } 45 46// caller must own and free character string 47char *pidToName(pid_t pid) { 48 char *retval = NULL; 49 if (pid == 0) { // special case from auditd/klogd for kernel 50 retval = strdup("logd"); 51 } else { 52 char buffer[512]; 53 snprintf(buffer, sizeof(buffer), "/proc/%u/cmdline", pid); 54 int fd = open(buffer, O_RDONLY); 55 if (fd >= 0) { 56 ssize_t ret = read(fd, buffer, sizeof(buffer)); 57 if (ret > 0) { 58 buffer[sizeof(buffer)-1] = '\0'; 59 // frameworks intermediate state 60 if (fastcmp<strcmp>(buffer, "<pre-initialized>")) { 61 retval = strdup(buffer); 62 } 63 } 64 close(fd); 65 } 66 } 67 return retval; 68} 69 70} 71 72void LogStatistics::add(LogBufferElement *element) { 73 log_id_t log_id = element->getLogId(); 74 unsigned short size = element->getMsgLen(); 75 mSizes[log_id] += size; 76 ++mElements[log_id]; 77 78 if (element->getDropped()) { 79 ++mDroppedElements[log_id]; 80 } else { 81 // When caller adding a chatty entry, they will have already 82 // called add() and subtract() for each entry as they are 83 // evaluated and trimmed, thus recording size and number of 84 // elements, but we must recognize the manufactured dropped 85 // entry as not contributing to the lifetime totals. 86 mSizesTotal[log_id] += size; 87 SizesTotal += size; 88 ++mElementsTotal[log_id]; 89 } 90 91 if (log_id == LOG_ID_KERNEL) { 92 return; 93 } 94 95 uidTable[log_id].add(element->getUid(), element); 96 if (element->getUid() == AID_SYSTEM) { 97 pidSystemTable[log_id].add(element->getPid(), element); 98 } 99 100 if (!enable) { 101 return; 102 } 103 104 pidTable.add(element->getPid(), element); 105 tidTable.add(element->getTid(), element); 106 107 uint32_t tag = element->getTag(); 108 if (tag) { 109 if (log_id == LOG_ID_SECURITY) { 110 securityTagTable.add(tag, element); 111 } else { 112 tagTable.add(tag, element); 113 } 114 } 115} 116 117void LogStatistics::subtract(LogBufferElement *element) { 118 log_id_t log_id = element->getLogId(); 119 unsigned short size = element->getMsgLen(); 120 mSizes[log_id] -= size; 121 --mElements[log_id]; 122 if (element->getDropped()) { 123 --mDroppedElements[log_id]; 124 } 125 126 if (log_id == LOG_ID_KERNEL) { 127 return; 128 } 129 130 uidTable[log_id].subtract(element->getUid(), element); 131 if (element->getUid() == AID_SYSTEM) { 132 pidSystemTable[log_id].subtract(element->getPid(), element); 133 } 134 135 if (!enable) { 136 return; 137 } 138 139 pidTable.subtract(element->getPid(), element); 140 tidTable.subtract(element->getTid(), element); 141 142 uint32_t tag = element->getTag(); 143 if (tag) { 144 if (log_id == LOG_ID_SECURITY) { 145 securityTagTable.subtract(tag, element); 146 } else { 147 tagTable.subtract(tag, element); 148 } 149 } 150} 151 152// Atomically set an entry to drop 153// entry->setDropped(1) must follow this call, caller should do this explicitly. 154void LogStatistics::drop(LogBufferElement *element) { 155 log_id_t log_id = element->getLogId(); 156 unsigned short size = element->getMsgLen(); 157 mSizes[log_id] -= size; 158 ++mDroppedElements[log_id]; 159 160 uidTable[log_id].drop(element->getUid(), element); 161 if (element->getUid() == AID_SYSTEM) { 162 pidSystemTable[log_id].drop(element->getPid(), element); 163 } 164 165 if (!enable) { 166 return; 167 } 168 169 pidTable.drop(element->getPid(), element); 170 tidTable.drop(element->getTid(), element); 171 172 uint32_t tag = element->getTag(); 173 if (tag) { 174 if (log_id == LOG_ID_SECURITY) { 175 securityTagTable.drop(tag, element); 176 } else { 177 tagTable.drop(tag, element); 178 } 179 } 180} 181 182// caller must own and free character string 183const char *LogStatistics::uidToName(uid_t uid) const { 184 // Local hard coded favourites 185 if (uid == AID_LOGD) { 186 return strdup("auditd"); 187 } 188 189 // Android system 190 if (uid < AID_APP) { 191 // in bionic, thread safe as long as we copy the results 192 struct passwd *pwd = getpwuid(uid); 193 if (pwd) { 194 return strdup(pwd->pw_name); 195 } 196 } 197 198 // Parse /data/system/packages.list 199 uid_t userId = uid % AID_USER_OFFSET; 200 const char *name = android::uidToName(userId); 201 if (!name && (userId > (AID_SHARED_GID_START - AID_APP))) { 202 name = android::uidToName(userId - (AID_SHARED_GID_START - AID_APP)); 203 } 204 if (name) { 205 return name; 206 } 207 208 // Android application 209 if (uid >= AID_APP) { 210 struct passwd *pwd = getpwuid(uid); 211 if (pwd) { 212 return strdup(pwd->pw_name); 213 } 214 } 215 216 // report uid -> pid(s) -> pidToName if unique 217 for(pidTable_t::const_iterator it = pidTable.begin(); it != pidTable.end(); ++it) { 218 const PidEntry &entry = it->second; 219 220 if (entry.getUid() == uid) { 221 const char *nameTmp = entry.getName(); 222 223 if (nameTmp) { 224 if (!name) { 225 name = strdup(nameTmp); 226 } else if (fastcmp<strcmp>(name, nameTmp)) { 227 free(const_cast<char *>(name)); 228 name = NULL; 229 break; 230 } 231 } 232 } 233 } 234 235 // No one 236 return name; 237} 238 239std::string UidEntry::formatHeader(const std::string &name, log_id_t id) const { 240 bool isprune = worstUidEnabledForLogid(id); 241 return formatLine(android::base::StringPrintf( 242 name.c_str(), android_log_id_to_name(id)), 243 std::string("Size"), 244 std::string(isprune ? "+/- Pruned" : "")) 245 + formatLine(std::string("UID PACKAGE"), 246 std::string("BYTES"), 247 std::string(isprune ? "NUM" : "")); 248} 249 250std::string UidEntry::format(const LogStatistics &stat, log_id_t id) const { 251 uid_t uid = getUid(); 252 std::string name = android::base::StringPrintf("%u", uid); 253 const char *nameTmp = stat.uidToName(uid); 254 if (nameTmp) { 255 name += android::base::StringPrintf( 256 "%*s%s", (int)std::max(6 - name.length(), (size_t)1), 257 "", nameTmp); 258 free(const_cast<char *>(nameTmp)); 259 } 260 261 std::string size = android::base::StringPrintf("%zu", getSizes()); 262 263 std::string pruned = ""; 264 if (worstUidEnabledForLogid(id)) { 265 size_t totalDropped = 0; 266 for (LogStatistics::uidTable_t::const_iterator it = stat.uidTable[id].begin(); 267 it != stat.uidTable[id].end(); ++it) { 268 totalDropped += it->second.getDropped(); 269 } 270 size_t sizes = stat.sizes(id); 271 size_t totalSize = stat.sizesTotal(id); 272 size_t totalElements = stat.elementsTotal(id); 273 float totalVirtualSize = (float)sizes + (float)totalDropped * totalSize 274 / totalElements; 275 size_t entrySize = getSizes(); 276 float virtualEntrySize = entrySize; 277 int realPermille = virtualEntrySize * 1000.0 / sizes; 278 size_t dropped = getDropped(); 279 if (dropped) { 280 pruned = android::base::StringPrintf("%zu", dropped); 281 virtualEntrySize += (float)dropped * totalSize / totalElements; 282 } 283 int virtualPermille = virtualEntrySize * 1000.0 / totalVirtualSize; 284 int permille = (realPermille - virtualPermille) * 1000L 285 / (virtualPermille ?: 1); 286 if ((permille < -1) || (1 < permille)) { 287 std::string change; 288 const char *units = "%"; 289 const char *prefix = (permille > 0) ? "+" : ""; 290 291 if (permille > 999) { 292 permille = (permille + 1000) / 100; // Now tenths fold 293 units = "X"; 294 prefix = ""; 295 } 296 if ((-99 < permille) && (permille < 99)) { 297 change = android::base::StringPrintf("%s%d.%u%s", 298 prefix, 299 permille / 10, 300 ((permille < 0) ? (-permille % 10) : (permille % 10)), 301 units); 302 } else { 303 change = android::base::StringPrintf("%s%d%s", 304 prefix, 305 (permille + 5) / 10, units); 306 } 307 ssize_t spaces = EntryBaseConstants::pruned_len 308 - 2 - pruned.length() - change.length(); 309 if ((spaces <= 0) && pruned.length()) { 310 spaces = 1; 311 } 312 if (spaces > 0) { 313 change += android::base::StringPrintf("%*s", (int)spaces, ""); 314 } 315 pruned = change + pruned; 316 } 317 } 318 319 std::string output = formatLine(name, size, pruned); 320 321 if (uid != AID_SYSTEM) { 322 return output; 323 } 324 325 static const size_t maximum_sorted_entries = 32; 326 std::unique_ptr<const PidEntry *[]> sorted 327 = stat.pidSystemTable[id].sort(uid, (pid_t)0, maximum_sorted_entries); 328 329 if (!sorted.get()) { 330 return output; 331 } 332 std::string byPid; 333 size_t index; 334 bool hasDropped = false; 335 for (index = 0; index < maximum_sorted_entries; ++index) { 336 const PidEntry *entry = sorted[index]; 337 if (!entry) { 338 break; 339 } 340 if (entry->getSizes() <= (getSizes() / 100)) { 341 break; 342 } 343 if (entry->getDropped()) { 344 hasDropped = true; 345 } 346 byPid += entry->format(stat, id); 347 } 348 if (index > 1) { // print this only if interesting 349 std::string ditto("\" "); 350 output += formatLine(std::string(" PID/UID COMMAND LINE"), 351 ditto, hasDropped ? ditto : std::string("")); 352 output += byPid; 353 } 354 355 return output; 356} 357 358std::string PidEntry::formatHeader(const std::string &name, log_id_t /* id */) const { 359 return formatLine(name, 360 std::string("Size"), 361 std::string("Pruned")) 362 + formatLine(std::string(" PID/UID COMMAND LINE"), 363 std::string("BYTES"), 364 std::string("NUM")); 365} 366 367std::string PidEntry::format(const LogStatistics &stat, log_id_t /* id */) const { 368 uid_t uid = getUid(); 369 pid_t pid = getPid(); 370 std::string name = android::base::StringPrintf("%5u/%u", pid, uid); 371 const char *nameTmp = getName(); 372 if (nameTmp) { 373 name += android::base::StringPrintf( 374 "%*s%s", (int)std::max(12 - name.length(), (size_t)1), 375 "", nameTmp); 376 } else if ((nameTmp = stat.uidToName(uid))) { 377 name += android::base::StringPrintf( 378 "%*s%s", (int)std::max(12 - name.length(), (size_t)1), 379 "", nameTmp); 380 free(const_cast<char *>(nameTmp)); 381 } 382 383 std::string size = android::base::StringPrintf("%zu", 384 getSizes()); 385 386 std::string pruned = ""; 387 size_t dropped = getDropped(); 388 if (dropped) { 389 pruned = android::base::StringPrintf("%zu", dropped); 390 } 391 392 return formatLine(name, size, pruned); 393} 394 395std::string TidEntry::formatHeader(const std::string &name, log_id_t /* id */) const { 396 return formatLine(name, 397 std::string("Size"), 398 std::string("Pruned")) 399 + formatLine(std::string(" TID/UID COMM"), 400 std::string("BYTES"), 401 std::string("NUM")); 402} 403 404std::string TidEntry::format(const LogStatistics &stat, log_id_t /* id */) const { 405 uid_t uid = getUid(); 406 std::string name = android::base::StringPrintf("%5u/%u", 407 getTid(), uid); 408 const char *nameTmp = getName(); 409 if (nameTmp) { 410 name += android::base::StringPrintf( 411 "%*s%s", (int)std::max(12 - name.length(), (size_t)1), 412 "", nameTmp); 413 } else if ((nameTmp = stat.uidToName(uid))) { 414 // if we do not have a PID name, lets punt to try UID name? 415 name += android::base::StringPrintf( 416 "%*s%s", (int)std::max(12 - name.length(), (size_t)1), 417 "", nameTmp); 418 free(const_cast<char *>(nameTmp)); 419 // We tried, better to not have a name at all, we still 420 // have TID/UID by number to report in any case. 421 } 422 423 std::string size = android::base::StringPrintf("%zu", 424 getSizes()); 425 426 std::string pruned = ""; 427 size_t dropped = getDropped(); 428 if (dropped) { 429 pruned = android::base::StringPrintf("%zu", dropped); 430 } 431 432 return formatLine(name, size, pruned); 433} 434 435std::string TagEntry::formatHeader(const std::string &name, log_id_t id) const { 436 bool isprune = worstUidEnabledForLogid(id); 437 return formatLine(name, 438 std::string("Size"), 439 std::string(isprune ? "Prune" : "")) 440 + formatLine(std::string(" TAG/UID TAGNAME"), 441 std::string("BYTES"), 442 std::string(isprune ? "NUM" : "")); 443} 444 445std::string TagEntry::format(const LogStatistics & /* stat */, log_id_t /* id */) const { 446 std::string name; 447 uid_t uid = getUid(); 448 if (uid == (uid_t)-1) { 449 name = android::base::StringPrintf("%7u", 450 getKey()); 451 } else { 452 name = android::base::StringPrintf("%7u/%u", 453 getKey(), uid); 454 } 455 size_t len = 0; 456 const char *nameTmp = getName(len); 457 if (nameTmp) { 458 name += android::base::StringPrintf( 459 "%*s%.*s", (int)std::max(14 - name.length(), (size_t)1), 460 "", (int)len, nameTmp); 461 } 462 463 std::string size = android::base::StringPrintf("%zu", 464 getSizes()); 465 466 std::string pruned = ""; 467 size_t dropped = getDropped(); 468 if (dropped) { 469 pruned = android::base::StringPrintf("%zu", dropped); 470 } 471 472 return formatLine(name, size, pruned); 473} 474 475std::string LogStatistics::format(uid_t uid, pid_t pid, 476 unsigned int logMask) const { 477 static const unsigned short spaces_total = 19; 478 479 // Report on total logging, current and for all time 480 481 std::string output = "size/num"; 482 size_t oldLength; 483 short spaces = 1; 484 485 log_id_for_each(id) { 486 if (!(logMask & (1 << id))) continue; 487 oldLength = output.length(); 488 if (spaces < 0) spaces = 0; 489 output += android::base::StringPrintf("%*s%s", spaces, "", 490 android_log_id_to_name(id)); 491 spaces += spaces_total + oldLength - output.length(); 492 } 493 if (spaces < 0) spaces = 0; 494 output += android::base::StringPrintf("%*sTotal", spaces, ""); 495 496 static const char TotalStr[] = "\nTotal"; 497 spaces = 10 - strlen(TotalStr); 498 output += TotalStr; 499 500 size_t totalSize = 0; 501 size_t totalEls = 0; 502 log_id_for_each(id) { 503 if (!(logMask & (1 << id))) continue; 504 oldLength = output.length(); 505 if (spaces < 0) spaces = 0; 506 size_t szs = sizesTotal(id); 507 totalSize += szs; 508 size_t els = elementsTotal(id); 509 totalEls += els; 510 output += android::base::StringPrintf("%*s%zu/%zu", spaces, "", szs, els); 511 spaces += spaces_total + oldLength - output.length(); 512 } 513 if (spaces < 0) spaces = 0; 514 output += android::base::StringPrintf("%*s%zu/%zu", spaces, "", totalSize, totalEls); 515 516 static const char NowStr[] = "\nNow"; 517 spaces = 10 - strlen(NowStr); 518 output += NowStr; 519 520 totalSize = 0; 521 totalEls = 0; 522 log_id_for_each(id) { 523 if (!(logMask & (1 << id))) continue; 524 525 size_t els = elements(id); 526 if (els) { 527 oldLength = output.length(); 528 if (spaces < 0) spaces = 0; 529 size_t szs = sizes(id); 530 totalSize += szs; 531 totalEls += els; 532 output += android::base::StringPrintf("%*s%zu/%zu", spaces, "", szs, els); 533 spaces -= output.length() - oldLength; 534 } 535 spaces += spaces_total; 536 } 537 if (spaces < 0) spaces = 0; 538 output += android::base::StringPrintf("%*s%zu/%zu", spaces, "", totalSize, totalEls); 539 540 static const char OverheadStr[] = "\nOverhead"; 541 spaces = 10 - strlen(OverheadStr); 542 output += OverheadStr; 543 544 totalSize = 0; 545 log_id_for_each(id) { 546 if (!(logMask & (1 << id))) continue; 547 548 size_t els = elements(id); 549 if (els) { 550 oldLength = output.length(); 551 if (spaces < 0) spaces = 0; 552 // estimate the std::list overhead. 553 static const size_t overhead = 554 ((sizeof(LogBufferElement) + sizeof(uint64_t) - 1) & 555 -sizeof(uint64_t)) + 556 sizeof(std::list<LogBufferElement*>); 557 size_t szs = sizes(id) + els * overhead; 558 totalSize += szs; 559 output += android::base::StringPrintf("%*s%zu", spaces, "", szs); 560 spaces -= output.length() - oldLength; 561 } 562 spaces += spaces_total; 563 } 564 totalSize += sizeOf(); 565 if (spaces < 0) spaces = 0; 566 output += android::base::StringPrintf("%*s%zu", spaces, "", totalSize); 567 568 // Report on Chattiest 569 570 std::string name; 571 572 // Chattiest by application (UID) 573 log_id_for_each(id) { 574 if (!(logMask & (1 << id))) continue; 575 576 name = (uid == AID_ROOT) 577 ? "Chattiest UIDs in %s log buffer:" 578 : "Logging for your UID in %s log buffer:"; 579 output += uidTable[id].format(*this, uid, pid, name, id); 580 } 581 582 if (enable) { 583 name = ((uid == AID_ROOT) && !pid) 584 ? "Chattiest PIDs:" 585 : "Logging for this PID:"; 586 output += pidTable.format(*this, uid, pid, name); 587 name = "Chattiest TIDs"; 588 if (pid) name += android::base::StringPrintf(" for PID %d", pid); 589 name += ":"; 590 output += tidTable.format(*this, uid, pid, name); 591 } 592 593 if (enable && (logMask & (1 << LOG_ID_EVENTS))) { 594 name = "Chattiest events log buffer TAGs"; 595 if (pid) name += android::base::StringPrintf(" for PID %d", pid); 596 name += ":"; 597 output += tagTable.format(*this, uid, pid, name, LOG_ID_EVENTS); 598 } 599 600 if (enable && (logMask & (1 << LOG_ID_SECURITY))) { 601 name = "Chattiest security log buffer TAGs"; 602 if (pid) name += android::base::StringPrintf(" for PID %d", pid); 603 name += ":"; 604 output += securityTagTable.format(*this, uid, pid, name, LOG_ID_SECURITY); 605 } 606 607 return output; 608} 609 610namespace android { 611 612uid_t pidToUid(pid_t pid) { 613 char buffer[512]; 614 snprintf(buffer, sizeof(buffer), "/proc/%u/status", pid); 615 FILE *fp = fopen(buffer, "r"); 616 if (fp) { 617 while (fgets(buffer, sizeof(buffer), fp)) { 618 int uid; 619 if (sscanf(buffer, "Uid: %d", &uid) == 1) { 620 fclose(fp); 621 return uid; 622 } 623 } 624 fclose(fp); 625 } 626 return AID_LOGD; // associate this with the logger 627} 628 629} 630 631uid_t LogStatistics::pidToUid(pid_t pid) { 632 return pidTable.add(pid)->second.getUid(); 633} 634 635// caller must free character string 636const char *LogStatistics::pidToName(pid_t pid) const { 637 // An inconvenient truth ... getName() can alter the object 638 pidTable_t &writablePidTable = const_cast<pidTable_t &>(pidTable); 639 const char *name = writablePidTable.add(pid)->second.getName(); 640 if (!name) { 641 return NULL; 642 } 643 return strdup(name); 644} 645