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 #pragma once 18 19 #include <ctype.h> 20 #include <inttypes.h> 21 #include <stdint.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <sys/types.h> 25 26 #include <algorithm> // std::max 27 #include <array> 28 #include <memory> 29 #include <mutex> 30 #include <string> 31 #include <string_view> 32 #include <unordered_map> 33 34 #include <android-base/stringprintf.h> 35 #include <android-base/thread_annotations.h> 36 #include <android/log.h> 37 #include <log/log_time.h> 38 #include <private/android_filesystem_config.h> 39 #include <utils/FastStrcmp.h> 40 41 #include "LogUtils.h" 42 43 #define log_id_for_each(i) \ 44 for (log_id_t i = LOG_ID_MIN; (i) < LOG_ID_MAX; (i) = (log_id_t)((i) + 1)) 45 46 class LogStatistics; 47 class UidEntry; 48 class PidEntry; 49 50 struct LogStatisticsElement { 51 uid_t uid; 52 pid_t pid; 53 pid_t tid; 54 uint32_t tag; 55 log_time realtime; 56 const char* msg; 57 uint16_t msg_len; 58 log_id_t log_id; 59 uint16_t total_len; 60 }; 61 62 template <typename TKey, typename TEntry> 63 class LogHashtable { 64 std::unordered_map<TKey, TEntry> map; 65 bucket_size()66 size_t bucket_size() const { 67 size_t count = 0; 68 for (size_t idx = 0; idx < map.bucket_count(); ++idx) { 69 size_t bucket_size = map.bucket_size(idx); 70 if (bucket_size == 0) bucket_size = 1; 71 count += bucket_size; 72 } 73 float load_factor = map.max_load_factor(); 74 if (load_factor < 1.0) return count; 75 return count * load_factor; 76 } 77 78 static const size_t unordered_map_per_entry_overhead = sizeof(void*); 79 static const size_t unordered_map_bucket_overhead = sizeof(void*); 80 81 public: size()82 size_t size() const { 83 return map.size(); 84 } 85 86 // Estimate unordered_map memory usage. sizeOf()87 size_t sizeOf() const { 88 return sizeof(*this) + 89 (size() * (sizeof(TEntry) + unordered_map_per_entry_overhead)) + 90 (bucket_size() * sizeof(size_t) + unordered_map_bucket_overhead); 91 } 92 93 typedef typename std::unordered_map<TKey, TEntry>::iterator iterator; 94 typedef 95 typename std::unordered_map<TKey, TEntry>::const_iterator const_iterator; 96 97 // Returns a sorted array of up to len highest entries sorted by size. If fewer than len 98 // entries are found, their positions are set to nullptr. 99 template <size_t len> MaxEntries(uid_t uid,pid_t pid,std::array<const TKey *,len> & out_keys,std::array<const TEntry *,len> & out_entries)100 void MaxEntries(uid_t uid, pid_t pid, std::array<const TKey*, len>& out_keys, 101 std::array<const TEntry*, len>& out_entries) const { 102 out_keys.fill(nullptr); 103 out_entries.fill(nullptr); 104 for (const auto& [key, entry] : map) { 105 uid_t entry_uid = 0; 106 if constexpr (std::is_same_v<TEntry, UidEntry>) { 107 entry_uid = key; 108 } else { 109 entry_uid = entry.uid(); 110 } 111 if (uid != AID_ROOT && uid != entry_uid) { 112 continue; 113 } 114 pid_t entry_pid = 0; 115 if constexpr (std::is_same_v<TEntry, PidEntry>) { 116 entry_pid = key; 117 } else { 118 entry_pid = entry.pid(); 119 } 120 if (pid && entry_pid && pid != entry_pid) { 121 continue; 122 } 123 124 size_t sizes = entry.getSizes(); 125 ssize_t index = len - 1; 126 while ((!out_entries[index] || sizes > out_entries[index]->getSizes()) && --index >= 0) 127 ; 128 if (++index < (ssize_t)len) { 129 size_t num = len - index - 1; 130 if (num) { 131 memmove(&out_keys[index + 1], &out_keys[index], num * sizeof(const TKey*)); 132 memmove(&out_entries[index + 1], &out_entries[index], 133 num * sizeof(const TEntry*)); 134 } 135 out_keys[index] = &key; 136 out_entries[index] = &entry; 137 } 138 } 139 } 140 Add(const TKey & key,const LogStatisticsElement & element)141 iterator Add(const TKey& key, const LogStatisticsElement& element) { 142 iterator it = map.find(key); 143 if (it == map.end()) { 144 it = map.insert(std::make_pair(key, TEntry(element))).first; 145 } else { 146 it->second.Add(element); 147 } 148 return it; 149 } 150 Add(const TKey & key)151 iterator Add(const TKey& key) { 152 iterator it = map.find(key); 153 if (it == map.end()) { 154 it = map.insert(std::make_pair(key, TEntry(key))).first; 155 } else { 156 it->second.Add(key); 157 } 158 return it; 159 } 160 Subtract(const TKey & key,const LogStatisticsElement & element)161 void Subtract(const TKey& key, const LogStatisticsElement& element) { 162 iterator it = map.find(key); 163 if (it != map.end() && it->second.Subtract(element)) { 164 map.erase(it); 165 } 166 } 167 Drop(const TKey & key,const LogStatisticsElement & element)168 void Drop(const TKey& key, const LogStatisticsElement& element) { 169 iterator it = map.find(key); 170 if (it != map.end()) { 171 it->second.Drop(element); 172 } 173 } 174 Erase(const TKey & key,const LogStatisticsElement & element)175 void Erase(const TKey& key, const LogStatisticsElement& element) { 176 iterator it = map.find(key); 177 if (it != map.end()) { 178 it->second.Erase(element); 179 } 180 } 181 begin()182 iterator begin() { return map.begin(); } begin()183 const_iterator begin() const { return map.begin(); } end()184 iterator end() { return map.end(); } end()185 const_iterator end() const { return map.end(); } 186 }; 187 188 class EntryBase { 189 public: EntryBase()190 EntryBase() : size_(0) {} EntryBase(const LogStatisticsElement & element)191 explicit EntryBase(const LogStatisticsElement& element) : size_(element.total_len) {} 192 getSizes()193 size_t getSizes() const { return size_; } 194 Add(const LogStatisticsElement & element)195 void Add(const LogStatisticsElement& element) { size_ += element.total_len; } Subtract(const LogStatisticsElement & element)196 bool Subtract(const LogStatisticsElement& element) { 197 size_ -= element.total_len; 198 return size_ == 0; 199 } Drop(const LogStatisticsElement & element)200 void Drop(const LogStatisticsElement& element) { size_ -= element.msg_len; } Erase(const LogStatisticsElement & element)201 void Erase(const LogStatisticsElement& element) { size_ -= element.total_len; } 202 203 static constexpr size_t PRUNED_LEN = 14; 204 static constexpr size_t TOTAL_LEN = 80; 205 formatLine(const std::string & name,const std::string & size,const std::string & pruned)206 static std::string formatLine(const std::string& name, 207 const std::string& size, 208 const std::string& pruned) { 209 ssize_t drop_len = std::max(pruned.length() + 1, PRUNED_LEN); 210 ssize_t size_len = std::max(size.length() + 1, TOTAL_LEN - name.length() - drop_len - 1); 211 212 std::string ret = android::base::StringPrintf( 213 "%s%*s%*s", name.c_str(), (int)size_len, size.c_str(), 214 (int)drop_len, pruned.c_str()); 215 // remove any trailing spaces 216 size_t pos = ret.size(); 217 size_t len = 0; 218 while (pos && isspace(ret[--pos])) ++len; 219 if (len) ret.erase(pos + 1, len); 220 return ret + "\n"; 221 } 222 223 private: 224 size_t size_; 225 }; 226 227 class UidEntry : public EntryBase { 228 public: UidEntry(const LogStatisticsElement & element)229 explicit UidEntry(const LogStatisticsElement& element) 230 : EntryBase(element), pid_(element.pid) {} 231 pid()232 pid_t pid() const { return pid_; } 233 Add(const LogStatisticsElement & element)234 void Add(const LogStatisticsElement& element) { 235 if (pid_ != element.pid) { 236 pid_ = -1; 237 } 238 EntryBase::Add(element); 239 } 240 241 std::string formatHeader(const std::string& name, log_id_t id) const; 242 std::string format(const LogStatistics& stat, log_id_t id, uid_t uid) const; 243 244 private: 245 pid_t pid_; 246 }; 247 248 namespace android { 249 uid_t pidToUid(pid_t pid); 250 } 251 252 class PidEntry : public EntryBase { 253 public: PidEntry(pid_t pid)254 explicit PidEntry(pid_t pid) 255 : EntryBase(), uid_(android::pidToUid(pid)), name_(android::pidToName(pid)) {} PidEntry(const LogStatisticsElement & element)256 explicit PidEntry(const LogStatisticsElement& element) 257 : EntryBase(element), uid_(element.uid), name_(android::pidToName(element.pid)) {} PidEntry(const PidEntry & element)258 PidEntry(const PidEntry& element) 259 : EntryBase(element), 260 uid_(element.uid_), 261 name_(element.name_ ? strdup(element.name_) : nullptr) {} ~PidEntry()262 ~PidEntry() { free(name_); } 263 uid()264 uid_t uid() const { return uid_; } name()265 const char* name() const { return name_; } 266 Add(pid_t new_pid)267 void Add(pid_t new_pid) { 268 if (name_ && !fastcmp<strncmp>(name_, "zygote", 6)) { 269 free(name_); 270 name_ = nullptr; 271 } 272 if (!name_) { 273 name_ = android::pidToName(new_pid); 274 } 275 } 276 Add(const LogStatisticsElement & element)277 void Add(const LogStatisticsElement& element) { 278 uid_t incoming_uid = element.uid; 279 if (uid() != incoming_uid) { 280 uid_ = incoming_uid; 281 free(name_); 282 name_ = android::pidToName(element.pid); 283 } else { 284 Add(element.pid); 285 } 286 EntryBase::Add(element); 287 } 288 289 std::string formatHeader(const std::string& name, log_id_t id) const; 290 std::string format(const LogStatistics& stat, log_id_t id, pid_t pid) const; 291 292 private: 293 uid_t uid_; 294 char* name_; 295 }; 296 297 class TidEntry : public EntryBase { 298 public: TidEntry(pid_t tid,pid_t pid)299 TidEntry(pid_t tid, pid_t pid) 300 : EntryBase(), pid_(pid), uid_(android::pidToUid(tid)), name_(android::tidToName(tid)) {} TidEntry(const LogStatisticsElement & element)301 explicit TidEntry(const LogStatisticsElement& element) 302 : EntryBase(element), 303 pid_(element.pid), 304 uid_(element.uid), 305 name_(android::tidToName(element.tid)) {} TidEntry(const TidEntry & element)306 TidEntry(const TidEntry& element) 307 : EntryBase(element), 308 pid_(element.pid_), 309 uid_(element.uid_), 310 name_(element.name_ ? strdup(element.name_) : nullptr) {} ~TidEntry()311 ~TidEntry() { free(name_); } 312 pid()313 pid_t pid() const { return pid_; } uid()314 uid_t uid() const { return uid_; } name()315 const char* name() const { return name_; } 316 Add(pid_t incomingTid)317 void Add(pid_t incomingTid) { 318 if (name_ && !fastcmp<strncmp>(name_, "zygote", 6)) { 319 free(name_); 320 name_ = nullptr; 321 } 322 if (!name_) { 323 name_ = android::tidToName(incomingTid); 324 } 325 } 326 Add(const LogStatisticsElement & element)327 void Add(const LogStatisticsElement& element) { 328 uid_t incoming_uid = element.uid; 329 pid_t incoming_pid = element.pid; 330 if (uid() != incoming_uid || pid() != incoming_pid) { 331 uid_ = incoming_uid; 332 pid_ = incoming_pid; 333 free(name_); 334 name_ = android::tidToName(element.tid); 335 } else { 336 Add(element.tid); 337 } 338 EntryBase::Add(element); 339 } 340 341 std::string formatHeader(const std::string& name, log_id_t id) const; 342 std::string format(const LogStatistics& stat, log_id_t id, pid_t pid) const; 343 344 private: 345 pid_t pid_; 346 uid_t uid_; 347 char* name_; 348 }; 349 350 class TagEntry : public EntryBase { 351 public: TagEntry(const LogStatisticsElement & element)352 explicit TagEntry(const LogStatisticsElement& element) 353 : EntryBase(element), tag_(element.tag), pid_(element.pid), uid_(element.uid) {} 354 key()355 uint32_t key() const { return tag_; } pid()356 pid_t pid() const { return pid_; } uid()357 uid_t uid() const { return uid_; } name()358 const char* name() const { return android::tagToName(tag_); } 359 Add(const LogStatisticsElement & element)360 void Add(const LogStatisticsElement& element) { 361 if (uid_ != element.uid) { 362 uid_ = -1; 363 } 364 if (pid_ != element.pid) { 365 pid_ = -1; 366 } 367 EntryBase::Add(element); 368 } 369 370 std::string formatHeader(const std::string& name, log_id_t id) const; 371 std::string format(const LogStatistics& stat, log_id_t id, uint32_t) const; 372 373 private: 374 const uint32_t tag_; 375 pid_t pid_; 376 uid_t uid_; 377 }; 378 379 class TagNameEntry : public EntryBase { 380 public: TagNameEntry(const LogStatisticsElement & element)381 explicit TagNameEntry(const LogStatisticsElement& element) 382 : EntryBase(element), tid_(element.tid), pid_(element.pid), uid_(element.uid) {} 383 tid()384 pid_t tid() const { return tid_; } pid()385 pid_t pid() const { return pid_; } uid()386 uid_t uid() const { return uid_; } 387 Add(const LogStatisticsElement & element)388 void Add(const LogStatisticsElement& element) { 389 if (uid_ != element.uid) { 390 uid_ = -1; 391 } 392 if (pid_ != element.pid) { 393 pid_ = -1; 394 } 395 if (tid_ != element.tid) { 396 tid_ = -1; 397 } 398 EntryBase::Add(element); 399 } 400 401 std::string formatHeader(const std::string& name, log_id_t id) const; 402 std::string format(const LogStatistics& stat, log_id_t id, const std::string& key_name) const; 403 404 private: 405 pid_t tid_; 406 pid_t pid_; 407 uid_t uid_; 408 }; 409 410 class LogStatistics { 411 friend UidEntry; 412 friend PidEntry; 413 friend TidEntry; 414 415 size_t mSizes[LOG_ID_MAX] GUARDED_BY(lock_); 416 size_t mElements[LOG_ID_MAX] GUARDED_BY(lock_); 417 size_t mSizesTotal[LOG_ID_MAX] GUARDED_BY(lock_); 418 size_t mElementsTotal[LOG_ID_MAX] GUARDED_BY(lock_); 419 log_time mOldest[LOG_ID_MAX] GUARDED_BY(lock_); 420 log_time mNewest[LOG_ID_MAX] GUARDED_BY(lock_); 421 static std::atomic<size_t> SizesTotal; 422 bool enable; 423 424 // uid to size list 425 typedef LogHashtable<uid_t, UidEntry> uidTable_t; 426 uidTable_t uidTable[LOG_ID_MAX] GUARDED_BY(lock_); 427 428 // pid of system to size list 429 typedef LogHashtable<pid_t, PidEntry> pidSystemTable_t; 430 pidSystemTable_t pidSystemTable[LOG_ID_MAX] GUARDED_BY(lock_); 431 432 // pid to uid list 433 typedef LogHashtable<pid_t, PidEntry> pidTable_t; 434 pidTable_t pidTable GUARDED_BY(lock_); 435 436 // tid to uid list 437 typedef LogHashtable<pid_t, TidEntry> tidTable_t; 438 tidTable_t tidTable GUARDED_BY(lock_); 439 440 // tag list 441 typedef LogHashtable<uint32_t, TagEntry> tagTable_t; 442 tagTable_t tagTable GUARDED_BY(lock_); 443 444 // security tag list 445 tagTable_t securityTagTable GUARDED_BY(lock_); 446 447 // global tag list 448 typedef LogHashtable<std::string, TagNameEntry> tagNameTable_t; 449 tagNameTable_t tagNameTable; 450 sizeOf()451 size_t sizeOf() const REQUIRES(lock_) { 452 size_t size = sizeof(*this) + pidTable.sizeOf() + tidTable.sizeOf() + 453 tagTable.sizeOf() + securityTagTable.sizeOf() + 454 tagNameTable.sizeOf() + 455 (pidTable.size() * sizeof(pidTable_t::iterator)) + 456 (tagTable.size() * sizeof(tagTable_t::iterator)); 457 for (const auto& it : pidTable) { 458 const char* name = it.second.name(); 459 if (name) size += strlen(name) + 1; 460 } 461 for (const auto& it : tidTable) { 462 const char* name = it.second.name(); 463 if (name) size += strlen(name) + 1; 464 } 465 for (const auto& it : tagNameTable) { 466 size += sizeof(std::string); 467 size_t len = it.first.size(); 468 // Account for short string optimization: if the string's length is <= 22 bytes for 64 469 // bit or <= 10 bytes for 32 bit, then there is no additional allocation. 470 if ((sizeof(std::string) == 24 && len > 22) || 471 (sizeof(std::string) != 24 && len > 10)) { 472 size += len; 473 } 474 } 475 log_id_for_each(id) { 476 size += uidTable[id].sizeOf(); 477 size += uidTable[id].size() * sizeof(uidTable_t::iterator); 478 size += pidSystemTable[id].sizeOf(); 479 size += pidSystemTable[id].size() * sizeof(pidSystemTable_t::iterator); 480 } 481 return size; 482 } 483 484 public: 485 LogStatistics(bool enable_statistics, bool track_total_size, 486 std::optional<log_time> start_time = {}); 487 488 void AddTotal(log_id_t log_id, uint16_t size) EXCLUDES(lock_); 489 490 // Add is for adding an element to the log buffer. 491 // Add the total size of the element to statistics. 492 void Add(LogStatisticsElement entry) EXCLUDES(lock_); 493 // Subtract is for removing an element from the log buffer. 494 // Subtract the total size of the element from statistics. 495 void Subtract(LogStatisticsElement entry) EXCLUDES(lock_); 496 497 bool ShouldPrune(log_id id, unsigned long max_size, unsigned long* prune_rows) const 498 EXCLUDES(lock_); 499 500 // Return the consumed size of the given buffer. Sizes(log_id_t id)501 size_t Sizes(log_id_t id) const EXCLUDES(lock_) { 502 auto lock = std::lock_guard{lock_}; 503 if (overhead_[id]) { 504 return *overhead_[id]; 505 } 506 return mSizes[id]; 507 } 508 509 // Return the uncompressed size of the contents of the given buffer. SizeReadable(log_id_t id)510 size_t SizeReadable(log_id_t id) const EXCLUDES(lock_) { 511 auto lock = std::lock_guard{lock_}; 512 return mSizes[id]; 513 } 514 515 // TODO: Get rid of this entirely. sizesTotal()516 static size_t sizesTotal() { 517 return SizesTotal; 518 } 519 520 std::string ReportInteresting() const EXCLUDES(lock_); 521 std::string Format(uid_t uid, pid_t pid, unsigned int logMask) const EXCLUDES(lock_); 522 523 const char* PidToName(pid_t pid) const EXCLUDES(lock_); 524 uid_t PidToUid(pid_t pid) EXCLUDES(lock_); 525 const char* UidToName(uid_t uid) const EXCLUDES(lock_); 526 set_overhead(log_id_t id,size_t size)527 void set_overhead(log_id_t id, size_t size) { 528 auto lock = std::lock_guard{lock_}; 529 overhead_[id] = size; 530 } 531 532 private: 533 template <typename TKey, typename TEntry> 534 std::string FormatTable(const LogHashtable<TKey, TEntry>& table, uid_t uid, pid_t pid, 535 const std::string& name = std::string(""), 536 log_id_t id = LOG_ID_MAX) const REQUIRES(lock_); 537 void FormatTmp(const char* nameTmp, uid_t uid, std::string& name, std::string& size, 538 size_t nameLen) const REQUIRES(lock_); 539 const char* UidToNameLocked(uid_t uid) const REQUIRES(lock_); 540 541 mutable std::mutex lock_; 542 bool track_total_size_; 543 544 std::optional<size_t> overhead_[LOG_ID_MAX] GUARDED_BY(lock_); 545 }; 546