• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-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 "tools/sampler/aspt_converter.h"
17 
18 namespace ark::tooling::sampler {
19 
CollectTracesStats()20 size_t AsptConverter::CollectTracesStats()
21 {
22     stackTraces_.clear();
23 
24     size_t sampleCounter = 0;
25     SampleInfo sample;
26     while (reader_.GetNextSample(&sample)) {
27         ++sampleCounter;
28 
29         if (dumpType_ == DumpType::WITHOUT_THREAD_SEPARATION) {
30             // NOTE: zeroing thread_id to make samples indistinguishable in mode without thread separation
31             sample.threadInfo.threadId = 0;
32         }
33 
34         if (!buildColdGraph_) {
35             // NOTE: zeroing thread_status to make samples indistinguishable
36             // in mode without building cold flamegraph
37             sample.threadInfo.threadStatus = SampleInfo::ThreadStatus::UNDECLARED;
38         }
39 
40         auto it = stackTraces_.find(sample);
41         if (it == stackTraces_.end()) {
42             stackTraces_.insert({sample, 1});
43             continue;
44         }
45         ++it->second;
46     }
47     return sampleCounter;
48 }
49 
CollectModules()50 bool AsptConverter::CollectModules()
51 {
52     FileInfo mInfo;
53     while (reader_.GetNextModule(&mInfo)) {
54         modules_.push_back(mInfo);
55     }
56 
57     return !modules_.empty();
58 }
59 
BuildModulesMap()60 bool AsptConverter::BuildModulesMap()
61 {
62     for (auto &mdl : modules_) {
63         std::string filepath = mdl.pathname;
64 
65         if (substituteDirectories_.has_value()) {
66             SubstituteDirectories(&filepath);
67         }
68 
69         if (!ark::os::IsFileExists(filepath)) {
70             LOG(ERROR, PROFILER) << "Module not found, path: " << filepath;
71         }
72 
73         if (modulesMap_.find(mdl.ptr) == modulesMap_.end()) {
74             auto pfUnique = panda_file::OpenPandaFileOrZip(filepath.c_str());
75             if (mdl.checksum != pfUnique->GetHeader()->checksum) {
76                 LOG(FATAL, PROFILER) << "Checksum of panda files isn't equal";
77                 return false;
78             }
79 
80             modulesMap_.insert({mdl.ptr, std::move(pfUnique)});
81         }
82     }
83     return !modulesMap_.empty();
84 }
85 
SubstituteDirectories(std::string * pathname) const86 void AsptConverter::SubstituteDirectories(std::string *pathname) const
87 {
88     for (size_t i = 0; i < substituteDirectories_->source.size(); ++i) {
89         auto pos = pathname->find(substituteDirectories_->source[i]);
90         if (pos != std::string::npos) {
91             pathname->replace(pos, substituteDirectories_->source[i].size(), substituteDirectories_->destination[i]);
92             break;
93         }
94     }
95 }
96 
DumpResolvedTracesAsCSV(const char * filename)97 bool AsptConverter::DumpResolvedTracesAsCSV(const char *filename)
98 {
99     std::unique_ptr<TraceDumper> dumper;
100 
101     switch (dumpType_) {
102         case DumpType::WITHOUT_THREAD_SEPARATION:
103         case DumpType::THREAD_SEPARATION_BY_TID:
104             dumper =
105                 std::make_unique<SingleCSVDumper>(filename, dumpType_, &modulesMap_, &methodsMap_, buildColdGraph_);
106             break;
107         case DumpType::THREAD_SEPARATION_BY_CSV:
108             dumper = std::make_unique<MultipleCSVDumper>(filename, &modulesMap_, &methodsMap_, buildColdGraph_);
109             break;
110         default:
111             UNREACHABLE();
112     }
113     for (auto &[sample, count] : stackTraces_) {
114         ASSERT(sample.stackInfo.managedStackSize <= SampleInfo::StackInfo::MAX_STACK_DEPTH);
115         dumper->DumpTraces(sample, count);
116     }
117     return true;
118 }
119 
BuildMethodsMap()120 void AsptConverter::BuildMethodsMap()
121 {
122     for (const auto &pfPair : modulesMap_) {
123         const panda_file::File *pf = pfPair.second.get();
124         if (pf == nullptr) {
125             continue;
126         }
127         auto classesSpan = pf->GetClasses();
128         BuildMethodsMapHelper(pf, classesSpan);
129     }
130 }
131 
BuildMethodsMapHelper(const panda_file::File * pf,Span<const uint32_t> & classesSpan)132 void AsptConverter::BuildMethodsMapHelper(const panda_file::File *pf, Span<const uint32_t> &classesSpan)
133 {
134     for (auto id : classesSpan) {
135         if (pf->IsExternal(panda_file::File::EntityId(id))) {
136             continue;
137         }
138         panda_file::ClassDataAccessor cda(*pf, panda_file::File::EntityId(id));
139         cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
140             std::string methodName = utf::Mutf8AsCString(mda.GetName().data);
141             std::string className = utf::Mutf8AsCString(cda.GetDescriptor());
142             if (className[className.length() - 1] == ';') {
143                 className.pop_back();
144             }
145             std::string fullName = className + "::";
146             fullName += methodName;
147             methodsMap_[pf][mda.GetMethodId().GetOffset()] = std::move(fullName);
148         });
149     }
150 }
151 
DumpModulesToFile(const std::string & outname) const152 bool AsptConverter::DumpModulesToFile(const std::string &outname) const
153 {
154     std::ofstream out(outname, std::ios::binary);
155     if (!out) {
156         LOG(ERROR, PROFILER) << "Can't open " << outname;
157         out.close();
158         return false;
159     }
160 
161     for (auto &mdl : modules_) {
162         out << mdl.checksum << " " << mdl.pathname << "\n";
163     }
164 
165     if (out.fail()) {
166         LOG(ERROR, PROFILER) << "Failbit or the badbit (or both) was set";
167         out.close();
168         return false;
169     }
170 
171     return true;
172 }
173 
174 /* static */
GetDumpTypeFromOptions(const Options & cliOptions)175 DumpType AsptConverter::GetDumpTypeFromOptions(const Options &cliOptions)
176 {
177     const std::string dumpTypeStr = cliOptions.GetCsvTidSeparation();
178 
179     DumpType dumpType;
180     if (dumpTypeStr == "single-csv-single-tid") {
181         dumpType = DumpType::WITHOUT_THREAD_SEPARATION;
182     } else if (dumpTypeStr == "single-csv-multi-tid") {
183         dumpType = DumpType::THREAD_SEPARATION_BY_TID;
184     } else if (dumpTypeStr == "multi-csv") {
185         dumpType = DumpType::THREAD_SEPARATION_BY_CSV;
186     } else {
187         std::cerr << "unknown value of csv-tid-distribution option: '" << dumpTypeStr
188                   << "' single-csv-multi-tid will be set" << std::endl;
189         dumpType = DumpType::THREAD_SEPARATION_BY_TID;
190     }
191 
192     return dumpType;
193 }
194 
RunDumpModulesMode(const std::string & outname)195 bool AsptConverter::RunDumpModulesMode(const std::string &outname)
196 {
197     if (CollectTracesStats() == 0) {
198         LOG(ERROR, PROFILER) << "No samples found in file";
199         return false;
200     }
201 
202     if (!CollectModules()) {
203         LOG(ERROR, PROFILER) << "No modules found in file, names would not be resolved";
204         return false;
205     }
206 
207     if (!DumpModulesToFile(outname)) {
208         LOG(ERROR, PROFILER) << "Can't dump modules to file";
209         return false;
210     }
211 
212     return true;
213 }
214 
RunDumpTracesInCsvMode(const std::string & outname)215 bool AsptConverter::RunDumpTracesInCsvMode(const std::string &outname)
216 {
217     if (CollectTracesStats() == 0) {
218         LOG(ERROR, PROFILER) << "No samples found in file";
219         return false;
220     }
221 
222     if (!CollectModules()) {
223         LOG(ERROR, PROFILER) << "No modules found in file, names would not be resolved";
224     }
225 
226     if (!BuildModulesMap()) {
227         LOG(ERROR, PROFILER) << "Can't build modules map";
228         return false;
229     }
230 
231     BuildMethodsMap();
232     DumpResolvedTracesAsCSV(outname.c_str());
233 
234     return true;
235 }
236 
RunWithOptions(const Options & cliOptions)237 bool AsptConverter::RunWithOptions(const Options &cliOptions)
238 {
239     std::string outname = cliOptions.GetOutput();
240 
241     dumpType_ = GetDumpTypeFromOptions(cliOptions);
242     buildColdGraph_ = cliOptions.IsColdGraphEnable();
243 
244     if (cliOptions.IsSubstituteModuleDir()) {
245         substituteDirectories_ = {cliOptions.GetSubstituteSourceStr(), cliOptions.GetSubstituteDestinationStr()};
246         if (substituteDirectories_->source.size() != substituteDirectories_->destination.size()) {
247             LOG(FATAL, PROFILER) << "different number of strings in substitute option";
248         }
249     }
250 
251     if (cliOptions.IsDumpModules()) {
252         return RunDumpModulesMode(outname);
253     }
254 
255     return RunDumpTracesInCsvMode(outname);
256 }
257 
258 }  // namespace ark::tooling::sampler
259