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