• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2024 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 
16 #include "libpandabase/utils/time.h"
17 #include "runtime/coroutines/coroutine_stats.h"
18 #include <chrono>
19 
20 namespace ark {
21 
22 // clang-tidy cannot detect that we are going to initialize intervalStarts_ via fill()
23 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
CoroutineStatsBase()24 CoroutineStatsBase::CoroutineStatsBase()
25 {
26     intervalStarts_.fill(INVALID_TIME_METRIC_VAL);
27 }
28 
StartInterval(CoroutineTimeStats id)29 void CoroutineStatsBase::StartInterval(CoroutineTimeStats id)
30 {
31     if (!IsEnabled()) {
32         return;
33     }
34     LOG(DEBUG, COROUTINES) << "CoroutineStatsBase::StartInterval: " << ToIndex(id) << " Stats: " << this;
35     ASSERT(!IsInInterval(id));
36     TimeMetricVal start = time::GetCurrentTimeInNanos();
37     intervalStarts_[ToIndex(id)] = start;
38 }
39 
FinishInterval(CoroutineTimeStats id)40 void CoroutineStatsBase::FinishInterval(CoroutineTimeStats id)
41 {
42     if (!IsEnabled()) {
43         return;
44     }
45     LOG(DEBUG, COROUTINES) << "CoroutineStatsBase::FinishInterval " << ToIndex(id) << " Stats: " << this;
46     ASSERT(IsInInterval(id));
47     TimeMetricVal end = time::GetCurrentTimeInNanos();
48     TimeMetricVal delay = end - intervalStarts_[ToIndex(id)];
49     intervalStarts_[ToIndex(id)] = INVALID_TIME_METRIC_VAL;
50     auto &metric = timeStats_[ToIndex(id)];
51     metric.AddValue(delay);
52 }
53 
IsInInterval(CoroutineTimeStats id) const54 bool CoroutineStatsBase::IsInInterval(CoroutineTimeStats id) const
55 {
56     return (intervalStarts_[ToIndex(id)] != INVALID_TIME_METRIC_VAL);
57 }
58 
RecordTimeStatsValue(CoroutineTimeStats id,TimeMetricVal value)59 void CoroutineStatsBase::RecordTimeStatsValue(CoroutineTimeStats id, TimeMetricVal value)
60 {
61     auto &metric = timeStats_[ToIndex(id)];
62     metric.AddValue(value);
63 }
64 
RecordMemStatsValue(CoroutineMemStats id,MemMetricVal value)65 void CoroutineStatsBase::RecordMemStatsValue(CoroutineMemStats id, MemMetricVal value)
66 {
67     auto &metric = memStats_[ToIndex(id)];
68     metric.AddValue(value);
69 }
70 
GetTimeStatValue(CoroutineTimeStats id) const71 const SimpleHistogram<CoroutineStatsBase::TimeMetricVal> &CoroutineStatsBase::GetTimeStatValue(
72     CoroutineTimeStats id) const
73 {
74     return timeStats_[ToIndex(id)];
75 }
76 
GetTimeStatString(CoroutineTimeStats id) const77 PandaString CoroutineStatsBase::GetTimeStatString(CoroutineTimeStats id) const
78 {
79     auto &metric = timeStats_[ToIndex(id)];
80     if (metric.GetCount() > 0) {
81         return metric.GetGeneralStatistic() + " Count: " + ark::ToPandaString(metric.GetCount());
82     }
83     return "<no data>";
84 }
85 
GetMemStatValue(CoroutineMemStats id) const86 const SimpleHistogram<CoroutineStatsBase::MemMetricVal> &CoroutineStatsBase::GetMemStatValue(CoroutineMemStats id) const
87 {
88     return memStats_[ToIndex(id)];
89 }
90 
GetMemStatString(CoroutineMemStats id) const91 PandaString CoroutineStatsBase::GetMemStatString(CoroutineMemStats id) const
92 {
93     auto &metric = memStats_[ToIndex(id)];
94     if (metric.GetCount() > 0) {
95         return metric.GetGeneralStatistic();
96     }
97     return "<no data>";
98 }
99 
GetTimeStatsDescription()100 CoroutineStatsBase::TimeStatsDescriptionMap &CoroutineStatsBase::GetTimeStatsDescription()
101 {
102     static TimeStatsDescriptionMap timeMetricsDescription = {
103         {CoroutineTimeStats::INIT, {"INIT", {AggregateType::SUM}, false}},
104         {CoroutineTimeStats::LAUNCH, {"LAUNCH", {AggregateType::AVG, AggregateType::MAX, AggregateType::COUNT}, true}},
105         {CoroutineTimeStats::SCH_ALL, {"SCH_ALL", {AggregateType::SUM}, true}},
106         {CoroutineTimeStats::CTX_SWITCH,
107          {"CTX_SWITCH", {AggregateType::AVG, AggregateType::MAX, AggregateType::COUNT}, true}},
108         {CoroutineTimeStats::LOCKS, {"LOCKS", {AggregateType::SUM}, true}},
109     };
110     ASSERT(timeMetricsDescription.size() == COROUTINE_STATS_ENUM_SIZE<CoroutineTimeStats>);
111     return timeMetricsDescription;
112 }
113 
operator <<(std::ostream & os,CoroutineStatsBase::AggregateType id)114 std::ostream &operator<<(std::ostream &os, CoroutineStatsBase::AggregateType id)
115 {
116     switch (id) {
117         case CoroutineStatsBase::AggregateType::MAX:
118             os << "MAX";
119             break;
120         case CoroutineStatsBase::AggregateType::MIN:
121             os << "MIN";
122             break;
123         case CoroutineStatsBase::AggregateType::AVG:
124             os << "AVG";
125             break;
126         case CoroutineStatsBase::AggregateType::COUNT:
127             os << "COUNT";
128             break;
129         case CoroutineStatsBase::AggregateType::SUM:
130             os << "SUM";
131             break;
132         default:
133             os << "UNKNOWN";
134             break;
135     }
136     return os;
137 }
138 
GetBriefStatistics() const139 PandaString CoroutineStats::GetBriefStatistics() const
140 {
141     PandaStringStream ss;
142     constexpr size_t NS_IN_MICROSECOND = 1000ULL;
143     ss << "[General stats]\n";
144     ss << "INIT [us]: " << (GetTimeStatValue(CoroutineTimeStats::INIT).GetSum() / NS_IN_MICROSECOND) << "\n";
145     return ss.str();
146 }
147 
GenerateTimeStatsDataArray(const PandaVector<CoroutineWorkerStats * > & workerStats)148 CoroutineStats::TimeStatsDataArray CoroutineStats::GenerateTimeStatsDataArray(
149     const PandaVector<CoroutineWorkerStats *> &workerStats)
150 {
151     TimeStatsDataArray timeStats;
152     for (auto &s : timeStats) {
153         s = {
154             0,                                    /*MAX*/
155             std::numeric_limits<uint64_t>::max(), /*MIN*/
156             0,                                    /*AVG*/
157             0,                                    /*COUNT*/
158             0                                     /*SUM*/
159         };
160     }
161     using MetricAggregates = TimeStatsDataArray::value_type;
162     auto initMetric = [](CoroutineWorkerStats *ws, CoroutineTimeStats id, MetricAggregates &metricData) {
163         for (size_t aggrId = 0; aggrId < metricData.size(); ++aggrId) {
164             switch (static_cast<AggregateType>(aggrId)) {
165                 case AggregateType::MAX:
166                     metricData[aggrId] = std::max(metricData[aggrId], ws->GetTimeStatValue(id).GetMax());
167                     break;
168                 case AggregateType::MIN:
169                     metricData[aggrId] = std::min(metricData[aggrId], ws->GetTimeStatValue(id).GetMin());
170                     break;
171                 case AggregateType::AVG:
172                     // should be re-calculated later because of a float data type
173                     break;
174                 case AggregateType::COUNT:
175                     metricData[aggrId] += ws->GetTimeStatValue(id).GetCount();
176                     break;
177                 case AggregateType::SUM:
178                 default:
179                     metricData[aggrId] += ws->GetTimeStatValue(id).GetSum();
180                     break;
181             }
182         }
183     };
184     for (auto *ws : workerStats) {
185         for (auto &[id, descr] : GetTimeStatsDescription()) {
186             auto &metricData = timeStats[ToIndex(id)];
187             initMetric(ws, id, metricData);
188         }
189     }
190     return timeStats;
191 }
192 
GetFullStatistics(PandaVector<CoroutineWorkerStats * > && workerStats) const193 PandaString CoroutineStats::GetFullStatistics(PandaVector<CoroutineWorkerStats *> &&workerStats) const
194 {
195     PandaStringStream ss;
196     // common stats
197     ss << GetBriefStatistics();
198     // per worker stats
199     ss << "[Per worker stats]\n";
200     for (auto *ws : workerStats) {
201         ss << "Worker: " << ws->GetName() << "\n";
202         for (auto &[id, descr] : GetTimeStatsDescription()) {
203             if (ws->GetTimeStatValue(id).GetCount() > 0) {
204                 ss << "\t" << descr.prettyName << " [ns]: " << ws->GetTimeStatString(id) << "\n";
205             }
206         }
207     }
208     // aggregate data for all workers
209     ss << "Summary: \n";
210     TimeStatsDataArray timeStats = GenerateTimeStatsDataArray(workerStats);
211     for (auto &[id, descr] : GetTimeStatsDescription()) {
212         auto &metricData = timeStats[ToIndex(id)];
213         if ((!descr.perWorker) || (metricData[ToIndex(AggregateType::COUNT)] == 0)) {
214             continue;
215         }
216         ss << "\t" << descr.prettyName << " [ns]: ";
217         for (size_t aggrId = 0; aggrId < metricData.size(); ++aggrId) {
218             ss << static_cast<AggregateType>(aggrId) << "(";
219             if (aggrId == ToIndex(AggregateType::AVG)) {
220                 ss << static_cast<double>(metricData[ToIndex(AggregateType::SUM)]) /
221                           metricData[ToIndex(AggregateType::COUNT)];
222             } else {
223                 ss << metricData[ToIndex(aggrId)];
224             }
225             ss << ") ";
226         }
227         ss << "\n";
228     }
229     return ss.str();
230 }
231 
232 }  // namespace ark
233