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