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