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 "PrivateMetricsIpc.h"
21 : #include "IpcClient.h"
22 : #include "RialtoClientLogging.h"
23 : #include <cinttypes>
24 : #include <unistd.h>
25 :
26 : namespace
27 : {
28 4 : const char *sampleReasonToString(::firebolt::rialto::MetricsSampleReason reason)
29 : {
30 4 : switch (reason)
31 : {
32 1 : case firebolt::rialto::METRICS_SAMPLE_REASON_CONNECTED:
33 1 : return "CONNECTED";
34 2 : case firebolt::rialto::METRICS_SAMPLE_REASON_PERIODIC:
35 2 : return "PERIODIC";
36 1 : case firebolt::rialto::METRICS_SAMPLE_REASON_STATE_TRANSITION:
37 1 : return "STATE_TRANSITION";
38 0 : case firebolt::rialto::METRICS_SAMPLE_REASON_UNKNOWN:
39 : default:
40 0 : return "UNKNOWN";
41 : }
42 : }
43 : } // namespace
44 :
45 : namespace firebolt::rialto::client
46 : {
47 1 : std::shared_ptr<IPrivateMetricsIpcFactory> IPrivateMetricsIpcFactory::createFactory()
48 : {
49 1 : return PrivateMetricsIpcFactory::createFactory();
50 : }
51 :
52 1 : std::shared_ptr<PrivateMetricsIpcFactory> PrivateMetricsIpcFactory::createFactory()
53 : {
54 1 : std::shared_ptr<PrivateMetricsIpcFactory> factory;
55 :
56 : try
57 : {
58 1 : factory = std::make_shared<PrivateMetricsIpcFactory>();
59 : }
60 0 : catch (const std::exception &e)
61 : {
62 0 : RIALTO_CLIENT_LOG_ERROR("Failed to create the rialto private metrics ipc factory, reason: %s", e.what());
63 : }
64 :
65 1 : return factory;
66 : }
67 :
68 0 : std::shared_ptr<IPrivateMetricsIpc> PrivateMetricsIpcFactory::createPrivateMetricsIpc(IPrivateMetricsIpcClient *client)
69 : {
70 0 : auto &ipcClient{IIpcClientAccessor::instance().getIpcClient()};
71 0 : return std::make_shared<PrivateMetricsIpc>(client, ipcClient,
72 0 : firebolt::rialto::common::IEventThreadFactory::createFactory());
73 : }
74 :
75 2 : PrivateMetricsIpc::PrivateMetricsIpc(IPrivateMetricsIpcClient *client, IIpcClient &ipcClient,
76 2 : const std::shared_ptr<common::IEventThreadFactory> &eventThreadFactory)
77 2 : : IpcModule(ipcClient), m_privateMetricsIpcClient{client},
78 6 : m_eventThread(eventThreadFactory->createEventThread("rialto-metrics-events"))
79 : {
80 2 : RIALTO_CLIENT_LOG_MIL("Initialising private metrics IPC, pid=%d", getpid());
81 2 : if (!attachChannel())
82 : {
83 0 : throw std::runtime_error("Failed attach to the ipc channel");
84 : }
85 2 : if (!notifyClientReady())
86 : {
87 0 : throw std::runtime_error("Failed to notify private metrics readiness");
88 : }
89 2 : }
90 :
91 4 : PrivateMetricsIpc::~PrivateMetricsIpc()
92 : {
93 2 : RIALTO_CLIENT_LOG_MIL("Terminating private metrics IPC, pid=%d", getpid());
94 2 : detachChannel();
95 2 : m_eventThread.reset();
96 4 : }
97 :
98 2 : bool PrivateMetricsIpc::reportClientMetrics(std::uint64_t sampleId, std::uint32_t reason, const std::string &appName,
99 : std::uint32_t processId, std::uint64_t monotonicTimeMs,
100 : std::uint64_t epochTimeMs, std::uint64_t processCpuTimeMs,
101 : std::uint64_t processMemoryKb)
102 : {
103 2 : if (!reattachChannelIfRequired())
104 : {
105 0 : RIALTO_CLIENT_LOG_ERROR("Reattachment of the ipc channel failed, ipc disconnected");
106 0 : return false;
107 : }
108 :
109 2 : firebolt::rialto::ReportClientMetricsRequest request;
110 2 : auto metrics{request.mutable_metrics()};
111 2 : metrics->set_sample_id(sampleId);
112 2 : metrics->set_reason(static_cast<firebolt::rialto::MetricsSampleReason>(reason));
113 : metrics->set_app_name(appName);
114 2 : metrics->set_process_id(processId);
115 2 : metrics->set_monotonic_time_ms(monotonicTimeMs);
116 2 : metrics->set_epoch_time_ms(epochTimeMs);
117 2 : metrics->set_process_cpu_time_ms(processCpuTimeMs);
118 2 : metrics->set_process_memory_kb(processMemoryKb);
119 :
120 2 : RIALTO_CLIENT_LOG_DEBUG("Reporting metrics sample=%" PRIu64 ", reason=%s, app='%s', pid=%u, cpu_ms=%" PRIu64
121 : ", mem_kb=%" PRIu64,
122 : sampleId, sampleReasonToString(static_cast<firebolt::rialto::MetricsSampleReason>(reason)),
123 : appName.c_str(), processId, processCpuTimeMs, processMemoryKb);
124 :
125 2 : firebolt::rialto::ReportClientMetricsResponse response;
126 2 : auto ipcController = m_ipc.createRpcController();
127 2 : auto blockingClosure = m_ipc.createBlockingClosure();
128 2 : m_privateMetricsStub->reportClientMetrics(ipcController.get(), &request, &response, blockingClosure.get());
129 :
130 2 : blockingClosure->wait();
131 :
132 2 : if (ipcController->Failed())
133 : {
134 1 : RIALTO_CLIENT_LOG_DEBUG("Failed to report client metrics due to '%s'", ipcController->ErrorText().c_str());
135 1 : return false;
136 : }
137 :
138 1 : RIALTO_CLIENT_LOG_DEBUG("Reported metrics sample=%" PRIu64 ", reason=%s", sampleId,
139 : sampleReasonToString(static_cast<firebolt::rialto::MetricsSampleReason>(reason)));
140 :
141 1 : return true;
142 2 : }
143 :
144 2 : bool PrivateMetricsIpc::notifyClientReady()
145 : {
146 2 : if (!reattachChannelIfRequired())
147 : {
148 0 : RIALTO_CLIENT_LOG_ERROR("Reattachment of the ipc channel failed, ipc disconnected");
149 0 : return false;
150 : }
151 :
152 2 : RIALTO_CLIENT_LOG_MIL("Notifying server that private metrics IPC is ready, pid=%d", getpid());
153 :
154 2 : firebolt::rialto::NotifyClientReadyRequest request;
155 2 : firebolt::rialto::NotifyClientReadyResponse response;
156 2 : auto ipcController = m_ipc.createRpcController();
157 2 : auto blockingClosure = m_ipc.createBlockingClosure();
158 2 : m_privateMetricsStub->notifyClientReady(ipcController.get(), &request, &response, blockingClosure.get());
159 :
160 2 : blockingClosure->wait();
161 :
162 2 : if (ipcController->Failed())
163 : {
164 0 : RIALTO_CLIENT_LOG_ERROR("failed to notify private metrics readiness due to '%s'",
165 : ipcController->ErrorText().c_str());
166 0 : return false;
167 : }
168 :
169 2 : RIALTO_CLIENT_LOG_MIL("Server acknowledged private metrics IPC readiness, pid=%d", getpid());
170 :
171 2 : return true;
172 : }
173 :
174 2 : bool PrivateMetricsIpc::createRpcStubs(const std::shared_ptr<ipc::IChannel> &ipcChannel)
175 : {
176 2 : m_privateMetricsStub = std::make_shared<::firebolt::rialto::PrivateMetricsModule_Stub>(ipcChannel.get());
177 2 : return static_cast<bool>(m_privateMetricsStub);
178 : }
179 :
180 2 : bool PrivateMetricsIpc::subscribeToEvents(const std::shared_ptr<ipc::IChannel> &ipcChannel)
181 : {
182 2 : if (!ipcChannel)
183 : {
184 0 : return false;
185 : }
186 :
187 4 : int eventTag = ipcChannel->subscribe<firebolt::rialto::MetricsSampleRequestEvent>(
188 2 : [this](const std::shared_ptr<firebolt::rialto::MetricsSampleRequestEvent> &event)
189 3 : { m_eventThread->add(&PrivateMetricsIpc::onMetricsSampleRequested, this, event); });
190 2 : if (eventTag < 0)
191 : {
192 0 : return false;
193 : }
194 2 : m_eventTags.push_back(eventTag);
195 :
196 2 : RIALTO_CLIENT_LOG_MIL("Subscribed to private metrics sample requests, pid=%d, event_tag=%d", getpid(), eventTag);
197 :
198 2 : return true;
199 : }
200 :
201 1 : void PrivateMetricsIpc::onMetricsSampleRequested(const std::shared_ptr<firebolt::rialto::MetricsSampleRequestEvent> &event)
202 : {
203 1 : if (!m_privateMetricsIpcClient)
204 : {
205 0 : RIALTO_CLIENT_LOG_WARN("No private metrics client registered");
206 0 : return;
207 : }
208 1 : RIALTO_CLIENT_LOG_DEBUG("Received metrics sample request sample=%" PRIu64 ", reason=%s, pid=%d", event->sample_id(),
209 : sampleReasonToString(event->reason()), getpid());
210 1 : m_privateMetricsIpcClient->reportClientMetrics(event->sample_id(), event->reason());
211 : }
212 : } // namespace firebolt::rialto::client
|