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