1 /**
2 * Copyright (c) 2021-2025 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 "runtime/profilesaver/profile_saver.h"
17
18 #include <cerrno>
19 #include <chrono>
20 #include <thread>
21
22 #include "runtime/include/runtime.h"
23 #include "trace/trace.h"
24
25 namespace ark {
26
27 ProfileSaver *ProfileSaver::instance_ = nullptr;
28 // NOLINTNEXTLINE(fuchsia-statically-constructed-objects)
29 std::thread ProfileSaver::profilerSaverDaemonThread_;
30
CheckLocationForCompilation(const PandaString & location)31 static bool CheckLocationForCompilation(const PandaString &location)
32 {
33 return !location.empty();
34 }
35
36 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
ProfileSaver(const PandaString & outputFilename,const PandaVector<PandaString> & codePaths,const PandaString & appDir)37 ProfileSaver::ProfileSaver(const PandaString &outputFilename, const PandaVector<PandaString> &codePaths,
38 const PandaString &appDir)
39 {
40 AddTrackedLocations(outputFilename, codePaths, appDir);
41 }
42
43 // NB! it is the caller's responsibility to pass suitable output_filename, code_paths as well as app_data_dir.
AddTrackedLocations(const PandaString & outputFilename,const PandaVector<PandaString> & codePaths,const PandaString & appDataDir)44 void ProfileSaver::AddTrackedLocations(const PandaString &outputFilename, const PandaVector<PandaString> &codePaths,
45 const PandaString &appDataDir)
46 {
47 auto it = trackedPandafileBaseLocations_.find(outputFilename);
48 if (it == trackedPandafileBaseLocations_.end()) {
49 trackedPandafileBaseLocations_.insert(
50 std::make_pair(outputFilename, PandaSet<PandaString>(codePaths.begin(), codePaths.end())));
51 if (!appDataDir.empty()) {
52 appDataDirs_.insert(appDataDir);
53 }
54 } else {
55 if (UNLIKELY(appDataDirs_.count(appDataDir) <= 0)) {
56 LOG(INFO, RUNTIME) << "Cannot find app dir, bad output filename";
57 return;
58 }
59 it->second.insert(codePaths.begin(), codePaths.end());
60 }
61 }
62
Start(const PandaString & outputFilename,const PandaVector<PandaString> & codePaths,const PandaString & appDataDir)63 void ProfileSaver::Start(const PandaString &outputFilename, const PandaVector<PandaString> &codePaths,
64 const PandaString &appDataDir)
65 {
66 if (Runtime::GetCurrent() == nullptr) {
67 LOG(ERROR, RUNTIME) << "Runtime is nullptr";
68 return;
69 }
70
71 if (!Runtime::GetCurrent()->SaveProfileInfo()) {
72 LOG(ERROR, RUNTIME) << "ProfileSaver is forbidden";
73 return;
74 }
75
76 if (outputFilename.empty()) {
77 LOG(ERROR, RUNTIME) << "Invalid output filename";
78 return;
79 }
80
81 PandaVector<PandaString> codePathsToProfile;
82 for (const PandaString &location : codePaths) {
83 if (CheckLocationForCompilation(location)) {
84 codePathsToProfile.push_back(location);
85 }
86 }
87
88 if (codePathsToProfile.empty()) {
89 LOG(INFO, RUNTIME) << "No code paths should be profiled.";
90 return;
91 }
92
93 if (UNLIKELY(instance_ != nullptr)) {
94 LOG(INFO, RUNTIME) << "Profile Saver Singleton already exists";
95 instance_->AddTrackedLocations(outputFilename, codePathsToProfile, appDataDir);
96 return;
97 }
98
99 LOG(INFO, RUNTIME) << "Starting dumping profile saver output file" << outputFilename;
100
101 instance_ = new ProfileSaver(outputFilename, codePathsToProfile, appDataDir);
102 profilerSaverDaemonThread_ = std::thread(ProfileSaver::RunProfileSaverThread, reinterpret_cast<void *>(instance_));
103 }
104
Stop(bool dumpInfo)105 void ProfileSaver::Stop(bool dumpInfo)
106 {
107 {
108 os::memory::LockHolder lock(profile_saver_lock_);
109
110 if (instance_ == nullptr) {
111 LOG(ERROR, RUNTIME) << "Tried to stop a profile saver which was not started";
112 return;
113 }
114
115 if (instance_->shuttingDown_) {
116 LOG(ERROR, RUNTIME) << "Tried to stop the profile saver twice";
117 return;
118 }
119
120 instance_->shuttingDown_ = true;
121
122 if (dumpInfo) {
123 instance_->DumpInfo();
124 }
125 }
126
127 // Wait for the saver thread to stop.
128 // NB! we must release profile_saver_lock_ here.
129 profilerSaverDaemonThread_.join();
130
131 // Kill the singleton ...
132 delete instance_;
133 instance_ = nullptr;
134 }
135
IsStarted()136 bool ProfileSaver::IsStarted()
137 {
138 return instance_ != nullptr;
139 }
140
DumpInfo()141 void ProfileSaver::DumpInfo()
142 {
143 LOG(INFO, RUNTIME) << "ProfileSaver stoped" << '\n';
144 }
145
RunProfileSaverThread(void * arg)146 void *ProfileSaver::RunProfileSaverThread(void *arg)
147 {
148 // NOLINTNEXTLINE(hicpp-use-auto, modernize-use-auto)
149 ProfileSaver *profileSaver = reinterpret_cast<ProfileSaver *>(arg);
150 profileSaver->Run();
151
152 LOG(INFO, RUNTIME) << "Profile saver shutdown";
153 return nullptr;
154 }
155
Run()156 void ProfileSaver::Run()
157 {
158 while (!ShuttingDown()) {
159 LOG(INFO, RUNTIME) << "Step1: Time Sleeping >>>>>>> ";
160 uint32_t sleepytime = Runtime::GetOptions().GetProfilesaverSleepingTimeMs();
161 constexpr uint32_t TIME_SECONDS_IN_MS = 1000;
162 for (int i = 0; i < static_cast<int>(sleepytime / TIME_SECONDS_IN_MS); ++i) {
163 std::this_thread::sleep_for(std::chrono::seconds(1));
164 if (ShuttingDown()) {
165 break;
166 }
167 }
168
169 LOG(INFO, RUNTIME) << "Step2: tranverse the resolved class and methods >>>>>>> ";
170 TranverseAndCacheResolvedClassAndMethods();
171
172 LOG(INFO, RUNTIME) << "Step3: merge current profile file and save it back >>>>>>> ";
173 MergeAndDumpProfileData();
174 }
175 }
176
ShuttingDown()177 bool ProfileSaver::ShuttingDown()
178 {
179 os::memory::LockHolder lock(profile_saver_lock_);
180 return shuttingDown_;
181 }
182
CallBackTranverseResolvedClassAndMethods(PandaSet<ExtractedResolvedClasses> & resolvedClasses,PandaVector<ExtractedMethod> & methods,Class * klass)183 static bool CallBackTranverseResolvedClassAndMethods(PandaSet<ExtractedResolvedClasses> &resolvedClasses,
184 PandaVector<ExtractedMethod> &methods, Class *klass)
185 {
186 const panda_file::File *pandafile = klass->GetPandaFile();
187 panda_file::File::EntityId classfieldid = klass->GetFileId();
188
189 if (pandafile == nullptr) {
190 LOG(INFO, RUNTIME) << "panda file is nullptr";
191 return false;
192 }
193 LOG(INFO, RUNTIME) << " pandafile name = " << pandafile->GetFilename() << " classname = " << klass->GetName();
194
195 Span<Method> tmpMethods = klass->GetMethods();
196 LOG(INFO, RUNTIME) << " methods size = " << tmpMethods.size();
197 for (int i = 0; i < static_cast<int>(tmpMethods.Size()); ++i) {
198 Method &method = tmpMethods[i];
199 if (!method.IsNative()) {
200 if (method.GetHotnessCounter() < Method::GetInitialHotnessCounter()) {
201 ASSERT(method.GetPandaFile() != nullptr);
202 LOG(INFO, RUNTIME) << " method pandafile name = " << method.GetPandaFile()->GetFilename();
203 methods.emplace_back(ExtractedMethod(method.GetPandaFile(), method.GetFileId()));
204 }
205 }
206 }
207
208 ExtractedResolvedClasses tmpResolvedClasses(ConvertToString(pandafile->GetFilename()),
209 pandafile->GetHeader()->checksum);
210 LOG(INFO, RUNTIME) << " Add class " << klass->GetName();
211 auto it = resolvedClasses.find(tmpResolvedClasses);
212 if (it != resolvedClasses.end()) {
213 it->AddClass(classfieldid.GetOffset());
214 } else {
215 tmpResolvedClasses.AddClass(classfieldid.GetOffset());
216 resolvedClasses.insert(tmpResolvedClasses);
217 }
218
219 return true;
220 }
221
TranverseAndCacheResolvedClassAndMethods()222 void ProfileSaver::TranverseAndCacheResolvedClassAndMethods()
223 {
224 trace::ScopedTrace scopedTrace(__PRETTY_FUNCTION__);
225 PandaSet<ExtractedResolvedClasses> resolvedClasses;
226 PandaVector<ExtractedMethod> methods;
227 auto callBack = [&resolvedClasses, &methods](Class *klass) -> bool {
228 return CallBackTranverseResolvedClassAndMethods(resolvedClasses, methods, klass);
229 };
230
231 // NB: classliner == nullptr
232 if (Runtime::GetCurrent()->GetClassLinker() == nullptr) {
233 LOG(INFO, RUNTIME) << "classliner is nullptr";
234 return;
235 }
236
237 LOG(INFO, RUNTIME) << " Step2.1: tranverse the resolved class and methods";
238 Runtime::GetCurrent()->GetClassLinker()->EnumerateClasses(callBack);
239 LOG(INFO, RUNTIME) << " Step2.2: starting tracking all the pandafile locations and flush the cache";
240
241 for (const auto &it : trackedPandafileBaseLocations_) {
242 const PandaString &filename = it.first;
243 const PandaSet<PandaString> &locations = it.second;
244
245 PandaSet<ExtractedResolvedClasses> resolvedClassesForLocation;
246 PandaVector<ExtractedMethod> methodsForLocation;
247
248 LOG(INFO, RUNTIME) << " all the locations are:";
249 for (auto const &iter : locations) {
250 LOG(INFO, RUNTIME) << iter << " ";
251 }
252
253 LOG(INFO, RUNTIME) << " Methods name : ";
254 for (const ExtractedMethod &ref : methods) {
255 LOG(INFO, RUNTIME) << " " << ref.pandaFile->GetFilename();
256 if (locations.find(ConvertToString(ref.pandaFile->GetFilename())) != locations.end()) {
257 LOG(INFO, RUNTIME) << " bingo method!";
258 methodsForLocation.push_back(ref);
259 }
260 }
261 LOG(INFO, RUNTIME) << std::endl;
262 LOG(INFO, RUNTIME) << " Classes name";
263
264 for (const ExtractedResolvedClasses &classes : resolvedClasses) {
265 LOG(INFO, RUNTIME) << " " << classes.GetPandaFileLocation();
266 if (locations.find(classes.GetPandaFileLocation()) != locations.end()) {
267 LOG(INFO, RUNTIME) << " bingo class!";
268 resolvedClassesForLocation.insert(classes);
269 }
270 }
271
272 ProfileDumpInfo *info = GetOrAddCachedProfiledInfo(filename);
273 LOG(INFO, RUNTIME) << " Adding Bingo Methods and Classes";
274 info->AddMethodsAndClasses(methodsForLocation, resolvedClassesForLocation);
275 }
276 }
277
GetOrAddCachedProfiledInfo(const PandaString & filename)278 ProfileDumpInfo *ProfileSaver::GetOrAddCachedProfiledInfo(const PandaString &filename)
279 {
280 auto infoIt = profileCache_.find(filename);
281 if (infoIt == profileCache_.end()) {
282 LOG(INFO, RUNTIME) << " bingo profile_cache_!";
283 auto ret = profileCache_.insert(std::make_pair(filename, ProfileDumpInfo()));
284 ASSERT(ret.second);
285 infoIt = ret.first;
286 }
287 return &(infoIt->second);
288 }
289
GetOrAddCachedProfiledStatsInfo(const PandaString & filename)290 ProfileSaver::CntStats *ProfileSaver::GetOrAddCachedProfiledStatsInfo(const PandaString &filename)
291 {
292 auto infoIt = statcache.find(filename);
293 if (infoIt == statcache.end()) {
294 LOG(INFO, RUNTIME) << " bingo StatsInfo_cache_!";
295 auto ret = statcache.insert(std::make_pair(filename, CntStats()));
296 ASSERT(ret.second);
297 infoIt = ret.first;
298 }
299 return &(infoIt->second);
300 }
301
MergeAndDumpProfileData()302 void ProfileSaver::MergeAndDumpProfileData()
303 {
304 trace::ScopedTrace scopedTrace(__PRETTY_FUNCTION__);
305 for (const auto &it : trackedPandafileBaseLocations_) {
306 if (ShuttingDown()) {
307 return;
308 }
309 const PandaString &filename = it.first;
310 LOG(INFO, RUNTIME) << " Step3.1 starting merging and save the following file ***";
311 LOG(INFO, RUNTIME) << " filename = " << filename;
312
313 ProfileDumpInfo *cachedInfo = GetOrAddCachedProfiledInfo(filename);
314 CntStats *cachedStat = GetOrAddCachedProfiledStatsInfo(filename);
315 ASSERT(cachedInfo->GetNumberOfMethods() >= cachedStat->GetMethodCount());
316 ASSERT(cachedInfo->GetNumberOfResolvedClasses() >= cachedStat->GetClassCount());
317 uint64_t deltaNumberOfMethods = cachedInfo->GetNumberOfMethods() - cachedStat->GetMethodCount();
318 uint64_t deltaNumberOfClasses = cachedInfo->GetNumberOfResolvedClasses() - cachedStat->GetClassCount();
319 uint64_t numthreshold = Runtime::GetOptions().GetProfilesaverDeltaNumberThreshold();
320 if (deltaNumberOfMethods < numthreshold && deltaNumberOfClasses < numthreshold) {
321 LOG(INFO, RUNTIME) << " number of delta number/class not enough";
322 continue;
323 }
324
325 ssize_t bytesWritten;
326 if (cachedInfo->MergeAndSave(filename, &bytesWritten, true)) {
327 cachedStat->SetMethodCount(cachedInfo->GetNumberOfMethods());
328 cachedStat->SetClassCount(cachedInfo->GetNumberOfResolvedClasses());
329 } else {
330 LOG(INFO, RUNTIME) << "Could not save profiling info to " << filename;
331 }
332 }
333 }
334
335 } // namespace ark
336