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