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 251std::string UidEntry::format(const LogStatistics& stat, log_id_t id) const { 252 uid_t uid = getUid(); 253 std::string name = android::base::StringPrintf("%u", uid); 254 const char* nameTmp = stat.uidToName(uid); 255 if (nameTmp) { 256 name += android::base::StringPrintf( 257 "%*s%s", (int)std::max(6 - name.length(), (size_t)1), "", 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 = 267 stat.uidTable[id].begin(); 268 it != stat.uidTable[id].end(); ++it) { 269 totalDropped += it->second.getDropped(); 270 } 271 size_t sizes = stat.sizes(id); 272 size_t totalSize = stat.sizesTotal(id); 273 size_t totalElements = stat.elementsTotal(id); 274 float totalVirtualSize = 275 (float)sizes + (float)totalDropped * totalSize / totalElements; 276 size_t entrySize = getSizes(); 277 float virtualEntrySize = entrySize; 278 int realPermille = virtualEntrySize * 1000.0 / sizes; 279 size_t dropped = getDropped(); 280 if (dropped) { 281 pruned = android::base::StringPrintf("%zu", dropped); 282 virtualEntrySize += (float)dropped * totalSize / totalElements; 283 } 284 int virtualPermille = virtualEntrySize * 1000.0 / totalVirtualSize; 285 int permille = 286 (realPermille - virtualPermille) * 1000L / (virtualPermille ?: 1); 287 if ((permille < -1) || (1 < permille)) { 288 std::string change; 289 const char* units = "%"; 290 const char* prefix = (permille > 0) ? "+" : ""; 291 292 if (permille > 999) { 293 permille = (permille + 1000) / 100; // Now tenths fold 294 units = "X"; 295 prefix = ""; 296 } 297 if ((-99 < permille) && (permille < 99)) { 298 change = android::base::StringPrintf( 299 "%s%d.%u%s", prefix, permille / 10, 300 ((permille < 0) ? (-permille % 10) : (permille % 10)), 301 units); 302 } else { 303 change = android::base::StringPrintf( 304 "%s%d%s", prefix, (permille + 5) / 10, units); 305 } 306 ssize_t spaces = EntryBaseConstants::pruned_len - 2 - 307 pruned.length() - change.length(); 308 if ((spaces <= 0) && pruned.length()) { 309 spaces = 1; 310 } 311 if (spaces > 0) { 312 change += android::base::StringPrintf("%*s", (int)spaces, ""); 313 } 314 pruned = change + pruned; 315 } 316 } 317 318 std::string output = formatLine(name, size, pruned); 319 320 if (uid != AID_SYSTEM) { 321 return output; 322 } 323 324 static const size_t maximum_sorted_entries = 32; 325 std::unique_ptr<const PidEntry* []> sorted = 326 stat.pidSystemTable[id].sort(uid, (pid_t)0, maximum_sorted_entries); 327 328 if (!sorted.get()) { 329 return output; 330 } 331 std::string byPid; 332 size_t index; 333 bool hasDropped = false; 334 for (index = 0; index < maximum_sorted_entries; ++index) { 335 const PidEntry* entry = sorted[index]; 336 if (!entry) { 337 break; 338 } 339 if (entry->getSizes() <= (getSizes() / 100)) { 340 break; 341 } 342 if (entry->getDropped()) { 343 hasDropped = true; 344 } 345 byPid += entry->format(stat, id); 346 } 347 if (index > 1) { // print this only if interesting 348 std::string ditto("\" "); 349 output += formatLine(std::string(" PID/UID COMMAND LINE"), ditto, 350 hasDropped ? ditto : std::string("")); 351 output += byPid; 352 } 353 354 return output; 355} 356 357std::string PidEntry::formatHeader(const std::string& name, 358 log_id_t /* id */) const { 359 return formatLine(name, std::string("Size"), std::string("Pruned")) + 360 formatLine(std::string(" PID/UID COMMAND LINE"), 361 std::string("BYTES"), std::string("NUM")); 362} 363 364std::string PidEntry::format(const LogStatistics& stat, 365 log_id_t /* id */) const { 366 uid_t uid = getUid(); 367 pid_t pid = getPid(); 368 std::string name = android::base::StringPrintf("%5u/%u", pid, uid); 369 const char* nameTmp = getName(); 370 if (nameTmp) { 371 name += android::base::StringPrintf( 372 "%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", nameTmp); 373 } else if ((nameTmp = stat.uidToName(uid))) { 374 name += android::base::StringPrintf( 375 "%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", nameTmp); 376 free(const_cast<char*>(nameTmp)); 377 } 378 379 std::string size = android::base::StringPrintf("%zu", 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, 391 log_id_t /* id */) const { 392 return formatLine(name, std::string("Size"), std::string("Pruned")) + 393 formatLine(std::string(" TID/UID COMM"), std::string("BYTES"), 394 std::string("NUM")); 395} 396 397std::string TidEntry::format(const LogStatistics& stat, 398 log_id_t /* id */) const { 399 uid_t uid = getUid(); 400 std::string name = android::base::StringPrintf("%5u/%u", getTid(), uid); 401 const char* nameTmp = getName(); 402 if (nameTmp) { 403 name += android::base::StringPrintf( 404 "%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", nameTmp); 405 } else if ((nameTmp = stat.uidToName(uid))) { 406 // if we do not have a PID name, lets punt to try UID name? 407 name += android::base::StringPrintf( 408 "%*s%s", (int)std::max(12 - name.length(), (size_t)1), "", nameTmp); 409 free(const_cast<char*>(nameTmp)); 410 // We tried, better to not have a name at all, we still 411 // have TID/UID by number to report in any case. 412 } 413 414 std::string size = android::base::StringPrintf("%zu", getSizes()); 415 416 std::string pruned = ""; 417 size_t dropped = getDropped(); 418 if (dropped) { 419 pruned = android::base::StringPrintf("%zu", dropped); 420 } 421 422 return formatLine(name, size, pruned); 423} 424 425std::string TagEntry::formatHeader(const std::string& name, log_id_t id) const { 426 bool isprune = worstUidEnabledForLogid(id); 427 return formatLine(name, std::string("Size"), 428 std::string(isprune ? "Prune" : "")) + 429 formatLine(std::string(" TAG/UID TAGNAME"), 430 std::string("BYTES"), std::string(isprune ? "NUM" : "")); 431} 432 433std::string TagEntry::format(const LogStatistics& /* stat */, 434 log_id_t /* id */) const { 435 std::string name; 436 uid_t uid = getUid(); 437 if (uid == (uid_t)-1) { 438 name = android::base::StringPrintf("%7u", getKey()); 439 } else { 440 name = android::base::StringPrintf("%7u/%u", getKey(), uid); 441 } 442 const char* nameTmp = getName(); 443 if (nameTmp) { 444 name += android::base::StringPrintf( 445 "%*s%s", (int)std::max(14 - name.length(), (size_t)1), "", nameTmp); 446 } 447 448 std::string size = android::base::StringPrintf("%zu", getSizes()); 449 450 std::string pruned = ""; 451 size_t dropped = getDropped(); 452 if (dropped) { 453 pruned = android::base::StringPrintf("%zu", dropped); 454 } 455 456 return formatLine(name, size, pruned); 457} 458 459std::string LogStatistics::format(uid_t uid, pid_t pid, 460 unsigned int logMask) const { 461 static const unsigned short spaces_total = 19; 462 463 // Report on total logging, current and for all time 464 465 std::string output = "size/num"; 466 size_t oldLength; 467 short spaces = 1; 468 469 log_id_for_each(id) { 470 if (!(logMask & (1 << id))) continue; 471 oldLength = output.length(); 472 if (spaces < 0) spaces = 0; 473 output += android::base::StringPrintf("%*s%s", spaces, "", 474 android_log_id_to_name(id)); 475 spaces += spaces_total + oldLength - output.length(); 476 } 477 if (spaces < 0) spaces = 0; 478 output += android::base::StringPrintf("%*sTotal", spaces, ""); 479 480 static const char TotalStr[] = "\nTotal"; 481 spaces = 10 - strlen(TotalStr); 482 output += TotalStr; 483 484 size_t totalSize = 0; 485 size_t totalEls = 0; 486 log_id_for_each(id) { 487 if (!(logMask & (1 << id))) continue; 488 oldLength = output.length(); 489 if (spaces < 0) spaces = 0; 490 size_t szs = sizesTotal(id); 491 totalSize += szs; 492 size_t els = elementsTotal(id); 493 totalEls += els; 494 output += 495 android::base::StringPrintf("%*s%zu/%zu", spaces, "", szs, els); 496 spaces += spaces_total + oldLength - output.length(); 497 } 498 if (spaces < 0) spaces = 0; 499 output += android::base::StringPrintf("%*s%zu/%zu", spaces, "", totalSize, 500 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 += 519 android::base::StringPrintf("%*s%zu/%zu", spaces, "", szs, els); 520 spaces -= output.length() - oldLength; 521 } 522 spaces += spaces_total; 523 } 524 if (spaces < 0) spaces = 0; 525 output += android::base::StringPrintf("%*s%zu/%zu", spaces, "", totalSize, 526 totalEls); 527 528 static const char OverheadStr[] = "\nOverhead"; 529 spaces = 10 - strlen(OverheadStr); 530 output += OverheadStr; 531 532 totalSize = 0; 533 log_id_for_each(id) { 534 if (!(logMask & (1 << id))) continue; 535 536 size_t els = elements(id); 537 if (els) { 538 oldLength = output.length(); 539 if (spaces < 0) spaces = 0; 540 // estimate the std::list overhead. 541 static const size_t overhead = 542 ((sizeof(LogBufferElement) + sizeof(uint64_t) - 1) & 543 -sizeof(uint64_t)) + 544 sizeof(std::list<LogBufferElement*>); 545 size_t szs = sizes(id) + els * overhead; 546 totalSize += szs; 547 output += android::base::StringPrintf("%*s%zu", spaces, "", szs); 548 spaces -= output.length() - oldLength; 549 } 550 spaces += spaces_total; 551 } 552 totalSize += sizeOf(); 553 if (spaces < 0) spaces = 0; 554 output += android::base::StringPrintf("%*s%zu", spaces, "", totalSize); 555 556 // Report on Chattiest 557 558 std::string name; 559 560 // Chattiest by application (UID) 561 log_id_for_each(id) { 562 if (!(logMask & (1 << id))) continue; 563 564 name = (uid == AID_ROOT) ? "Chattiest UIDs in %s log buffer:" 565 : "Logging for your UID in %s log buffer:"; 566 output += uidTable[id].format(*this, uid, pid, name, id); 567 } 568 569 if (enable) { 570 name = ((uid == AID_ROOT) && !pid) ? "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 += 591 securityTagTable.format(*this, uid, pid, name, LOG_ID_SECURITY); 592 } 593 594 return output; 595} 596 597namespace android { 598 599uid_t pidToUid(pid_t pid) { 600 char buffer[512]; 601 snprintf(buffer, sizeof(buffer), "/proc/%u/status", pid); 602 FILE* fp = fopen(buffer, "r"); 603 if (fp) { 604 while (fgets(buffer, sizeof(buffer), fp)) { 605 int uid; 606 if (sscanf(buffer, "Uid: %d", &uid) == 1) { 607 fclose(fp); 608 return uid; 609 } 610 } 611 fclose(fp); 612 } 613 return AID_LOGD; // associate this with the logger 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