LCOV - code coverage report
Current view: top level - common/source - Profiler.cpp (source / functions) Coverage Total Hit
Test: coverage.info Lines: 88.8 % 89 79
Test Date: 2026-05-14 05:59:51 Functions: 100.0 % 14 14

            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 2025 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 "Profiler.h"
      21              : #include "RialtoCommonLogging.h"
      22              : 
      23              : #include <algorithm>
      24              : #include <cctype>
      25              : #include <cstddef>
      26              : #include <cstdint>
      27              : #include <cstdlib>
      28              : #include <fstream>
      29              : #include <memory>
      30              : #include <string>
      31              : 
      32              : namespace
      33              : {
      34              : inline constexpr const char *kProfilerEnv{"PROFILER_ENABLED"};
      35              : inline constexpr const char *kProfilerDumpFileEnv{"PROFILER_DUMP_FILE_NAME"};
      36              : inline constexpr std::size_t kMaxRecords{100};
      37              : 
      38           39 : bool getProfilerEnabled()
      39              : {
      40           39 :     const char *value = std::getenv(kProfilerEnv);
      41           39 :     if (!value || (value[0] == '\0'))
      42            0 :         return false;
      43              : 
      44           39 :     std::string stringValue(value);
      45           39 :     std::transform(stringValue.begin(), stringValue.end(), stringValue.begin(),
      46          175 :                    [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
      47              : 
      48           39 :     if (stringValue == "1" || stringValue == "true" || stringValue == "yes" || stringValue == "on")
      49           36 :         return true;
      50            3 :     if (stringValue == "0" || stringValue == "false" || stringValue == "no" || stringValue == "off")
      51            2 :         return false;
      52              : 
      53            1 :     return false;
      54           39 : }
      55              : 
      56           39 : std::optional<std::string> getDumpFileName()
      57              : {
      58           39 :     const char *value = std::getenv(kProfilerDumpFileEnv);
      59           39 :     if (!value || value[0] == '\0')
      60           33 :         return std::nullopt;
      61              : 
      62           18 :     return std::string{value};
      63              : }
      64              : } // namespace
      65              : 
      66              : namespace firebolt::rialto::common
      67              : {
      68           32 : std::shared_ptr<IProfilerFactory> IProfilerFactory::createFactory()
      69              : {
      70           32 :     std::shared_ptr<IProfilerFactory> factory;
      71              : 
      72              :     try
      73              :     {
      74           32 :         factory = std::make_shared<ProfilerFactory>();
      75              :     }
      76            0 :     catch (const std::exception &e)
      77              :     {
      78            0 :         RIALTO_COMMON_LOG_ERROR("Failed to create the profiler factory, reason: %s", e.what());
      79              :     }
      80              : 
      81           32 :     return factory;
      82              : }
      83              : 
      84           39 : std::unique_ptr<IProfiler> ProfilerFactory::createProfiler(std::string moduleName) const
      85              : {
      86           39 :     return std::make_unique<Profiler>(moduleName);
      87              : }
      88              : 
      89           39 : Profiler::Profiler(std::string module)
      90           39 :     : m_module(std::move(module)), m_enabled(getProfilerEnabled()), m_dumpFileName(getDumpFileName())
      91              : {
      92              : }
      93              : 
      94           35 : bool Profiler::isEnabled() const noexcept
      95              : {
      96           35 :     return m_enabled;
      97              : }
      98              : 
      99           17 : std::optional<Profiler::RecordId> Profiler::record(const std::string &stage)
     100              : {
     101           17 :     if (!m_enabled)
     102            0 :         return std::nullopt;
     103              : 
     104           17 :     auto now = Clock::now();
     105           17 :     std::lock_guard<std::mutex> lock(m_mutex);
     106              : 
     107           17 :     if (m_records.size() >= kMaxRecords)
     108              :     {
     109            0 :         return std::nullopt;
     110              :     }
     111              : 
     112           17 :     const Profiler::RecordId id = m_id++;
     113              : 
     114           17 :     m_records.push_back(Record{m_module, id, stage, std::string{}, now});
     115              : 
     116           17 :     return id;
     117              : }
     118              : 
     119            9 : std::optional<Profiler::RecordId> Profiler::record(const std::string &stage, const std::string &info)
     120              : {
     121            9 :     if (!m_enabled)
     122            0 :         return std::nullopt;
     123              : 
     124            9 :     auto now = Clock::now();
     125            9 :     std::lock_guard<std::mutex> lock(m_mutex);
     126              : 
     127            9 :     if (m_records.size() >= kMaxRecords)
     128              :     {
     129            0 :         return std::nullopt;
     130              :     }
     131              : 
     132            9 :     const Profiler::RecordId id = m_id++;
     133              : 
     134            9 :     m_records.push_back(Record{m_module, id, stage, info, now});
     135              : 
     136            9 :     return id;
     137              : }
     138              : 
     139            7 : void Profiler::log(const RecordId id)
     140              : {
     141            7 :     if (!m_enabled)
     142            0 :         return;
     143              : 
     144            7 :     const auto record = findById(id);
     145            7 :     if (record)
     146              :     {
     147            6 :         const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(record->time.time_since_epoch()).count();
     148              : 
     149            6 :         const auto idStr = std::to_string(static_cast<std::uint64_t>(record->id));
     150            6 :         const auto tsStr = std::to_string(static_cast<std::int64_t>(ms));
     151              : 
     152            6 :         RIALTO_COMMON_LOG_MIL("PROFILER | RECORD | MODULE[%s] ID[%s] STAGE[%s] INFO[%s] TIMESTAMP[%s]",
     153              :                               record->moduleName.c_str(), idStr.c_str(), record->stage.c_str(), record->info.c_str(),
     154              :                               tsStr.c_str());
     155              :     }
     156            7 : }
     157              : 
     158           11 : bool Profiler::dumpToFile() const
     159              : {
     160           11 :     if (!m_dumpFileName)
     161            2 :         return false;
     162              : 
     163            9 :     if (!m_enabled)
     164            0 :         return false;
     165              : 
     166            9 :     std::vector<Record> copy;
     167              :     {
     168            9 :         std::lock_guard<std::mutex> lock(m_mutex);
     169            9 :         copy = m_records;
     170              :     }
     171              : 
     172            9 :     std::ofstream out(*m_dumpFileName, std::ios::out | std::ios::app);
     173            9 :     if (!out.is_open())
     174            1 :         return false;
     175              : 
     176           20 :     for (const auto &record : copy)
     177              :     {
     178           12 :         const auto us = std::chrono::duration_cast<std::chrono::microseconds>(record.time.time_since_epoch()).count();
     179              : 
     180           12 :         out << "MODULE[" << record.moduleName << "] " << "ID[" << record.id << "] " << "STAGE[" << record.stage << "] "
     181           12 :             << "INFO[" << record.info << "] " << "TIMESTAMP[" << us << "]" << '\n';
     182              :     }
     183              : 
     184            8 :     return static_cast<bool>(out);
     185            6 : }
     186              : 
     187           10 : std::vector<IProfiler::Record> Profiler::getRecords() const
     188              : {
     189           10 :     if (!m_enabled)
     190            0 :         return {};
     191              : 
     192           10 :     std::lock_guard<std::mutex> lock(m_mutex);
     193           10 :     return m_records;
     194              : }
     195              : 
     196            7 : std::optional<Profiler::Record> Profiler::findById(Profiler::RecordId id) const
     197              : {
     198            7 :     std::lock_guard<std::mutex> lock(m_mutex);
     199              : 
     200           14 :     const auto it = std::find_if(m_records.begin(), m_records.end(), [&](const auto &record) { return record.id == id; });
     201              : 
     202            7 :     if (it != m_records.end())
     203            6 :         return *it;
     204              : 
     205            1 :     return std::nullopt;
     206            7 : }
     207              : 
     208              : }; // namespace firebolt::rialto::common
        

Generated by: LCOV version 2.0-1