Line data Source code
1 : /*
2 : * If not stated otherwise in this file or this component's LICENSE file the
3 : * following copyright and licenses apply:
4 : *
5 : * Copyright 2026 Sky UK
6 : *
7 : * Licensed under the Apache License, Version 2.0 (the "License");
8 : * you may not use this file except in compliance with the License.
9 : * You may obtain a copy of the License at
10 : *
11 : * http://www.apache.org/licenses/LICENSE-2.0
12 : *
13 : * Unless required by applicable law or agreed to in writing, software
14 : * distributed under the License is distributed on an "AS IS" BASIS,
15 : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 : * See the License for the specific language governing permissions and
17 : * limitations under the License.
18 : */
19 :
20 : #include "MetricsCollector.h"
21 : #include "LogMetricsReporter.h"
22 : #include "RialtoServerLogging.h"
23 : #include <chrono>
24 : #include <cinttypes>
25 : #include <fstream>
26 : #include <string>
27 : #include <sys/times.h>
28 : #include <unistd.h>
29 :
30 : namespace
31 : {
32 : constexpr std::chrono::seconds kMetricsInterval{15};
33 : constexpr std::uint64_t kMinElapsedMs{100};
34 : constexpr unsigned int kResponseTimeoutTimerCount{2};
35 : } // namespace
36 :
37 : namespace firebolt::rialto::server
38 : {
39 7 : std::shared_ptr<IMetricsCollectorFactory> IMetricsCollectorFactory::createFactory()
40 : {
41 7 : std::shared_ptr<IMetricsCollectorFactory> factory;
42 : try
43 : {
44 7 : factory = std::make_shared<MetricsCollectorFactory>();
45 : }
46 0 : catch (const std::exception &e)
47 : {
48 0 : RIALTO_SERVER_LOG_ERROR("Failed to create MetricsCollectorFactory, reason: %s", e.what());
49 : }
50 7 : return factory;
51 : }
52 :
53 0 : std::unique_ptr<IMetricsCollector> MetricsCollectorFactory::create(int clientId,
54 : const std::shared_ptr<IMetricsCollectorClient> &client,
55 : ApplicationState initialApplicationState)
56 : {
57 0 : std::unique_ptr<IMetricsCollector> collector;
58 : try
59 : {
60 0 : auto timerFactory = firebolt::rialto::common::ITimerFactory::getFactory();
61 0 : collector = std::make_unique<MetricsCollector>(clientId, client, timerFactory, initialApplicationState);
62 : }
63 0 : catch (const std::exception &e)
64 : {
65 0 : RIALTO_SERVER_LOG_ERROR("Failed to create MetricsCollector for client %d, reason: %s", clientId, e.what());
66 : }
67 0 : return collector;
68 : }
69 :
70 3 : MetricsCollector::MetricsCollector(int clientId, const std::shared_ptr<IMetricsCollectorClient> &client,
71 : const std::shared_ptr<firebolt::rialto::common::ITimerFactory> &timerFactory,
72 3 : ApplicationState initialApplicationState)
73 6 : : m_clientId{clientId}, m_client{client}, m_currentApplicationState{initialApplicationState},
74 21 : m_reporter{std::make_unique<LogMetricsReporter>()}, m_thresholdConfig{},
75 6 : m_thresholdChecker{m_thresholdConfig, m_reporter.get()}
76 : {
77 3 : if (m_currentApplicationState == ApplicationState::RUNNING)
78 : {
79 : using std::chrono::duration_cast;
80 : using std::chrono::milliseconds;
81 : using std::chrono::steady_clock;
82 : const auto kNowMs{
83 1 : static_cast<std::uint64_t>(duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count())};
84 3 : m_globalAggregator.begin(applicationStateToString(m_currentApplicationState), kNowMs);
85 : }
86 :
87 : m_timer =
88 : timerFactory
89 7 : ->createTimer(kMetricsInterval, [this]() { onTimerFired(); }, firebolt::rialto::common::TimerType::PERIODIC);
90 :
91 : // Request initial baseline sample
92 3 : m_client->requestMetricsSample(m_clientId, m_nextSampleId++, MetricsSampleReason::CONNECTED);
93 : }
94 :
95 6 : MetricsCollector::~MetricsCollector()
96 : {
97 3 : if (m_timer)
98 : {
99 3 : m_timer->cancel();
100 : }
101 6 : }
102 :
103 4 : void MetricsCollector::onTimerFired()
104 : {
105 4 : std::uint64_t sampleId{0};
106 :
107 : {
108 4 : std::lock_guard<std::mutex> lock{m_mutex};
109 4 : if (m_pendingPeriodicSampleId && ++m_pendingPeriodicTimerCount < kResponseTimeoutTimerCount)
110 : {
111 1 : return;
112 : }
113 :
114 3 : if (m_pendingPeriodicSampleId && m_clientResponsive)
115 : {
116 1 : m_clientResponsive = false;
117 1 : RIALTO_SERVER_LOG_WARN("Metrics client %d is not responding to sample requests", m_clientId);
118 : }
119 :
120 3 : sampleId = m_nextSampleId++;
121 3 : m_pendingPeriodicSampleId = sampleId;
122 3 : m_pendingPeriodicTimerCount = 0;
123 4 : }
124 :
125 3 : RIALTO_SERVER_LOG_DEBUG("Requesting periodic metrics sample=%" PRIu64 " from client %d", sampleId, m_clientId);
126 3 : m_client->requestMetricsSample(m_clientId, sampleId, MetricsSampleReason::PERIODIC);
127 : }
128 :
129 3 : void MetricsCollector::processMetrics(const ClientMetricsData &metrics)
130 : {
131 3 : const auto kServerMetrics{getServerMetrics()};
132 :
133 3 : std::optional<PreviousSample> previous;
134 3 : ApplicationState applicationState{ApplicationState::UNKNOWN};
135 3 : bool becameResponsive{false};
136 : {
137 3 : std::lock_guard<std::mutex> lock{m_mutex};
138 3 : previous = m_previousSample;
139 3 : applicationState = m_currentApplicationState;
140 3 : if (metrics.reason == MetricsSampleReason::PERIODIC)
141 : {
142 2 : if (m_pendingPeriodicSampleId && metrics.sampleId == *m_pendingPeriodicSampleId)
143 : {
144 1 : m_pendingPeriodicSampleId.reset();
145 1 : m_pendingPeriodicTimerCount = 0;
146 1 : if (!m_clientResponsive)
147 : {
148 1 : m_clientResponsive = true;
149 1 : becameResponsive = true;
150 : }
151 : }
152 : }
153 3 : }
154 :
155 3 : if (becameResponsive)
156 : {
157 1 : RIALTO_SERVER_LOG_INFO("Metrics client %d is responding again", m_clientId);
158 : }
159 :
160 3 : if (!previous.has_value())
161 : {
162 : // Baseline sample — store and return
163 2 : RIALTO_SERVER_LOG_MIL("Metrics baseline: sample=%" PRIu64 ", reason=%s, app='%s', client_pid=%u, "
164 : "client_cpu_ms=%" PRIu64 ", server_cpu_ms=%" PRIu64 ", "
165 : "client_mem_kb=%" PRIu64 ", server_mem_kb=%" PRIu64 ", "
166 : "cgroup_mem_kb=%" PRIu64 "/%" PRIu64,
167 : metrics.sampleId, sampleReasonToString(metrics.reason), metrics.appName.c_str(),
168 : metrics.processId, metrics.processCpuTimeMs, kServerMetrics.processCpuTimeMs,
169 : metrics.processMemoryKb, kServerMetrics.processMemoryKb,
170 : kServerMetrics.cgroupMemoryUsageKb, kServerMetrics.cgroupMemoryLimitKb);
171 :
172 2 : std::lock_guard<std::mutex> lock{m_mutex};
173 : m_previousSample =
174 2 : PreviousSample{metrics.monotonicTimeMs, metrics.processCpuTimeMs, metrics.processMemoryKb, kServerMetrics};
175 2 : return;
176 : }
177 :
178 1 : const auto &prev{previous.value()};
179 1 : const double kClientCpuPercentage{calculateCpuPercentage(metrics.processCpuTimeMs, prev.clientCpuTimeMs,
180 1 : metrics.monotonicTimeMs, prev.clientMonotonicTimeMs)};
181 : const double kServerCpuPercentage{
182 1 : calculateCpuPercentage(kServerMetrics.processCpuTimeMs, prev.serverMetrics.processCpuTimeMs,
183 1 : kServerMetrics.monotonicTimeMs, prev.serverMetrics.monotonicTimeMs)};
184 : const double kCombinedCpuPercentage{
185 1 : calculateCpuPercentage(metrics.processCpuTimeMs + kServerMetrics.processCpuTimeMs,
186 1 : prev.clientCpuTimeMs + prev.serverMetrics.processCpuTimeMs,
187 1 : kServerMetrics.monotonicTimeMs, prev.serverMetrics.monotonicTimeMs)};
188 :
189 : // Report via pluggable reporter
190 1 : if (m_reporter)
191 : {
192 1 : PeriodicMetricsReport periodicReport;
193 1 : periodicReport.sampleId = metrics.sampleId;
194 1 : periodicReport.monotonicTimeMs = kServerMetrics.monotonicTimeMs;
195 1 : periodicReport.reason = sampleReasonToString(metrics.reason);
196 1 : periodicReport.applicationState = applicationState;
197 1 : periodicReport.appName = metrics.appName;
198 1 : periodicReport.clientPid = metrics.processId;
199 1 : periodicReport.clientCpuPercent = kClientCpuPercentage;
200 1 : periodicReport.serverCpuPercent = kServerCpuPercentage;
201 1 : periodicReport.combinedCpuPercent = kCombinedCpuPercentage;
202 1 : periodicReport.clientCpuTimeMs = metrics.processCpuTimeMs;
203 1 : periodicReport.serverCpuTimeMs = kServerMetrics.processCpuTimeMs;
204 1 : periodicReport.clientMemoryKb = metrics.processMemoryKb;
205 1 : periodicReport.serverMemoryKb = kServerMetrics.processMemoryKb;
206 1 : periodicReport.cgroupMemoryUsageKb = kServerMetrics.cgroupMemoryUsageKb;
207 1 : periodicReport.cgroupMemoryLimitKb = kServerMetrics.cgroupMemoryLimitKb;
208 1 : periodicReport.shmMemoryKb = kServerMetrics.shmMemoryKb;
209 1 : m_reporter->reportPeriodicSample(periodicReport);
210 : }
211 :
212 : // Only feed PERIODIC samples into aggregators — STATE_TRANSITION samples have
213 : // unreliable CPU percentages due to tiny time deltas between rapid samples.
214 1 : if (metrics.reason == MetricsSampleReason::PERIODIC)
215 : {
216 1 : MetricsSample sample;
217 1 : sample.clientCpuPercent = kClientCpuPercentage;
218 1 : sample.serverCpuPercent = kServerCpuPercentage;
219 1 : sample.combinedCpuPercent = kCombinedCpuPercentage;
220 1 : sample.clientMemoryKb = metrics.processMemoryKb;
221 1 : sample.serverMemoryKb = kServerMetrics.processMemoryKb;
222 1 : sample.cgroupMemoryUsageKb = kServerMetrics.cgroupMemoryUsageKb;
223 1 : sample.cgroupMemoryLimitKb = kServerMetrics.cgroupMemoryLimitKb;
224 :
225 : {
226 1 : std::lock_guard<std::mutex> lock{m_mutex};
227 :
228 : // Feed into per-session aggregators
229 3 : for (auto &[unusedContext, sessionState] : m_sessionStates)
230 : {
231 : (void)unusedContext;
232 2 : sessionState.aggregator.addSample(sample);
233 : }
234 :
235 : // Feed into global aggregator
236 1 : if (m_currentApplicationState == ApplicationState::RUNNING)
237 : {
238 1 : m_globalAggregator.addSample(sample);
239 : }
240 : }
241 :
242 : // Check thresholds
243 1 : m_thresholdChecker.checkSample(kClientCpuPercentage, kServerCpuPercentage, kCombinedCpuPercentage,
244 1 : metrics.processMemoryKb, kServerMetrics.processMemoryKb,
245 1 : kServerMetrics.cgroupMemoryUsageKb, kServerMetrics.cgroupMemoryLimitKb);
246 : }
247 :
248 : // Update previous sample
249 : {
250 1 : std::lock_guard<std::mutex> lock{m_mutex};
251 : m_previousSample =
252 1 : PreviousSample{metrics.monotonicTimeMs, metrics.processCpuTimeMs, metrics.processMemoryKb, kServerMetrics};
253 : }
254 : }
255 :
256 3 : void MetricsCollector::notifyPlaybackStateChanged(int sessionId, PlaybackState oldState, PlaybackState newState)
257 : {
258 3 : notifyPlayerStateChanged("media-pipeline=" + std::to_string(sessionId), playbackStateToString(oldState),
259 : playbackStateToString(newState),
260 3 : newState == PlaybackState::STOPPED || newState == PlaybackState::END_OF_STREAM ||
261 : newState == PlaybackState::FAILURE);
262 : }
263 :
264 3 : void MetricsCollector::notifyWebAudioPlayerStateChanged(int handle, WebAudioPlayerState oldState,
265 : WebAudioPlayerState newState)
266 : {
267 3 : notifyPlayerStateChanged("web-audio=" + std::to_string(handle), webAudioPlayerStateToString(oldState),
268 : webAudioPlayerStateToString(newState),
269 3 : newState == WebAudioPlayerState::END_OF_STREAM || newState == WebAudioPlayerState::FAILURE);
270 : }
271 :
272 6 : void MetricsCollector::notifyPlayerStateChanged(const std::string &context, const char *oldState, const char *newState,
273 : bool terminalState)
274 : {
275 6 : RIALTO_SERVER_LOG_MIL("Metrics: PlaybackState changed %s, %s -> %s", context.c_str(), oldState, newState);
276 :
277 : using std::chrono::duration_cast;
278 : using std::chrono::milliseconds;
279 : using std::chrono::steady_clock;
280 : const auto kNowMs{
281 6 : static_cast<std::uint64_t>(duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count())};
282 :
283 6 : std::lock_guard<std::mutex> lock{m_mutex};
284 6 : auto sessionIter{m_sessionStates.find(context)};
285 6 : if (m_sessionStates.end() == sessionIter)
286 : {
287 : // First state notification for this session — create entry
288 2 : SessionMetricsState sessionState;
289 2 : sessionState.currentState = newState;
290 2 : sessionState.aggregator.begin(newState, kNowMs);
291 2 : m_sessionStates.emplace(context, std::move(sessionState));
292 2 : return;
293 : }
294 :
295 4 : auto &sessionState{sessionIter->second};
296 :
297 : // Finalize old state and emit report
298 4 : if (sessionState.aggregator.hasData() && m_reporter)
299 : {
300 2 : auto report{sessionState.aggregator.finalize(kNowMs)};
301 2 : StateTransitionReport transitionReport;
302 2 : transitionReport.context = context;
303 2 : transitionReport.metrics = report;
304 2 : m_reporter->reportStateTransition(transitionReport);
305 : }
306 :
307 4 : if (terminalState)
308 : {
309 : // Terminal state — remove session tracking
310 2 : m_sessionStates.erase(sessionIter);
311 : }
312 : else
313 : {
314 : // Begin accumulating for new state
315 2 : sessionState.currentState = newState;
316 6 : sessionState.aggregator.begin(newState, kNowMs);
317 : }
318 :
319 : // Request immediate sample for clean boundary
320 4 : m_client->requestMetricsSample(m_clientId, m_nextSampleId++, MetricsSampleReason::STATE_TRANSITION);
321 6 : }
322 :
323 2 : void MetricsCollector::notifyApplicationStateChanged(ApplicationState oldState, ApplicationState newState)
324 : {
325 2 : RIALTO_SERVER_LOG_MIL("Metrics: ApplicationState changed %s -> %s", applicationStateToString(oldState),
326 : applicationStateToString(newState));
327 :
328 : using std::chrono::duration_cast;
329 : using std::chrono::milliseconds;
330 : using std::chrono::steady_clock;
331 : const auto kNowMs{
332 2 : static_cast<std::uint64_t>(duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count())};
333 :
334 2 : std::lock_guard<std::mutex> lock{m_mutex};
335 2 : m_currentApplicationState = newState;
336 :
337 2 : if (oldState == ApplicationState::RUNNING && newState != ApplicationState::RUNNING)
338 : {
339 : // Leaving RUNNING — finalize global aggregator
340 1 : if (m_globalAggregator.hasData() && m_reporter)
341 : {
342 1 : auto report{m_globalAggregator.finalize(kNowMs)};
343 1 : StateTransitionReport transitionReport;
344 1 : transitionReport.context = "global";
345 1 : transitionReport.metrics = report;
346 1 : m_reporter->reportStateTransition(transitionReport);
347 : }
348 1 : m_globalAggregator.reset();
349 : }
350 :
351 2 : if (newState == ApplicationState::RUNNING && oldState != ApplicationState::RUNNING)
352 : {
353 : // Entering RUNNING — start fresh global accumulation
354 3 : m_globalAggregator.begin(applicationStateToString(newState), kNowMs);
355 : }
356 :
357 : // Request immediate sample for clean boundary
358 2 : m_client->requestMetricsSample(m_clientId, m_nextSampleId++, MetricsSampleReason::STATE_TRANSITION);
359 : }
360 :
361 3 : MetricsCollector::ProcessMetricsSample MetricsCollector::getServerMetrics() const
362 : {
363 : using std::chrono::duration_cast;
364 : using std::chrono::milliseconds;
365 : using std::chrono::steady_clock;
366 : using std::chrono::system_clock;
367 :
368 3 : struct tms processTimes
369 : {
370 : };
371 3 : const clock_t kCurrentTicks{times(&processTimes)};
372 3 : const int64_t kTicksPerSecond{sysconf(_SC_CLK_TCK)};
373 3 : std::uint64_t processCpuTimeMs{0};
374 3 : if ((static_cast<clock_t>(-1) != kCurrentTicks) && (kTicksPerSecond > 0))
375 : {
376 3 : const auto kProcessTicks{processTimes.tms_utime + processTimes.tms_stime};
377 3 : processCpuTimeMs = static_cast<std::uint64_t>((static_cast<double>(kProcessTicks) * 1000.0) /
378 3 : static_cast<double>(kTicksPerSecond));
379 : }
380 : else
381 : {
382 0 : RIALTO_SERVER_LOG_WARN("Failed to sample server process CPU usage");
383 : }
384 :
385 3 : std::uint64_t processMemoryKb{0};
386 : {
387 3 : std::ifstream status{"/proc/self/status"};
388 3 : std::string line;
389 69 : while (std::getline(status, line))
390 : {
391 69 : if (line.rfind("VmRSS:", 0) == 0)
392 : {
393 3 : if (std::sscanf(line.c_str(), "VmRSS: %" SCNu64, &processMemoryKb) != 1)
394 : {
395 0 : RIALTO_SERVER_LOG_WARN("Failed to parse server process memory usage");
396 : }
397 3 : break;
398 : }
399 : }
400 : }
401 :
402 3 : std::uint64_t cgroupMemoryUsageKb{0};
403 3 : std::uint64_t cgroupMemoryLimitKb{0};
404 : {
405 6 : auto readFileValue = [](const std::string &path) -> std::uint64_t
406 : {
407 6 : std::ifstream file{path};
408 6 : if (!file.is_open())
409 : {
410 0 : return 0;
411 : }
412 6 : std::string content;
413 6 : if (!std::getline(file, content) || content.empty() || content == "max")
414 : {
415 3 : return 0;
416 : }
417 3 : std::uint64_t value{0};
418 3 : if (std::sscanf(content.c_str(), "%" SCNu64, &value) == 1)
419 : {
420 3 : return value;
421 : }
422 0 : return 0;
423 6 : };
424 :
425 : // Resolve the process's cgroup path from /proc/self/cgroup
426 : // cgroup v2 format: "0::<relative-path>"
427 3 : auto getCgroupBasePath = []() -> std::string
428 : {
429 3 : std::ifstream cgroupFile{"/proc/self/cgroup"};
430 3 : if (!cgroupFile.is_open())
431 : {
432 0 : return {};
433 : }
434 3 : std::string line;
435 3 : while (std::getline(cgroupFile, line))
436 : {
437 : // cgroup v2 line starts with "0::"
438 3 : if (line.rfind("0::", 0) == 0)
439 : {
440 3 : std::string relativePath{line.substr(3)};
441 3 : if (!relativePath.empty() && relativePath != "/")
442 : {
443 3 : return "/sys/fs/cgroup" + relativePath;
444 : }
445 0 : return "/sys/fs/cgroup";
446 3 : }
447 : }
448 0 : return {};
449 3 : };
450 :
451 3 : std::uint64_t usageBytes{0};
452 3 : std::uint64_t limitBytes{0};
453 :
454 : // cgroup v2: read from process's own cgroup path
455 3 : std::string cgroupBase{getCgroupBasePath()};
456 3 : if (!cgroupBase.empty())
457 : {
458 3 : usageBytes = readFileValue(cgroupBase + "/memory.current");
459 3 : limitBytes = readFileValue(cgroupBase + "/memory.max");
460 : }
461 :
462 3 : if (usageBytes == 0)
463 : {
464 : // cgroup v1 fallback
465 0 : usageBytes = readFileValue("/sys/fs/cgroup/memory/memory.usage_in_bytes");
466 0 : limitBytes = readFileValue("/sys/fs/cgroup/memory/memory.limit_in_bytes");
467 : }
468 :
469 3 : cgroupMemoryUsageKb = usageBytes / 1024;
470 3 : cgroupMemoryLimitKb = limitBytes / 1024;
471 : }
472 :
473 3 : std::uint64_t shmMemoryKb{0};
474 : {
475 3 : std::ifstream smaps{"/proc/self/smaps_rollup"};
476 3 : std::string sline;
477 21 : while (std::getline(smaps, sline))
478 : {
479 21 : if (sline.rfind("Pss_Shmem:", 0) == 0)
480 : {
481 3 : std::sscanf(sline.c_str(), "Pss_Shmem: %" SCNu64, &shmMemoryKb);
482 3 : break;
483 : }
484 : }
485 : }
486 :
487 3 : return ProcessMetricsSample{static_cast<std::uint64_t>(
488 3 : duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count()),
489 3 : static_cast<std::uint64_t>(
490 3 : duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count()),
491 : processCpuTimeMs,
492 : processMemoryKb,
493 : cgroupMemoryUsageKb,
494 : cgroupMemoryLimitKb,
495 6 : shmMemoryKb};
496 : }
497 :
498 3 : double MetricsCollector::calculateCpuPercentage(std::uint64_t currentCpuTimeMs, std::uint64_t previousCpuTimeMs,
499 : std::uint64_t currentMonotonicTimeMs,
500 : std::uint64_t previousMonotonicTimeMs) const
501 : {
502 3 : if ((currentCpuTimeMs < previousCpuTimeMs) || (currentMonotonicTimeMs <= previousMonotonicTimeMs))
503 : {
504 0 : return 0.0;
505 : }
506 :
507 3 : const auto kElapsedMs{currentMonotonicTimeMs - previousMonotonicTimeMs};
508 3 : if (kElapsedMs < kMinElapsedMs)
509 : {
510 : // Time delta too small for meaningful CPU percentage
511 2 : return 0.0;
512 : }
513 :
514 1 : return (static_cast<double>(currentCpuTimeMs - previousCpuTimeMs) / static_cast<double>(kElapsedMs)) * 100.0;
515 : }
516 :
517 3 : const char *MetricsCollector::sampleReasonToString(MetricsSampleReason reason)
518 : {
519 3 : switch (reason)
520 : {
521 1 : case MetricsSampleReason::CONNECTED:
522 1 : return "CONNECTED";
523 2 : case MetricsSampleReason::PERIODIC:
524 2 : return "PERIODIC";
525 0 : case MetricsSampleReason::STATE_TRANSITION:
526 0 : return "STATE_TRANSITION";
527 0 : case MetricsSampleReason::UNKNOWN:
528 : default:
529 0 : return "UNKNOWN";
530 : }
531 : }
532 :
533 6 : const char *MetricsCollector::playbackStateToString(PlaybackState state)
534 : {
535 6 : switch (state)
536 : {
537 0 : case PlaybackState::IDLE:
538 0 : return "IDLE";
539 2 : case PlaybackState::PLAYING:
540 2 : return "PLAYING";
541 2 : case PlaybackState::PAUSED:
542 2 : return "PAUSED";
543 0 : case PlaybackState::SEEKING:
544 0 : return "SEEKING";
545 0 : case PlaybackState::SEEK_DONE:
546 0 : return "SEEK_DONE";
547 1 : case PlaybackState::STOPPED:
548 1 : return "STOPPED";
549 0 : case PlaybackState::END_OF_STREAM:
550 0 : return "END_OF_STREAM";
551 0 : case PlaybackState::FAILURE:
552 0 : return "FAILURE";
553 1 : case PlaybackState::UNKNOWN:
554 : default:
555 1 : return "UNKNOWN";
556 : }
557 : }
558 :
559 6 : const char *MetricsCollector::webAudioPlayerStateToString(WebAudioPlayerState state)
560 : {
561 6 : switch (state)
562 : {
563 0 : case WebAudioPlayerState::IDLE:
564 0 : return "IDLE";
565 2 : case WebAudioPlayerState::PLAYING:
566 2 : return "PLAYING";
567 2 : case WebAudioPlayerState::PAUSED:
568 2 : return "PAUSED";
569 1 : case WebAudioPlayerState::END_OF_STREAM:
570 1 : return "END_OF_STREAM";
571 0 : case WebAudioPlayerState::FAILURE:
572 0 : return "FAILURE";
573 1 : case WebAudioPlayerState::UNKNOWN:
574 : default:
575 1 : return "UNKNOWN";
576 : }
577 : }
578 :
579 6 : const char *MetricsCollector::applicationStateToString(ApplicationState state)
580 : {
581 6 : switch (state)
582 : {
583 4 : case ApplicationState::RUNNING:
584 4 : return "RUNNING";
585 1 : case ApplicationState::INACTIVE:
586 1 : return "INACTIVE";
587 1 : case ApplicationState::UNKNOWN:
588 : default:
589 1 : return "UNKNOWN";
590 : }
591 : }
592 : } // namespace firebolt::rialto::server
|