• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2022 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 panda {
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 
TranverseAndCacheResolvedClassAndMethods()183 void ProfileSaver::TranverseAndCacheResolvedClassAndMethods()
184 {
185     trace::ScopedTrace scopedTrace(__PRETTY_FUNCTION__);
186     PandaSet<ExtractedResolvedClasses> resolvedClasses;
187     PandaVector<ExtractedMethod> methods;
188     auto callBack = [&resolvedClasses, &methods](Class *klass) -> bool {
189         const panda_file::File *pandafile = klass->GetPandaFile();
190         panda_file::File::EntityId classfieldid = klass->GetFileId();
191 
192         if (pandafile == nullptr) {
193             LOG(INFO, RUNTIME) << "panda file is nullptr";
194             return false;
195         }
196         LOG(INFO, RUNTIME) << "      pandafile name = " << pandafile->GetFilename()
197                            << " classname = " << klass->GetName();
198 
199         Span<Method> tmpMethods = klass->GetMethods();
200         LOG(INFO, RUNTIME) << "      methods size = " << tmpMethods.size();
201         for (int i = 0; i < static_cast<int>(tmpMethods.Size()); ++i) {
202             Method &method = tmpMethods[i];
203             if (!method.IsNative()) {
204                 if (method.GetHotnessCounter() < Method::GetInitialHotnessCounter()) {
205                     ASSERT(method.GetPandaFile() != nullptr);
206                     LOG(INFO, RUNTIME) << "      method pandafile name = " << method.GetPandaFile()->GetFilename();
207                     methods.emplace_back(ExtractedMethod(method.GetPandaFile(), method.GetFileId()));
208                 }
209             }
210         }
211 
212         ExtractedResolvedClasses tmpResolvedClasses(ConvertToString(pandafile->GetFilename()),
213                                                     pandafile->GetHeader()->checksum);
214         LOG(INFO, RUNTIME) << "      Add class " << klass->GetName();
215         auto it = resolvedClasses.find(tmpResolvedClasses);
216         if (it != resolvedClasses.end()) {
217             it->AddClass(classfieldid.GetOffset());
218         } else {
219             tmpResolvedClasses.AddClass(classfieldid.GetOffset());
220             resolvedClasses.insert(tmpResolvedClasses);
221         }
222 
223         return true;
224     };
225 
226     // NB: classliner == nullptr
227     if (Runtime::GetCurrent()->GetClassLinker() == nullptr) {
228         LOG(INFO, RUNTIME) << "classliner is nullptr";
229         return;
230     }
231 
232     LOG(INFO, RUNTIME) << "  Step2.1: tranverse the resolved class and methods";
233     Runtime::GetCurrent()->GetClassLinker()->EnumerateClasses(callBack);
234     LOG(INFO, RUNTIME) << "  Step2.2: starting tracking all the pandafile locations and flush the cache";
235 
236     for (const auto &it : trackedPandafileBaseLocations_) {
237         const PandaString &filename = it.first;
238         const PandaSet<PandaString> &locations = it.second;
239 
240         PandaSet<ExtractedResolvedClasses> resolvedClassesForLocation;
241         PandaVector<ExtractedMethod> methodsForLocation;
242 
243         LOG(INFO, RUNTIME) << "      all the locations are:";
244         for (auto const &iter : locations) {
245             LOG(INFO, RUNTIME) << iter << " ";
246         }
247 
248         LOG(INFO, RUNTIME) << "      Methods name : ";
249         for (const ExtractedMethod &ref : methods) {
250             LOG(INFO, RUNTIME) << "      " << ref.pandaFile->GetFilename();
251             if (locations.find(ConvertToString(ref.pandaFile->GetFilename())) != locations.end()) {
252                 LOG(INFO, RUNTIME) << "      bingo method!";
253                 methodsForLocation.push_back(ref);
254             }
255         }
256         LOG(INFO, RUNTIME) << std::endl;
257         LOG(INFO, RUNTIME) << "      Classes name";
258 
259         for (const ExtractedResolvedClasses &classes : resolvedClasses) {
260             LOG(INFO, RUNTIME) << "      " << classes.GetPandaFileLocation();
261             if (locations.find(classes.GetPandaFileLocation()) != locations.end()) {
262                 LOG(INFO, RUNTIME) << "      bingo class!";
263                 resolvedClassesForLocation.insert(classes);
264             }
265         }
266 
267         ProfileDumpInfo *info = GetOrAddCachedProfiledInfo(filename);
268         LOG(INFO, RUNTIME) << "      Adding Bingo Methods and Classes";
269         info->AddMethodsAndClasses(methodsForLocation, resolvedClassesForLocation);
270     }
271 }
272 
GetOrAddCachedProfiledInfo(const PandaString & filename)273 ProfileDumpInfo *ProfileSaver::GetOrAddCachedProfiledInfo(const PandaString &filename)
274 {
275     auto infoIt = profileCache_.find(filename);
276     if (infoIt == profileCache_.end()) {
277         LOG(INFO, RUNTIME) << "      bingo profile_cache_!";
278         auto ret = profileCache_.insert(std::make_pair(filename, ProfileDumpInfo()));
279         ASSERT(ret.second);
280         infoIt = ret.first;
281     }
282     return &(infoIt->second);
283 }
284 
GetOrAddCachedProfiledStatsInfo(const PandaString & filename)285 ProfileSaver::CntStats *ProfileSaver::GetOrAddCachedProfiledStatsInfo(const PandaString &filename)
286 {
287     auto infoIt = statcache.find(filename);
288     if (infoIt == statcache.end()) {
289         LOG(INFO, RUNTIME) << "      bingo StatsInfo_cache_!";
290         auto ret = statcache.insert(std::make_pair(filename, CntStats()));
291         ASSERT(ret.second);
292         infoIt = ret.first;
293     }
294     return &(infoIt->second);
295 }
296 
MergeAndDumpProfileData()297 void ProfileSaver::MergeAndDumpProfileData()
298 {
299     trace::ScopedTrace scopedTrace(__PRETTY_FUNCTION__);
300     for (const auto &it : trackedPandafileBaseLocations_) {
301         if (ShuttingDown()) {
302             return;
303         }
304         const PandaString &filename = it.first;
305         LOG(INFO, RUNTIME) << "  Step3.1 starting merging and save the following file ***";
306         LOG(INFO, RUNTIME) << "      filename = " << filename;
307 
308         ProfileDumpInfo *cachedInfo = GetOrAddCachedProfiledInfo(filename);
309         CntStats *cachedStat = GetOrAddCachedProfiledStatsInfo(filename);
310         ASSERT(cachedInfo->GetNumberOfMethods() >= cachedStat->GetMethodCount());
311         ASSERT(cachedInfo->GetNumberOfResolvedClasses() >= cachedStat->GetClassCount());
312         uint64_t deltaNumberOfMethods = cachedInfo->GetNumberOfMethods() - cachedStat->GetMethodCount();
313         uint64_t deltaNumberOfClasses = cachedInfo->GetNumberOfResolvedClasses() - cachedStat->GetClassCount();
314         uint64_t numthreshold = Runtime::GetOptions().GetProfilesaverDeltaNumberThreshold();
315         if (deltaNumberOfMethods < numthreshold && deltaNumberOfClasses < numthreshold) {
316             LOG(INFO, RUNTIME) << "      number of delta number/class not enough";
317             continue;
318         }
319 
320         uint64_t bytesWritten;
321         if (cachedInfo->MergeAndSave(filename, &bytesWritten, true)) {
322             cachedStat->SetMethodCount(cachedInfo->GetNumberOfMethods());
323             cachedStat->SetClassCount(cachedInfo->GetNumberOfResolvedClasses());
324         } else {
325             LOG(INFO, RUNTIME) << "Could not save profiling info to " << filename;
326         }
327     }
328 }
329 
330 }  // namespace panda
331