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 "PrivateMetricsModuleService.h"
21 : #include "RialtoServerLogging.h"
22 : #include <IIpcController.h>
23 : #include <cinttypes>
24 :
25 : namespace firebolt::rialto::server::ipc
26 : {
27 1 : std::shared_ptr<IPrivateMetricsModuleServiceFactory> IPrivateMetricsModuleServiceFactory::createFactory()
28 : {
29 1 : std::shared_ptr<IPrivateMetricsModuleServiceFactory> factory;
30 :
31 : try
32 : {
33 1 : factory = std::make_shared<PrivateMetricsModuleServiceFactory>();
34 : }
35 0 : catch (const std::exception &e)
36 : {
37 0 : RIALTO_SERVER_LOG_ERROR("Failed to create the rialto private metrics module service factory, reason: %s",
38 : e.what());
39 : }
40 :
41 1 : return factory;
42 : }
43 :
44 : std::shared_ptr<IPrivateMetricsModuleService>
45 1 : PrivateMetricsModuleServiceFactory::create(service::IPrivateMetricsService &metricsService) const
46 : {
47 1 : std::shared_ptr<IPrivateMetricsModuleService> privateMetricsModule;
48 :
49 : try
50 : {
51 1 : privateMetricsModule = std::make_shared<PrivateMetricsModuleService>(metricsService);
52 : }
53 0 : catch (const std::exception &e)
54 : {
55 0 : RIALTO_SERVER_LOG_ERROR("Failed to create the rialto private metrics module service, reason: %s", e.what());
56 : }
57 :
58 1 : return privateMetricsModule;
59 : }
60 :
61 7 : PrivateMetricsModuleService::PrivateMetricsModuleService(service::IPrivateMetricsService &metricsService)
62 7 : : m_metricsService{metricsService}
63 : {
64 : }
65 :
66 7 : PrivateMetricsModuleService::~PrivateMetricsModuleService() = default;
67 :
68 1 : void PrivateMetricsModuleService::clientConnected(const std::shared_ptr<::firebolt::rialto::ipc::IClient> &ipcClient)
69 : {
70 1 : RIALTO_SERVER_LOG_INFO("Client connected to private metrics module");
71 : {
72 1 : std::lock_guard<std::mutex> lock{m_mutex};
73 : // Don't assign a clientId yet — wait for notifyClientReady
74 : }
75 1 : ipcClient->exportService(shared_from_this());
76 : }
77 :
78 2 : void PrivateMetricsModuleService::clientDisconnected(const std::shared_ptr<::firebolt::rialto::ipc::IClient> &ipcClient)
79 : {
80 2 : RIALTO_SERVER_LOG_INFO("Client disconnected from private metrics module");
81 2 : int clientId{0};
82 : {
83 2 : std::lock_guard<std::mutex> lock{m_mutex};
84 2 : auto iter = m_clientIds.find(ipcClient);
85 2 : if (iter != m_clientIds.end())
86 : {
87 2 : clientId = iter->second;
88 2 : m_clientIds.erase(iter);
89 2 : m_ipcClients.erase(clientId);
90 : }
91 : }
92 2 : if (clientId != 0)
93 : {
94 2 : m_metricsService.clientDisconnected(clientId);
95 : }
96 : }
97 :
98 6 : void PrivateMetricsModuleService::notifyClientReady(::google::protobuf::RpcController *controller,
99 : const ::firebolt::rialto::NotifyClientReadyRequest *request,
100 : ::firebolt::rialto::NotifyClientReadyResponse *response,
101 : ::google::protobuf::Closure *done)
102 : {
103 6 : RIALTO_SERVER_LOG_DEBUG("entry:");
104 6 : auto ipcController = dynamic_cast<firebolt::rialto::ipc::IController *>(controller);
105 6 : if (!ipcController)
106 : {
107 1 : RIALTO_SERVER_LOG_ERROR("ipc library provided incompatible controller object");
108 2 : controller->SetFailed("ipc library provided incompatible controller object");
109 1 : done->Run();
110 2 : return;
111 : }
112 :
113 5 : auto ipcClient{ipcController->getClient()};
114 5 : int clientId{0};
115 5 : bool isNewClient{false};
116 : {
117 5 : std::lock_guard<std::mutex> lock{m_mutex};
118 5 : auto clientIdIter{m_clientIds.find(ipcClient)};
119 5 : if (clientIdIter != m_clientIds.end())
120 : {
121 1 : clientId = clientIdIter->second;
122 : }
123 : else
124 : {
125 4 : clientId = m_nextClientId.fetch_add(1);
126 4 : m_clientIds.emplace(ipcClient, clientId);
127 4 : m_ipcClients.emplace(clientId, ipcClient);
128 4 : isNewClient = true;
129 : }
130 5 : }
131 :
132 5 : if (!isNewClient)
133 : {
134 : // A retried ready notification is redundant: keep the existing collector and client ID.
135 1 : RIALTO_SERVER_LOG_INFO("Client already ready for private metrics samples with clientId=%d", clientId);
136 1 : done->Run();
137 1 : return;
138 : }
139 :
140 4 : RIALTO_SERVER_LOG_MIL("Client ready for private metrics samples, assigned clientId=%d", clientId);
141 4 : done->Run();
142 :
143 : // Create a shared_ptr to this as IMetricsCollectorClient, aliasing with shared_from_this()
144 : // so the IPC layer stays alive as long as the MetricsCollector holds a reference.
145 4 : auto self = shared_from_this();
146 : std::shared_ptr<firebolt::rialto::server::IMetricsCollectorClient>
147 4 : clientInterface(self, static_cast<firebolt::rialto::server::IMetricsCollectorClient *>(this));
148 4 : m_metricsService.clientReady(clientId, clientInterface);
149 5 : }
150 :
151 7 : void PrivateMetricsModuleService::reportClientMetrics(::google::protobuf::RpcController *controller,
152 : const ::firebolt::rialto::ReportClientMetricsRequest *request,
153 : ::firebolt::rialto::ReportClientMetricsResponse *response,
154 : ::google::protobuf::Closure *done)
155 : {
156 7 : RIALTO_SERVER_LOG_DEBUG("entry:");
157 7 : auto ipcController = dynamic_cast<firebolt::rialto::ipc::IController *>(controller);
158 7 : if (!ipcController)
159 : {
160 1 : RIALTO_SERVER_LOG_ERROR("ipc library provided incompatible controller object");
161 2 : controller->SetFailed("ipc library provided incompatible controller object");
162 1 : done->Run();
163 3 : return;
164 : }
165 6 : if (!request->has_metrics())
166 : {
167 1 : RIALTO_SERVER_LOG_ERROR("reportClientMetrics request missing metrics");
168 2 : controller->SetFailed("Missing metrics");
169 1 : done->Run();
170 1 : return;
171 : }
172 :
173 5 : auto ipcClient{ipcController->getClient()};
174 5 : int clientId{0};
175 : {
176 5 : std::lock_guard<std::mutex> lock{m_mutex};
177 5 : auto iter = m_clientIds.find(ipcClient);
178 5 : if (iter != m_clientIds.end())
179 : {
180 4 : clientId = iter->second;
181 : }
182 5 : }
183 :
184 5 : if (clientId == 0)
185 : {
186 1 : RIALTO_SERVER_LOG_WARN("reportClientMetrics from unknown client");
187 1 : done->Run();
188 1 : return;
189 : }
190 :
191 4 : const auto &protoMetrics{request->metrics()};
192 4 : firebolt::rialto::server::ClientMetricsData metrics;
193 4 : metrics.sampleId = protoMetrics.sample_id();
194 4 : metrics.appName = protoMetrics.app_name();
195 4 : metrics.processId = protoMetrics.process_id();
196 4 : metrics.monotonicTimeMs = protoMetrics.monotonic_time_ms();
197 4 : metrics.epochTimeMs = protoMetrics.epoch_time_ms();
198 4 : metrics.processCpuTimeMs = protoMetrics.process_cpu_time_ms();
199 4 : metrics.processMemoryKb = protoMetrics.process_memory_kb();
200 :
201 : // Convert proto reason to our enum
202 4 : switch (protoMetrics.reason())
203 : {
204 1 : case firebolt::rialto::METRICS_SAMPLE_REASON_CONNECTED:
205 1 : metrics.reason = firebolt::rialto::server::MetricsSampleReason::CONNECTED;
206 1 : break;
207 1 : case firebolt::rialto::METRICS_SAMPLE_REASON_PERIODIC:
208 1 : metrics.reason = firebolt::rialto::server::MetricsSampleReason::PERIODIC;
209 1 : break;
210 1 : case firebolt::rialto::METRICS_SAMPLE_REASON_STATE_TRANSITION:
211 1 : metrics.reason = firebolt::rialto::server::MetricsSampleReason::STATE_TRANSITION;
212 1 : break;
213 1 : default:
214 1 : metrics.reason = firebolt::rialto::server::MetricsSampleReason::UNKNOWN;
215 1 : break;
216 : }
217 :
218 4 : done->Run();
219 :
220 4 : m_metricsService.reportMetrics(clientId, metrics);
221 5 : }
222 :
223 1 : void PrivateMetricsModuleService::notifyApplicationStateChanged(ApplicationState newState)
224 : {
225 1 : m_metricsService.notifyApplicationStateChanged(newState);
226 : }
227 :
228 6 : void PrivateMetricsModuleService::requestMetricsSample(int clientId, std::uint64_t sampleId,
229 : firebolt::rialto::server::MetricsSampleReason reason)
230 : {
231 6 : std::shared_ptr<::firebolt::rialto::ipc::IClient> ipcClient;
232 : {
233 6 : std::lock_guard<std::mutex> lock{m_mutex};
234 6 : auto iter = m_ipcClients.find(clientId);
235 6 : if (iter == m_ipcClients.end())
236 : {
237 1 : return;
238 : }
239 5 : ipcClient = iter->second;
240 6 : }
241 :
242 5 : if (!ipcClient || !ipcClient->isConnected())
243 : {
244 1 : return;
245 : }
246 :
247 4 : auto event{std::make_shared<firebolt::rialto::MetricsSampleRequestEvent>()};
248 4 : event->set_sample_id(sampleId);
249 :
250 : // Convert our enum to proto enum
251 4 : switch (reason)
252 : {
253 1 : case firebolt::rialto::server::MetricsSampleReason::CONNECTED:
254 1 : event->set_reason(firebolt::rialto::METRICS_SAMPLE_REASON_CONNECTED);
255 1 : break;
256 1 : case firebolt::rialto::server::MetricsSampleReason::PERIODIC:
257 1 : event->set_reason(firebolt::rialto::METRICS_SAMPLE_REASON_PERIODIC);
258 1 : break;
259 1 : case firebolt::rialto::server::MetricsSampleReason::STATE_TRANSITION:
260 1 : event->set_reason(firebolt::rialto::METRICS_SAMPLE_REASON_STATE_TRANSITION);
261 1 : break;
262 1 : default:
263 1 : event->set_reason(firebolt::rialto::METRICS_SAMPLE_REASON_UNKNOWN);
264 1 : break;
265 : }
266 :
267 4 : RIALTO_SERVER_LOG_DEBUG("Requesting metrics sample=%" PRIu64 " from client %d", sampleId, clientId);
268 :
269 4 : if (!ipcClient->sendEvent(event))
270 : {
271 1 : RIALTO_SERVER_LOG_DEBUG("Failed to request client metrics sample=%" PRIu64 " from client %d", sampleId, clientId);
272 : }
273 6 : }
274 : } // namespace firebolt::rialto::server::ipc
|