1/*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15#include <thread>
16#include <algorithm>
17
18#include <log_utils.h>
19#include <properties.h>
20
21#include "log_stats.h"
22
23namespace OHOS {
24namespace HiviewDFX {
25using namespace std;
26
27LogStats::LogStats()
28{
29    Reset();
30    enable = IsStatsEnable();
31    tagEnable = IsTagStatsEnable();
32}
33LogStats::~LogStats() {}
34
35static inline int idxLvl(uint16_t lvl)
36{
37    return static_cast<int>(lvl) - LevelBase;
38}
39
40static void UpdateStats(StatsEntry &entry, const StatsInfo &info)
41{
42    LogTimeStamp ts_mono(info.mono_sec, info.tv_nsec);
43    static LogTimeStamp ts_audit_period(1, 0); // Audit period : one second
44    ts_mono -= entry.monoTimeLast;
45    if (ts_mono > ts_audit_period) {
46        entry.monoTimeLast -= entry.monoTimeAuditStart;
47        float secs = entry.monoTimeLast.FloatSecs();
48        if (secs < 1.0f) {
49            secs = 1.0f;
50        }
51        float freq = entry.tmpLines / secs;
52        if (freq > entry.freqMax) {
53            entry.freqMax = freq;
54            entry.realTimeFreqMax = entry.realTimeLast;
55        }
56        entry.tmpLines = 0;
57        float throughput = entry.tmpLen / secs;
58        if (throughput > entry.throughputMax) {
59            entry.throughputMax = throughput;
60            entry.realTimeThroughputMax = entry.realTimeLast;
61        }
62        entry.tmpLen = 0;
63        entry.monoTimeAuditStart.SetTimeStamp(info.mono_sec, info.tv_nsec);
64    }
65
66    int lvl = idxLvl(info.level);
67    entry.lines[lvl]++;
68    entry.len[lvl] += info.len;
69    entry.dropped += info.dropped;
70    entry.tmpLines++;
71    entry.tmpLen += info.len;
72    entry.monoTimeLast.SetTimeStamp(info.mono_sec, info.tv_nsec);
73    entry.realTimeLast.SetTimeStamp(info.tv_sec, info.tv_nsec);
74}
75
76static void ResetStatsEntry(StatsEntry &entry)
77{
78    entry.dropped = 0;
79    entry.tmpLines = 0;
80    entry.freqMax = 0;
81    entry.realTimeFreqMax.SetTimeStamp(0, 0);
82    entry.tmpLen = 0;
83    entry.throughputMax = 0;
84    entry.realTimeThroughputMax.SetTimeStamp(0, 0);
85    entry.monoTimeLast.SetTimeStamp(0, 0);
86    entry.monoTimeAuditStart.SetTimeStamp(0, 0);
87    for (uint32_t &i : entry.lines) {
88        i = 0;
89    }
90    for (uint64_t &i : entry.len) {
91        i = 0;
92    }
93}
94
95static void StatsInfo2NewStatsEntry(const StatsInfo &info, StatsEntry &entry)
96{
97    entry.dropped = info.dropped;
98    entry.tmpLines = 1;
99    entry.freqMax = 0;
100    entry.realTimeFreqMax.SetTimeStamp(info.tv_sec, info.tv_nsec);
101    entry.tmpLen = info.len;
102    entry.throughputMax = 0;
103    entry.realTimeThroughputMax.SetTimeStamp(info.tv_sec, info.tv_nsec);
104    entry.realTimeLast.SetTimeStamp(info.tv_sec, info.tv_nsec);
105    entry.monoTimeLast.SetTimeStamp(info.mono_sec, info.tv_nsec);
106    entry.monoTimeAuditStart.SetTimeStamp(info.mono_sec, info.tv_nsec);
107    for (uint32_t &i : entry.lines) {
108        i = 0;
109    }
110    for (uint64_t &i : entry.len) {
111        i = 0;
112    }
113    int lvl = idxLvl(info.level);
114    entry.lines[lvl] = 1;
115    entry.len[lvl] = info.len;
116}
117
118void LogStats::UpdateTagTable(TagTable& tt, const StatsInfo &info)
119{
120    if (!tagEnable) {
121        return;
122    }
123    auto itt = tt.find(info.tag);
124    if (itt != tt.end()) {
125        UpdateStats(itt->second, info);
126    } else {
127        TagStatsEntry entry;
128        StatsInfo2NewStatsEntry(info, entry);
129        (void)tt.emplace(info.tag, entry);
130    }
131}
132
133void LogStats::UpdateDomainTable(const StatsInfo &info)
134{
135    DomainTable& t = domainStats[info.type];
136    auto it = t.find(info.domain);
137    if (it != t.end()) {
138        UpdateStats(it->second.stats, info);
139    } else {
140        DomainStatsEntry entry;
141        StatsInfo2NewStatsEntry(info, entry.stats);
142        auto result = t.emplace(info.domain, entry);
143        if (!result.second) {
144            cerr << "Add entry to DomainTable error" << endl;
145            return;
146        }
147        it = result.first;
148    }
149    UpdateTagTable(it->second.tagStats, info);
150}
151
152void LogStats::UpdatePidTable(const StatsInfo &info)
153{
154    PidTable& t = pidStats;
155    auto it = t.find(info.pid);
156    if (it != t.end()) {
157        UpdateStats(it->second.statsAll, info);
158        UpdateStats(it->second.stats[info.type], info);
159    } else {
160        PidStatsEntry entry;
161        StatsInfo2NewStatsEntry(info, entry.statsAll);
162        for (StatsEntry &e : entry.stats) {
163            ResetStatsEntry(e);
164        }
165        entry.stats[info.type] = entry.statsAll;
166        entry.name = GetNameByPid(info.pid);
167        auto result = t.emplace(info.pid, entry);
168        if (!result.second) {
169            cerr << "Add entry to PidTable error" << endl;
170            return;
171        }
172        it = result.first;
173    }
174    UpdateTagTable(it->second.tagStats, info);
175}
176
177void LogStats::Count(const StatsInfo &info)
178{
179    if (enable) {
180        std::scoped_lock lk(lock);
181        int index = idxLvl(info.level);
182        totalLines[index]++;
183        totalLens[index] += info.len;
184        UpdateDomainTable(info);
185        UpdatePidTable(info);
186    }
187}
188
189void LogStats::Reset()
190{
191    std::scoped_lock lk(lock);
192    for (auto &t : domainStats) {
193        t.clear();
194    }
195    pidStats.clear();
196    tsBegin = LogTimeStamp(CLOCK_REALTIME);
197    monoBegin = LogTimeStamp(CLOCK_MONOTONIC);
198    for (int i = 0; i < LevelNum; i++) {
199        totalLines[i] = 0;
200        totalLens[i] = 0;
201    }
202}
203
204const LogTypeDomainTable& LogStats::GetDomainTable() const
205{
206    return domainStats;
207}
208
209const PidTable& LogStats::GetPidTable() const
210{
211    return pidStats;
212}
213
214const LogTimeStamp& LogStats::GetBeginTs() const
215{
216    return tsBegin;
217}
218
219const LogTimeStamp& LogStats::GetBeginMono() const
220{
221    return monoBegin;
222}
223
224void LogStats::GetTotalLines(uint32_t (&in_lines)[LevelNum]) const
225{
226    std::copy(totalLines, totalLines + LevelNum, in_lines);
227}
228
229void LogStats::GetTotalLens(uint64_t (&in_lens)[LevelNum]) const
230{
231    std::copy(totalLens, totalLens + LevelNum, in_lens);
232}
233
234bool LogStats::IsEnable() const
235{
236    return enable;
237}
238
239bool LogStats::IsTagEnable() const
240{
241    return tagEnable;
242}
243
244std::unique_lock<std::mutex> LogStats::GetLock()
245{
246    std::unique_lock<std::mutex> lk(lock);
247    return lk;
248}
249
250void LogStats::Print()
251{
252    cout << "Domain Table:" << endl;
253    int i = 0;
254    for (auto &t : domainStats) {
255        i++;
256        if (t.size() == 0) {
257            continue;
258        }
259        cout << "  Log type:" << (i - 1) << endl;
260        for (auto &it : t) {
261            cout << "    Domain: 0x" << std::hex << it.first << std::dec << endl;
262            cout << "      ";
263            it.second.stats.Print();
264            for (auto &itt : it.second.tagStats) {
265                cout << "      Tag: " << itt.first << endl;
266                cout << "        ";
267                itt.second.Print();
268            }
269        }
270    }
271    cout << "Pid Table:" << endl;
272    for (auto &t : pidStats) {
273        cout << "  Proc: " << t.second.name << "(" << t.first << ")" << endl;
274        cout << "    ";
275        t.second.statsAll.Print();
276        int i = 0;
277        for (auto &s : t.second.stats) {
278            i++;
279            if (s.GetTotalLines() == 0) {
280                continue;
281            }
282            cout << "      Log Type: " << (i - 1) << endl;
283            cout << "        ";
284            s.Print();
285        }
286        for (auto &itt : t.second.tagStats) {
287            cout << "      Tag: " << itt.first << endl;
288            cout << "        ";
289            itt.second.Print();
290        }
291    }
292}
293} // namespace HiviewDFX
294} // namespace OHOS