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