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