• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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