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