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