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