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 #ifndef _LOGD_LOG_STATISTICS_H__ 18 #define _LOGD_LOG_STATISTICS_H__ 19 20 #include <memory> 21 #include <stdlib.h> 22 #include <sys/types.h> 23 24 #include <algorithm> // std::max 25 #include <string> // std::string 26 #include <unordered_map> 27 28 #include <android-base/stringprintf.h> 29 #include <log/log.h> 30 #include <private/android_filesystem_config.h> 31 32 #include "LogBufferElement.h" 33 #include "LogUtils.h" 34 35 #define log_id_for_each(i) \ 36 for (log_id_t i = LOG_ID_MIN; i < LOG_ID_MAX; i = (log_id_t) (i + 1)) 37 38 class LogStatistics; 39 40 template <typename TKey, typename TEntry> 41 class LogHashtable { 42 43 std::unordered_map<TKey, TEntry> map; 44 45 public: 46 47 typedef typename std::unordered_map<TKey, TEntry>::iterator iterator; 48 typedef typename std::unordered_map<TKey, TEntry>::const_iterator const_iterator; 49 50 std::unique_ptr<const TEntry *[]> sort(uid_t uid, pid_t pid, 51 size_t len) const { 52 if (!len) { 53 std::unique_ptr<const TEntry *[]> sorted(NULL); 54 return sorted; 55 } 56 57 const TEntry **retval = new const TEntry* [len]; 58 memset(retval, 0, sizeof(*retval) * len); 59 60 for(const_iterator it = map.begin(); it != map.end(); ++it) { 61 const TEntry &entry = it->second; 62 63 if ((uid != AID_ROOT) && (uid != entry.getUid())) { 64 continue; 65 } 66 if (pid && entry.getPid() && (pid != entry.getPid())) { 67 continue; 68 } 69 70 size_t sizes = entry.getSizes(); 71 ssize_t index = len - 1; 72 while ((!retval[index] || (sizes > retval[index]->getSizes())) 73 && (--index >= 0)) 74 ; 75 if (++index < (ssize_t)len) { 76 size_t num = len - index - 1; 77 if (num) { 78 memmove(&retval[index + 1], &retval[index], 79 num * sizeof(retval[0])); 80 } 81 retval[index] = &entry; 82 } 83 } 84 std::unique_ptr<const TEntry *[]> sorted(retval); 85 return sorted; 86 } 87 88 inline iterator add(TKey key, LogBufferElement *element) { 89 iterator it = map.find(key); 90 if (it == map.end()) { 91 it = map.insert(std::make_pair(key, TEntry(element))).first; 92 } else { 93 it->second.add(element); 94 } 95 return it; 96 } 97 98 inline iterator add(TKey key) { 99 iterator it = map.find(key); 100 if (it == map.end()) { 101 it = map.insert(std::make_pair(key, TEntry(key))).first; 102 } else { 103 it->second.add(key); 104 } 105 return it; 106 } 107 108 void subtract(TKey key, LogBufferElement *element) { 109 iterator it = map.find(key); 110 if ((it != map.end()) && it->second.subtract(element)) { 111 map.erase(it); 112 } 113 } 114 115 inline void drop(TKey key, LogBufferElement *element) { 116 iterator it = map.find(key); 117 if (it != map.end()) { 118 it->second.drop(element); 119 } 120 } 121 122 inline iterator begin() { return map.begin(); } 123 inline const_iterator begin() const { return map.begin(); } 124 inline iterator end() { return map.end(); } 125 inline const_iterator end() const { return map.end(); } 126 127 std::string format( 128 const LogStatistics &stat, 129 uid_t uid, 130 pid_t pid, 131 const std::string &name = std::string(""), 132 log_id_t id = LOG_ID_MAX) const { 133 static const size_t maximum_sorted_entries = 32; 134 std::string output; 135 std::unique_ptr<const TEntry *[]> sorted = sort(uid, pid, 136 maximum_sorted_entries); 137 if (!sorted.get()) { 138 return output; 139 } 140 bool headerPrinted = false; 141 for (size_t index = 0; index < maximum_sorted_entries; ++index) { 142 const TEntry *entry = sorted[index]; 143 if (!entry) { 144 break; 145 } 146 if (entry->getSizes() <= (sorted[0]->getSizes() / 100)) { 147 break; 148 } 149 if (!headerPrinted) { 150 output += "\n\n"; 151 output += entry->formatHeader(name, id); 152 headerPrinted = true; 153 } 154 output += entry->format(stat, id); 155 } 156 return output; 157 } 158 }; 159 160 namespace EntryBaseConstants { 161 static constexpr size_t pruned_len = 14; 162 static constexpr size_t total_len = 80; 163 } 164 165 struct EntryBase { 166 size_t size; 167 168 EntryBase():size(0) { } 169 EntryBase(LogBufferElement *element):size(element->getMsgLen()) { } 170 171 size_t getSizes() const { return size; } 172 173 inline void add(LogBufferElement *element) { size += element->getMsgLen(); } 174 inline bool subtract(LogBufferElement *element) { 175 size -= element->getMsgLen(); 176 return !size; 177 } 178 179 static std::string formatLine( 180 const std::string &name, 181 const std::string &size, 182 const std::string &pruned) { 183 ssize_t drop_len = std::max(pruned.length() + 1, 184 EntryBaseConstants::pruned_len); 185 ssize_t size_len = std::max(size.length() + 1, 186 EntryBaseConstants::total_len 187 - name.length() - drop_len - 1); 188 189 if (pruned.length()) { 190 return android::base::StringPrintf("%s%*s%*s\n", name.c_str(), 191 (int)size_len, size.c_str(), 192 (int)drop_len, pruned.c_str()); 193 } else { 194 return android::base::StringPrintf("%s%*s\n", name.c_str(), 195 (int)size_len, size.c_str()); 196 } 197 } 198 }; 199 200 struct EntryBaseDropped : public EntryBase { 201 size_t dropped; 202 203 EntryBaseDropped():dropped(0) { } 204 EntryBaseDropped(LogBufferElement *element): 205 EntryBase(element), 206 dropped(element->getDropped()){ 207 } 208 209 size_t getDropped() const { return dropped; } 210 211 inline void add(LogBufferElement *element) { 212 dropped += element->getDropped(); 213 EntryBase::add(element); 214 } 215 inline bool subtract(LogBufferElement *element) { 216 dropped -= element->getDropped(); 217 return EntryBase::subtract(element) && !dropped; 218 } 219 inline void drop(LogBufferElement *element) { 220 dropped += 1; 221 EntryBase::subtract(element); 222 } 223 }; 224 225 struct UidEntry : public EntryBaseDropped { 226 const uid_t uid; 227 pid_t pid; 228 229 UidEntry(LogBufferElement *element): 230 EntryBaseDropped(element), 231 uid(element->getUid()), 232 pid(element->getPid()) { 233 } 234 235 inline const uid_t&getKey() const { return uid; } 236 inline const uid_t&getUid() const { return getKey(); } 237 inline const pid_t&getPid() const { return pid; } 238 239 inline void add(LogBufferElement *element) { 240 if (pid != element->getPid()) { 241 pid = -1; 242 } 243 EntryBase::add(element); 244 } 245 246 std::string formatHeader(const std::string &name, log_id_t id) const; 247 std::string format(const LogStatistics &stat, log_id_t id) const; 248 }; 249 250 namespace android { 251 uid_t pidToUid(pid_t pid); 252 } 253 254 struct PidEntry : public EntryBaseDropped { 255 const pid_t pid; 256 uid_t uid; 257 char *name; 258 259 PidEntry(pid_t pid): 260 EntryBaseDropped(), 261 pid(pid), 262 uid(android::pidToUid(pid)), 263 name(android::pidToName(pid)) { 264 } 265 PidEntry(LogBufferElement *element): 266 EntryBaseDropped(element), 267 pid(element->getPid()), 268 uid(element->getUid()), 269 name(android::pidToName(pid)) { 270 } 271 PidEntry(const PidEntry &element): 272 EntryBaseDropped(element), 273 pid(element.pid), 274 uid(element.uid), 275 name(element.name ? strdup(element.name) : NULL) { 276 } 277 ~PidEntry() { free(name); } 278 279 const pid_t&getKey() const { return pid; } 280 const pid_t&getPid() const { return getKey(); } 281 const uid_t&getUid() const { return uid; } 282 const char*getName() const { return name; } 283 284 inline void add(pid_t newPid) { 285 if (name && !fast<strncmp>(name, "zygote", 6)) { 286 free(name); 287 name = NULL; 288 } 289 if (!name) { 290 name = android::pidToName(newPid); 291 } 292 } 293 294 inline void add(LogBufferElement *element) { 295 uid_t incomingUid = element->getUid(); 296 if (getUid() != incomingUid) { 297 uid = incomingUid; 298 free(name); 299 name = android::pidToName(element->getPid()); 300 } else { 301 add(element->getPid()); 302 } 303 EntryBaseDropped::add(element); 304 } 305 306 std::string formatHeader(const std::string &name, log_id_t id) const; 307 std::string format(const LogStatistics &stat, log_id_t id) const; 308 }; 309 310 struct TidEntry : public EntryBaseDropped { 311 const pid_t tid; 312 pid_t pid; 313 uid_t uid; 314 char *name; 315 316 TidEntry(pid_t tid, pid_t pid): 317 EntryBaseDropped(), 318 tid(tid), 319 pid(pid), 320 uid(android::pidToUid(tid)), 321 name(android::tidToName(tid)) { 322 } 323 TidEntry(LogBufferElement *element): 324 EntryBaseDropped(element), 325 tid(element->getTid()), 326 pid(element->getPid()), 327 uid(element->getUid()), 328 name(android::tidToName(tid)) { 329 } 330 TidEntry(const TidEntry &element): 331 EntryBaseDropped(element), 332 tid(element.tid), 333 pid(element.pid), 334 uid(element.uid), 335 name(element.name ? strdup(element.name) : NULL) { 336 } 337 ~TidEntry() { free(name); } 338 339 const pid_t&getKey() const { return tid; } 340 const pid_t&getTid() const { return getKey(); } 341 const pid_t&getPid() const { return pid; } 342 const uid_t&getUid() const { return uid; } 343 const char*getName() const { return name; } 344 345 inline void add(pid_t incomingTid) { 346 if (name && !fast<strncmp>(name, "zygote", 6)) { 347 free(name); 348 name = NULL; 349 } 350 if (!name) { 351 name = android::tidToName(incomingTid); 352 } 353 } 354 355 inline void add(LogBufferElement *element) { 356 uid_t incomingUid = element->getUid(); 357 pid_t incomingPid = element->getPid(); 358 if ((getUid() != incomingUid) || (getPid() != incomingPid)) { 359 uid = incomingUid; 360 pid = incomingPid; 361 free(name); 362 name = android::tidToName(element->getTid()); 363 } else { 364 add(element->getTid()); 365 } 366 EntryBaseDropped::add(element); 367 } 368 369 std::string formatHeader(const std::string &name, log_id_t id) const; 370 std::string format(const LogStatistics &stat, log_id_t id) const; 371 }; 372 373 struct TagEntry : public EntryBase { 374 const uint32_t tag; 375 pid_t pid; 376 uid_t uid; 377 378 TagEntry(LogBufferElement *element): 379 EntryBase(element), 380 tag(element->getTag()), 381 pid(element->getPid()), 382 uid(element->getUid()) { 383 } 384 385 const uint32_t&getKey() const { return tag; } 386 const pid_t&getPid() const { return pid; } 387 const uid_t&getUid() const { return uid; } 388 const char*getName() const { return android::tagToName(tag); } 389 390 inline void add(LogBufferElement *element) { 391 if (uid != element->getUid()) { 392 uid = -1; 393 } 394 if (pid != element->getPid()) { 395 pid = -1; 396 } 397 EntryBase::add(element); 398 } 399 400 std::string formatHeader(const std::string &name, log_id_t id) const; 401 std::string format(const LogStatistics &stat, log_id_t id) const; 402 }; 403 404 // Log Statistics 405 class LogStatistics { 406 friend UidEntry; 407 408 size_t mSizes[LOG_ID_MAX]; 409 size_t mElements[LOG_ID_MAX]; 410 size_t mDroppedElements[LOG_ID_MAX]; 411 size_t mSizesTotal[LOG_ID_MAX]; 412 size_t mElementsTotal[LOG_ID_MAX]; 413 bool enable; 414 415 // uid to size list 416 typedef LogHashtable<uid_t, UidEntry> uidTable_t; 417 uidTable_t uidTable[LOG_ID_MAX]; 418 419 // pid of system to size list 420 typedef LogHashtable<pid_t, PidEntry> pidSystemTable_t; 421 pidSystemTable_t pidSystemTable[LOG_ID_MAX]; 422 423 // pid to uid list 424 typedef LogHashtable<pid_t, PidEntry> pidTable_t; 425 pidTable_t pidTable; 426 427 // tid to uid list 428 typedef LogHashtable<pid_t, TidEntry> tidTable_t; 429 tidTable_t tidTable; 430 431 // tag list 432 typedef LogHashtable<uint32_t, TagEntry> tagTable_t; 433 tagTable_t tagTable; 434 435 // security tag list 436 tagTable_t securityTagTable; 437 438 public: 439 LogStatistics(); 440 441 void enableStatistics() { enable = true; } 442 443 void add(LogBufferElement *entry); 444 void subtract(LogBufferElement *entry); 445 // entry->setDropped(1) must follow this call 446 void drop(LogBufferElement *entry); 447 // Correct for coalescing two entries referencing dropped content 448 void erase(LogBufferElement *element) { 449 log_id_t log_id = element->getLogId(); 450 --mElements[log_id]; 451 --mDroppedElements[log_id]; 452 } 453 454 std::unique_ptr<const UidEntry *[]> sort(uid_t uid, pid_t pid, 455 size_t len, log_id id) { 456 return uidTable[id].sort(uid, pid, len); 457 } 458 std::unique_ptr<const PidEntry *[]> sort(uid_t uid, pid_t pid, 459 size_t len, log_id id, uid_t) { 460 return pidSystemTable[id].sort(uid, pid, len); 461 } 462 463 // fast track current value by id only 464 size_t sizes(log_id_t id) const { return mSizes[id]; } 465 size_t elements(log_id_t id) const { return mElements[id]; } 466 size_t realElements(log_id_t id) const { 467 return mElements[id] - mDroppedElements[id]; 468 } 469 size_t sizesTotal(log_id_t id) const { return mSizesTotal[id]; } 470 size_t elementsTotal(log_id_t id) const { return mElementsTotal[id]; } 471 472 std::string format(uid_t uid, pid_t pid, unsigned int logMask) const; 473 474 // helper (must be locked directly or implicitly by mLogElementsLock) 475 const char *pidToName(pid_t pid) const; 476 uid_t pidToUid(pid_t pid); 477 const char *uidToName(uid_t uid) const; 478 }; 479 480 #endif // _LOGD_LOG_STATISTICS_H__ 481