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