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