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