• 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 
114     dumper->SetBuildSystemFrames(buildSystemFrames_);
115 
116     for (auto &[sample, count] : stackTraces_) {
117         ASSERT(sample.stackInfo.managedStackSize <= SampleInfo::StackInfo::MAX_STACK_DEPTH);
118         dumper->DumpTraces(sample, count);
119     }
120     return true;
121 }
122 
BuildMethodsMap()123 void AsptConverter::BuildMethodsMap()
124 {
125     for (const auto &pfPair : modulesMap_) {
126         const panda_file::File *pf = pfPair.second.get();
127         if (pf == nullptr) {
128             continue;
129         }
130         auto classesSpan = pf->GetClasses();
131         BuildMethodsMapHelper(pf, classesSpan);
132     }
133 }
134 
BuildMethodsMapHelper(const panda_file::File * pf,Span<const uint32_t> & classesSpan)135 void AsptConverter::BuildMethodsMapHelper(const panda_file::File *pf, Span<const uint32_t> &classesSpan)
136 {
137     for (auto id : classesSpan) {
138         if (pf->IsExternal(panda_file::File::EntityId(id))) {
139             continue;
140         }
141         panda_file::ClassDataAccessor cda(*pf, panda_file::File::EntityId(id));
142         cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
143             std::string methodName = utf::Mutf8AsCString(mda.GetName().data);
144             std::string className = utf::Mutf8AsCString(cda.GetDescriptor());
145             if (className[className.length() - 1] == ';') {
146                 className.pop_back();
147             }
148             std::string fullName = className + "::";
149             fullName += methodName;
150             methodsMap_[pf][mda.GetMethodId().GetOffset()] = std::move(fullName);
151         });
152     }
153 }
154 
DumpModulesToFile(const std::string & outname) const155 bool AsptConverter::DumpModulesToFile(const std::string &outname) const
156 {
157     std::ofstream out(outname, std::ios::binary);
158     if (!out) {
159         LOG(ERROR, PROFILER) << "Can't open " << outname;
160         out.close();
161         return false;
162     }
163 
164     for (auto &mdl : modules_) {
165         out << mdl.checksum << " " << mdl.pathname << "\n";
166     }
167 
168     if (out.fail()) {
169         LOG(ERROR, PROFILER) << "Failbit or the badbit (or both) was set";
170         out.close();
171         return false;
172     }
173 
174     return true;
175 }
176 
177 /* static */
GetDumpTypeFromOptions(const Options & cliOptions)178 DumpType AsptConverter::GetDumpTypeFromOptions(const Options &cliOptions)
179 {
180     const auto &dumpTypeStr = cliOptions.GetCsvTidSeparation();
181 
182     DumpType dumpType;
183     if (dumpTypeStr == "single-csv-single-tid") {
184         dumpType = DumpType::WITHOUT_THREAD_SEPARATION;
185     } else if (dumpTypeStr == "single-csv-multi-tid") {
186         dumpType = DumpType::THREAD_SEPARATION_BY_TID;
187     } else if (dumpTypeStr == "multi-csv") {
188         dumpType = DumpType::THREAD_SEPARATION_BY_CSV;
189     } else {
190         std::cerr << "unknown value of csv-tid-distribution option: '" << dumpTypeStr
191                   << "' single-csv-multi-tid will be set" << std::endl;
192         dumpType = DumpType::THREAD_SEPARATION_BY_TID;
193     }
194 
195     return dumpType;
196 }
197 
RunDumpModulesMode(const std::string & outname)198 bool AsptConverter::RunDumpModulesMode(const std::string &outname)
199 {
200     if (CollectTracesStats() == 0) {
201         LOG(ERROR, PROFILER) << "No samples found in file";
202         return false;
203     }
204 
205     if (!CollectModules()) {
206         LOG(ERROR, PROFILER) << "No modules found in file, names would not be resolved";
207         return false;
208     }
209 
210     if (!DumpModulesToFile(outname)) {
211         LOG(ERROR, PROFILER) << "Can't dump modules to file";
212         return false;
213     }
214 
215     return true;
216 }
217 
RunDumpTracesInCsvMode(const std::string & outname)218 bool AsptConverter::RunDumpTracesInCsvMode(const std::string &outname)
219 {
220     if (CollectTracesStats() == 0) {
221         LOG(ERROR, PROFILER) << "No samples found in file";
222         return false;
223     }
224 
225     if (!CollectModules()) {
226         LOG(ERROR, PROFILER) << "No modules found in file, names would not be resolved";
227     }
228 
229     if (!BuildModulesMap()) {
230         LOG(ERROR, PROFILER) << "Can't build modules map";
231         return false;
232     }
233 
234     BuildMethodsMap();
235     DumpResolvedTracesAsCSV(outname.c_str());
236 
237     return true;
238 }
239 
RunWithOptions(const Options & cliOptions)240 bool AsptConverter::RunWithOptions(const Options &cliOptions)
241 {
242     const auto &outname = cliOptions.GetOutput();
243 
244     dumpType_ = GetDumpTypeFromOptions(cliOptions);
245     buildColdGraph_ = cliOptions.IsColdGraphEnable();
246     buildSystemFrames_ = cliOptions.IsDumpSystemFrames();
247 
248     if (cliOptions.IsSubstituteModuleDir()) {
249         substituteDirectories_ = {cliOptions.GetSubstituteSourceStr(), cliOptions.GetSubstituteDestinationStr()};
250         if (substituteDirectories_->source.size() != substituteDirectories_->destination.size()) {
251             LOG(FATAL, PROFILER) << "different number of strings in substitute option";
252         }
253     }
254 
255     if (cliOptions.IsDumpModules()) {
256         return RunDumpModulesMode(outname);
257     }
258 
259     return RunDumpTracesInCsvMode(outname);
260 }
261 
262 }  // namespace ark::tooling::sampler
263