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
|