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