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