LCOV - code coverage report
Current view: top level - media/client/main/source - ClientController.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 91.1 % 169 154
Test Date: 2026-09-11 17:36:12 Functions: 100.0 % 19 19

            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 2023 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 "ClientController.h"
      21              : #include "RialtoClientLogging.h"
      22              : #include "SharedMemoryHandle.h"
      23              : #include <algorithm>
      24              : #include <chrono>
      25              : #include <cinttypes>
      26              : #include <cstdint>
      27              : #include <cstdio>
      28              : #include <cstring>
      29              : #include <fstream>
      30              : #include <sys/mman.h>
      31              : #include <sys/times.h>
      32              : #include <sys/un.h>
      33              : #include <unistd.h>
      34              : #include <utility>
      35              : 
      36              : namespace
      37              : {
      38              : // The following error would be reported if a client is deleted
      39              : // before unregisterClient() was called. Calling unregisterClient can
      40              : // be automated via a proxy class (like the class MediaPipelineProxy)
      41              : const std::string kClientPointerNotLocked{"A client could not be locked"};
      42              : }; // namespace
      43              : 
      44              : namespace firebolt::rialto::client
      45              : {
      46            1 : IClientControllerAccessor &IClientControllerAccessor::instance()
      47              : {
      48            1 :     static ClientControllerAccessor factory;
      49            1 :     return factory;
      50              : }
      51              : 
      52            1 : IClientController &ClientControllerAccessor::getClientController() const
      53              : {
      54            2 :     static ClientController ClientController{IControlIpcFactory::createFactory(),
      55            3 :                                              IPrivateMetricsIpcFactory::createFactory()};
      56            0 :     return ClientController;
      57              : }
      58              : 
      59           22 : ClientController::ClientController(const std::shared_ptr<IControlIpcFactory> &ControlIpcFactory,
      60           22 :                                    const std::shared_ptr<IPrivateMetricsIpcFactory> &privateMetricsIpcFactory)
      61           22 :     : m_currentState{ApplicationState::UNKNOWN}, m_registrationRequired{true}
      62              : {
      63           22 :     RIALTO_CLIENT_LOG_DEBUG("entry:");
      64              : 
      65           22 :     const char kSrcRev[] = SRCREV;
      66           22 :     const char kTags[] = TAGS;
      67              : 
      68              :     if (std::strlen(kSrcRev) > 0)
      69              :     {
      70              :         if (std::strlen(kTags) > 0)
      71              :         {
      72              :             RIALTO_CLIENT_LOG_MIL("Release Tag(s): %s (Commit ID: %s)", kTags, kSrcRev);
      73              :         }
      74              :         else
      75              :         {
      76           22 :             RIALTO_CLIENT_LOG_MIL("Release Tag(s): No Release Tags! (Commit ID: %s)", kSrcRev);
      77              :         }
      78              :     }
      79              :     else
      80              :     {
      81              :         RIALTO_CLIENT_LOG_WARN("Failed to get git commit ID!");
      82              :     }
      83              : 
      84           22 :     m_controlIpc = ControlIpcFactory->createControlIpc(this);
      85           21 :     if (nullptr == m_controlIpc)
      86              :     {
      87            1 :         throw std::runtime_error("Failed to create the ControlIpc object");
      88              :     }
      89              : 
      90           20 :     m_privateMetricsIpc = privateMetricsIpcFactory->createPrivateMetricsIpc(this);
      91           20 :     if (nullptr == m_privateMetricsIpc)
      92              :     {
      93            1 :         throw std::runtime_error("Failed to create the PrivateMetricsIpc object");
      94              :     }
      95           40 : }
      96              : 
      97           38 : ClientController::~ClientController()
      98              : {
      99           19 :     RIALTO_CLIENT_LOG_DEBUG("entry:");
     100              : 
     101           19 :     termSharedMemory();
     102           38 : }
     103              : 
     104           27 : std::shared_ptr<ISharedMemoryHandle> ClientController::getSharedMemoryHandle()
     105              : {
     106           27 :     std::lock_guard<std::mutex> lock{m_mutex};
     107           54 :     return m_shmHandle;
     108           27 : }
     109              : 
     110           13 : bool ClientController::registerClient(std::weak_ptr<IControlClient> client, ApplicationState &appState)
     111              : {
     112           13 :     std::shared_ptr<IControlClient> clientLocked = client.lock();
     113           13 :     if (!clientLocked)
     114              :     {
     115            1 :         RIALTO_CLIENT_LOG_ERROR("Client ptr is null");
     116            1 :         return false;
     117              :     }
     118              : 
     119           12 :     std::lock_guard<std::mutex> lock{m_mutex};
     120           12 :     if (m_registrationRequired)
     121              :     {
     122            9 :         if (!m_controlIpc->registerClient())
     123              :         {
     124            1 :             RIALTO_CLIENT_LOG_ERROR("Failed to register client");
     125            1 :             return false;
     126              :         }
     127              :     }
     128           11 :     m_registrationRequired = false;
     129              : 
     130           11 :     bool alreadyRegistered{std::find_if(m_clients.begin(), m_clients.end(),
     131            4 :                                         [&](auto &i)
     132              :                                         {
     133            4 :                                             std::shared_ptr<IControlClient> iLocked = i.lock();
     134            4 :                                             return (iLocked == clientLocked);
     135           26 :                                         }) != m_clients.end()};
     136           11 :     if (!alreadyRegistered)
     137            7 :         m_clients.push_back(client);
     138           11 :     appState = m_currentState;
     139              : 
     140           11 :     return true;
     141           13 : }
     142              : 
     143            1 : bool ClientController::unregisterClient(std::weak_ptr<IControlClient> client)
     144              : {
     145            1 :     std::shared_ptr<IControlClient> clientLocked = client.lock();
     146            1 :     if (!clientLocked)
     147              :     {
     148            0 :         RIALTO_CLIENT_LOG_ERROR("Client ptr is null");
     149            0 :         return false;
     150              :     }
     151              : 
     152            1 :     std::lock_guard<std::mutex> lock{m_mutex};
     153              : 
     154            1 :     bool found{false};
     155            1 :     for (auto i = m_clients.begin(); i != m_clients.end();)
     156              :     {
     157            1 :         std::shared_ptr<IControlClient> iLocked = i->lock();
     158            1 :         if (!iLocked)
     159              :         {
     160            0 :             RIALTO_CLIENT_LOG_ERROR("%s", kClientPointerNotLocked.c_str());
     161            0 :             i = m_clients.erase(i);
     162              :         }
     163            1 :         else if (iLocked == clientLocked)
     164              :         {
     165            1 :             i = m_clients.erase(i);
     166            1 :             found = true;
     167            1 :             break;
     168              :         }
     169              :         else
     170            0 :             ++i;
     171            1 :     }
     172              : 
     173            1 :     if (!found)
     174              :     {
     175            0 :         RIALTO_CLIENT_LOG_ERROR("Client not found");
     176            0 :         return false;
     177              :     }
     178              : 
     179            1 :     return true;
     180              : }
     181              : 
     182           11 : bool ClientController::initSharedMemory()
     183              : try
     184              : {
     185           11 :     std::lock_guard<std::mutex> lock{m_mutex};
     186           11 :     int32_t shmFd{-1};
     187           11 :     uint32_t shmBufferLen{0U};
     188           11 :     if (!m_controlIpc->getSharedMemory(shmFd, shmBufferLen))
     189              :     {
     190            1 :         RIALTO_CLIENT_LOG_ERROR("Failed to get the shared memory");
     191            1 :         return false;
     192              :     }
     193           10 :     m_shmHandle = std::make_shared<SharedMemoryHandle>(shmFd, shmBufferLen);
     194              : 
     195            8 :     RIALTO_CLIENT_LOG_INFO("Shared buffer was successfully initialised");
     196            8 :     return true;
     197           11 : }
     198            2 : catch (const std::exception &e)
     199              : {
     200            2 :     RIALTO_CLIENT_LOG_ERROR("Failed to initialise shared memory: %s", e.what());
     201            2 :     return false;
     202              : }
     203              : 
     204           26 : void ClientController::termSharedMemory()
     205              : {
     206           26 :     std::lock_guard<std::mutex> lock{m_mutex};
     207           26 :     m_shmHandle.reset();
     208              : }
     209              : 
     210           19 : void ClientController::notifyApplicationState(ApplicationState state)
     211              : {
     212              :     {
     213           19 :         std::lock_guard<std::mutex> lock{m_mutex};
     214           19 :         if (ApplicationState::UNKNOWN == state)
     215              :         {
     216            3 :             RIALTO_CLIENT_LOG_DEBUG("Application state changed to unknown. Client will have to register next time");
     217            3 :             m_registrationRequired = true;
     218              :         }
     219           19 :         if (m_currentState == state)
     220              :         {
     221            1 :             RIALTO_CLIENT_LOG_WARN("Rialto application state already set, %s", stateToString(m_currentState).c_str());
     222            1 :             return;
     223              :         }
     224           19 :     }
     225              : 
     226           18 :     switch (state)
     227              :     {
     228           11 :     case ApplicationState::RUNNING:
     229              :     {
     230           11 :         if (!initSharedMemory())
     231              :         {
     232            3 :             RIALTO_CLIENT_LOG_ERROR("Could not initalise the shared memory");
     233            3 :             return;
     234              :         }
     235              :         // Inform clients after memory initialisation
     236            8 :         changeStateAndNotifyClients(state);
     237            8 :         break;
     238              :     }
     239            7 :     case ApplicationState::INACTIVE:
     240              :     case ApplicationState::UNKNOWN:
     241              :     {
     242              :         // Inform clients before memory termination
     243            7 :         changeStateAndNotifyClients(state);
     244            7 :         termSharedMemory();
     245            7 :         break;
     246              :     }
     247              :     }
     248              : }
     249              : 
     250           31 : std::string ClientController::stateToString(ApplicationState state)
     251              : {
     252           31 :     switch (state)
     253              :     {
     254           14 :     case ApplicationState::RUNNING:
     255              :     {
     256           28 :         return "RUNNING";
     257              :     }
     258            5 :     case ApplicationState::INACTIVE:
     259              :     {
     260           10 :         return "INACTIVE";
     261              :     }
     262           12 :     case ApplicationState::UNKNOWN:
     263              :     default:
     264              :     {
     265           24 :         return "UNKNOWN";
     266              :     }
     267              :     }
     268              : }
     269              : 
     270           15 : void ClientController::changeStateAndNotifyClients(ApplicationState state)
     271              : {
     272           15 :     std::vector<std::shared_ptr<IControlClient>> currentClients;
     273              :     {
     274           15 :         std::lock_guard<std::mutex> lock{m_mutex};
     275           15 :         RIALTO_CLIENT_LOG_MIL("Rialto application state changed from %s to %s", stateToString(m_currentState).c_str(),
     276              :                               stateToString(state).c_str());
     277           15 :         m_currentState = state;
     278           22 :         for (const std::weak_ptr<IControlClient> &client : m_clients)
     279              :         {
     280            7 :             std::shared_ptr<IControlClient> clientLocked{client.lock()};
     281            7 :             if (clientLocked)
     282              :             {
     283            7 :                 currentClients.push_back(std::move(clientLocked));
     284              :             }
     285              :             else
     286              :             {
     287            0 :                 RIALTO_CLIENT_LOG_ERROR("%s", kClientPointerNotLocked.c_str());
     288              :             }
     289            7 :         }
     290           15 :     }
     291           22 :     for (const auto &client : currentClients)
     292              :     {
     293            7 :         client->notifyApplicationState(state);
     294              :     }
     295           15 : }
     296              : 
     297            1 : void ClientController::reportClientMetrics(std::uint64_t sampleId, std::uint32_t reason)
     298              : {
     299            2 :     if (!m_privateMetricsIpc->reportClientMetrics(sampleId, reason, getProcessName(),
     300            1 :                                                   static_cast<std::uint32_t>(getpid()), getMonotonicTimeMs(),
     301              :                                                   getEpochTimeMs(), getProcessCpuTimeMs(), getProcessMemoryKb()))
     302              :     {
     303            0 :         RIALTO_CLIENT_LOG_DEBUG("Failed to report client process metrics");
     304              :     }
     305            1 : }
     306              : 
     307            1 : std::uint64_t ClientController::getMonotonicTimeMs() const
     308              : {
     309              :     using std::chrono::duration_cast;
     310              :     using std::chrono::milliseconds;
     311              :     using std::chrono::steady_clock;
     312              : 
     313            1 :     return static_cast<std::uint64_t>(duration_cast<milliseconds>(steady_clock::now().time_since_epoch()).count());
     314              : }
     315              : 
     316            1 : std::uint64_t ClientController::getEpochTimeMs() const
     317              : {
     318              :     using std::chrono::duration_cast;
     319              :     using std::chrono::milliseconds;
     320              :     using std::chrono::system_clock;
     321              : 
     322            1 :     return static_cast<std::uint64_t>(duration_cast<milliseconds>(system_clock::now().time_since_epoch()).count());
     323              : }
     324              : 
     325            1 : std::uint64_t ClientController::getProcessCpuTimeMs() const
     326              : {
     327            1 :     struct tms processTimes = {0};
     328            1 :     const clock_t kCurrentTicks{times(&processTimes)};
     329            1 :     const int64_t kTicksPerSecond{sysconf(_SC_CLK_TCK)};
     330            1 :     if ((static_cast<clock_t>(-1) == kCurrentTicks) || (kTicksPerSecond <= 0))
     331              :     {
     332            0 :         RIALTO_CLIENT_LOG_WARN("Failed to sample client process CPU usage");
     333            0 :         return 0;
     334              :     }
     335              : 
     336            1 :     const auto kProcessTicks{processTimes.tms_utime + processTimes.tms_stime};
     337            1 :     return static_cast<std::uint64_t>((static_cast<double>(kProcessTicks) * 1000.0) /
     338            1 :                                       static_cast<double>(kTicksPerSecond));
     339              : }
     340              : 
     341            1 : std::string ClientController::getProcessName() const
     342              : {
     343            1 :     std::ifstream comm{"/proc/self/comm"};
     344            1 :     std::string processName;
     345            1 :     if (std::getline(comm, processName) && !processName.empty())
     346              :     {
     347            1 :         return processName;
     348              :     }
     349            0 :     return "unknown";
     350            1 : }
     351              : 
     352            1 : std::uint64_t ClientController::getProcessMemoryKb() const
     353              : {
     354            1 :     std::ifstream status{"/proc/self/status"};
     355            1 :     std::string line;
     356           24 :     while (std::getline(status, line))
     357              :     {
     358           23 :         if (line.rfind("VmRSS:", 0) == 0)
     359              :         {
     360            1 :             std::uint64_t memKb{0};
     361            1 :             if (std::sscanf(line.c_str(), "VmRSS: %" SCNu64, &memKb) == 1)
     362              :             {
     363            1 :                 return memKb;
     364              :             }
     365              :         }
     366              :     }
     367            0 :     RIALTO_CLIENT_LOG_WARN("Failed to sample client process memory usage");
     368            0 :     return 0;
     369            1 : }
     370              : } // namespace firebolt::rialto::client
        

Generated by: LCOV version 2.0-1