• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "timers.h"
17 #include "os/file.h"
18 
19 #include <algorithm>
20 #include <cstring>
21 
22 #ifndef IS_ARKUI_X_TARGET
23 #include <nlohmann/json.hpp>
24 #endif
25 
26 namespace panda {
27 TimeStartFunc Timer::timerStart = Timer::TimerStartDoNothing;
28 TimeEndFunc Timer::timerEnd = Timer::TimerEndDoNothing;
29 std::unordered_map<std::string_view, TimeRecord> Timer::timers_;
30 std::vector<std::string_view> Timer::events_;
31 std::mutex Timer::mutex_;
32 std::string Timer::perfFile_;
33 #ifndef IS_ARKUI_X_TARGET
34 static const int INDENT_LEVEL = 4;
35 #endif
36 
InitializeTimer(std::string & perfFile)37 void Timer::InitializeTimer(std::string &perfFile)
38 {
39     if (!perfFile.empty()) {
40         Timer::timerStart = Timer::TimerStartImpl;
41         Timer::timerEnd = Timer::TimerEndImpl;
42         perfFile_ = perfFile;
43     }
44 }
45 
WriteFile(std::stringstream & ss,std::string & perfFile)46 void WriteFile(std::stringstream &ss, std::string &perfFile)
47 {
48     std::ofstream fs;
49     fs.open(panda::os::file::File::GetExtendedFilePath(perfFile));
50     if (!fs.is_open()) {
51         std::cerr << "Failed to open perf file: " << perfFile << ". Errro: " << std::strerror(errno) << std::endl;
52         return;
53     }
54     fs << ss.str();
55     fs.close();
56 }
57 
DescentComparator(const std::pair<std::string,double> p1,const std::pair<std::string,double> p2)58 bool DescentComparator(const std::pair<std::string, double> p1, const std::pair<std::string, double> p2)
59 {
60     return p1.second > p2.second;
61 }
62 
ProcessTimePointRecord(const std::string & file,const TimePointRecord & timePointRecord,double & eventTime,std::vector<std::pair<std::string,double>> & eachFileTime,std::vector<std::string> & incompleteRecords)63 void ProcessTimePointRecord(
64     const std::string& file,
65     const TimePointRecord& timePointRecord,
66     double& eventTime,
67     std::vector<std::pair<std::string, double>>& eachFileTime,
68     std::vector<std::string>& incompleteRecords)
69 {
70     if (timePointRecord.startTime != std::chrono::steady_clock::time_point() &&
71         timePointRecord.endTime != std::chrono::steady_clock::time_point()) {
72         auto duration = timePointRecord.endTime - timePointRecord.startTime;
73         if (duration.count() >= 0) {
74             auto t = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();
75             eventTime += t;
76             if (!file.empty()) {
77                 eachFileTime.emplace_back(file, t);
78             }
79         } else {
80             incompleteRecords.push_back(file + ": The end time is earlier than the start time");
81         }
82     } else {
83         incompleteRecords.push_back(file + ": " +
84             (timePointRecord.startTime == std::chrono::steady_clock::time_point() ?
85             "Lack of start time" : "Lack of end time"));
86     }
87 }
88 
PrintTimers()89 void Timer::PrintTimers()
90 {
91 #ifndef IS_ARKUI_X_TARGET
92     const std::string suffix = ".json";
93     if (perfFile_.length() >= suffix.length() &&
94         perfFile_.compare(perfFile_.length() - suffix.length(), suffix.length(), suffix) == 0) {
95         PrintJson();
96         return;
97     }
98 #endif
99     PrintTxt();
100 }
101 
102 #ifndef IS_ARKUI_X_TARGET
to_json(nlohmann::json & j,const TimeRecord & t)103 void to_json(nlohmann::json& j, const TimeRecord& t)
104 {
105     auto early = std::chrono::steady_clock::time_point::max();
106     auto late = std::chrono::steady_clock::time_point::min();
107 
108     for (auto& [fileName, timePointRecord] : t.timePoints) {
109         if (timePointRecord.startTime < early) {
110             early = timePointRecord.startTime;
111         }
112         if (timePointRecord.endTime > late) {
113             late = timePointRecord.endTime;
114         }
115     }
116     auto start = std::chrono::duration_cast<std::chrono::nanoseconds>(early.time_since_epoch()).count();
117     auto end = std::chrono::duration_cast<std::chrono::nanoseconds>(late.time_since_epoch()).count();
118     auto duration = end - start;
119 
120     std::string parentEvent;
121     auto eventIter = eventParent.find(t.event);
122     if (eventIter != eventParent.end()) {
123         parentEvent = eventIter->second;
124     }
125     j = nlohmann::json{
126         {"name", t.event},
127         {"parentEvent", parentEvent},
128         {"startTime", start},
129         {"endTime", end},
130         {"duration", duration}
131     };
132 }
133 
PrintJson()134 void Timer::PrintJson()
135 {
136     std::stringstream ss;
137     nlohmann::json jsonArray = nlohmann::json::array();
138 
139     for (auto &event: events_) {
140         auto &timeRecord = timers_.at(event);
141         nlohmann::json j = timeRecord;
142         jsonArray.push_back(j);
143     }
144 
145     ss << jsonArray.dump(INDENT_LEVEL) << std::endl;
146 
147     WriteFile(ss, perfFile_);
148 }
149 #endif
150 
PrintTxt()151 void Timer::PrintTxt()
152 {
153     std::stringstream ss;
154     ss << "------------- Compilation time consumption in milliseconds: -------------" << std::endl;
155     ss << "Note: When compiling multiple files in parallel, " <<
156           "we will track the time consumption of each file individually. The output will aggregate these times, " <<
157           "potentially resulting in a total time consumption less than the sum of individual file times." <<
158           std::endl << std::endl;
159 
160     std::vector<std::string> summedUpTimeString;
161     ss << "------------- Compilation time consumption of each file: ----------------" << std::endl;
162 
163     for (auto &event: events_) {
164         auto &timeRecord = timers_.at(event);
165         auto formattedEvent =
166             std::string(timeRecord.level, ' ') + std::string(timeRecord.level, '#') + std::string(event);
167 
168         double eventTime = 0.0;
169         std::vector<std::pair<std::string, double>> eachFileTime;
170         std::vector<std::string> incompleteRecords;
171 
172         for (const auto &[file, timePointRecord] : timeRecord.timePoints) {
173             ProcessTimePointRecord(file, timePointRecord, eventTime, eachFileTime, incompleteRecords);
174         }
175 
176         // print each file time consumption in descending order
177         if (!eachFileTime.empty()) {
178             std::sort(eachFileTime.begin(), eachFileTime.end(), DescentComparator);
179             ss << formattedEvent << ", time consumption of each file:" << std::endl;
180         }
181         for (auto &pair : eachFileTime) {
182             if (!pair.first.empty()) {
183                 ss << pair.first << ": " << pair.second << " ms" <<std::endl;
184             }
185         }
186 
187         // Print incomplete records
188         if (!incompleteRecords.empty()) {
189             ss << formattedEvent << ", Incomplete records:" << std::endl;
190             for (const auto& record : incompleteRecords) {
191                 ss << "  " << record << std::endl;
192             }
193         }
194 
195         // collect the sum of each file's time consumption
196         std::stringstream eventSummedUpTimeStream;
197         eventSummedUpTimeStream << formattedEvent << ": " << eventTime << " ms";
198         summedUpTimeString.push_back(eventSummedUpTimeStream.str());
199     }
200     ss << "------------- Compilation time consumption summed up: -------------------" << std::endl;
201     for (auto &str: summedUpTimeString) {
202         ss << str << std::endl;
203     }
204     ss << "-------------------------------------------------------------------------" << std::endl;
205     WriteFile(ss, perfFile_);
206 }
207 
TimerStartImpl(const std::string_view event,std::string fileName)208 void Timer::TimerStartImpl(const std::string_view event, std::string fileName)
209 {
210     TimePointRecord tpr;
211     tpr.startTime = std::chrono::steady_clock::now();
212     int level = 0;
213     auto eventIter = eventMap.find(event);
214     if (eventIter != eventMap.end()) {
215         level = eventIter->second;
216     } else {
217         std::cerr << "Undefined event: " << event << ". Please check!" << std::endl;
218     }
219 
220     std::unique_lock<std::mutex> lock(mutex_);
221     auto iter = timers_.find(event);
222     if (iter != timers_.end()) {
223         iter->second.timePoints.emplace(fileName, tpr);
224     } else {
225         TimeRecord tr;
226         tr.timePoints.emplace(fileName, tpr);
227         tr.event = event;
228         tr.level = level;
229 
230         timers_.emplace(event, tr);
231         events_.push_back(event);
232     }
233 }
234 
TimerEndImpl(const std::string_view event,std::string fileName)235 void Timer::TimerEndImpl(const std::string_view event, std::string fileName)
236 {
237     auto endTime = std::chrono::steady_clock::now();
238     std::unique_lock<std::mutex> lock(mutex_);
239     auto time_record_iter = timers_.find(event);
240     if (time_record_iter == timers_.end()) {
241         std::cerr << "Event " << event << " not found in records, skip record end time!" << std::endl;
242         return;
243     }
244     auto& timePoints = time_record_iter->second.timePoints;
245     auto time_point_iter = timePoints.find(fileName);
246     if (time_point_iter == timePoints.end()) {
247         std::cerr << "Event " << event << " and file " << fileName <<
248             " start timer not found in records, skip record end time!" << std::endl;
249         return;
250     }
251     time_point_iter->second.endTime = endTime;
252 }
253 
254 }  // namespace panda
255