1 /*
2 * Copyright (c) 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 <algorithm>
17 #include <cassert>
18 #include <sstream>
19 #include "libabckit/include/c/abckit.h"
20 #include "api_scanner.h"
21 #include "helpers/helpers.h"
22
ApiScanner(enum AbckitApiVersion version,AbckitFile * file,std::vector<ApiInfo> apiList)23 ApiScanner::ApiScanner(enum AbckitApiVersion version, AbckitFile *file, std::vector<ApiInfo> apiList)
24 : file_(file), apiList_(std::move(apiList))
25 {
26 impl_ = AbckitGetApiImpl(version);
27 implI_ = AbckitGetInspectApiImpl(version);
28 implG_ = AbckitGetGraphApiImpl(version);
29 dynG_ = AbckitGetIsaApiDynamicImpl(version);
30 vh_ = VisitHelper(file_, impl_, implI_, implG_, dynG_);
31 }
32
CollectApiUsages()33 void ApiScanner::CollectApiUsages()
34 {
35 vh_.EnumerateModules([&](AbckitCoreModule *mod) {
36 SuspectsMap suspects;
37 GetSuspects(mod, suspects);
38 if (suspects.empty()) {
39 return;
40 }
41 vh_.EnumerateModuleFunctions(mod, [&](AbckitCoreFunction *method) { CollectUsageInMethod(method, suspects); });
42 });
43 }
44
GetSuspects(AbckitCoreModule * mod,SuspectsMap & suspects)45 bool ApiScanner::GetSuspects(AbckitCoreModule *mod, SuspectsMap &suspects)
46 {
47 vh_.EnumerateModuleImports(mod, [&](AbckitCoreImportDescriptor *id) {
48 auto importName = vh_.GetString(implI_->importDescriptorGetName(id));
49 auto *importedModule = implI_->importDescriptorGetImportedModule(id);
50 auto source = vh_.GetString(implI_->moduleGetName(importedModule));
51 for (size_t i = 0; i < apiList_.size(); ++i) {
52 const auto api = apiList_[i];
53 if (source != api.source) {
54 continue;
55 }
56 if (importName == api.objName) {
57 suspects.emplace(id, i);
58 }
59 }
60 });
61 return !suspects.empty();
62 }
63
IsLoadApi(AbckitCoreImportDescriptor * id,size_t apiIndex,AbckitInst * inst)64 bool ApiScanner::IsLoadApi(AbckitCoreImportDescriptor *id, size_t apiIndex, AbckitInst *inst)
65 {
66 // find the following pattern:
67 // 1. ldExternalModuleVar (import {ApiNamespace} from "./modules/myApi")
68 // 2. ldObjByName v1, "foo" (api.propName)
69 // or
70 // 1. ldExternalModuleVar (import {bar} from "./modules/myApi")
71
72 if (dynG_->iGetOpcode(inst) != ABCKIT_ISA_API_DYNAMIC_OPCODE_LDEXTERNALMODULEVAR) {
73 return false;
74 }
75
76 if (dynG_->iGetImportDescriptor(inst) != id) {
77 return false;
78 }
79
80 const auto &prop = apiList_[apiIndex].propName;
81 if (prop.empty()) {
82 return true;
83 }
84
85 bool found = false;
86 vh_.EnumerateInstUsers(inst, [&](AbckitInst *user) {
87 if (!found && dynG_->iGetOpcode(user) == ABCKIT_ISA_API_DYNAMIC_OPCODE_LDOBJBYNAME) {
88 auto str = vh_.GetString(implG_->iGetString(user));
89 found = str == prop;
90 }
91 });
92
93 return found;
94 }
95
GetMethodName(AbckitCoreFunction * method)96 std::string ApiScanner::GetMethodName(AbckitCoreFunction *method)
97 {
98 auto mname = implI_->functionGetName(method);
99 auto methodName = std::string(libabckit::test::helpers::AbckitStringToString(mname));
100 auto *cls = implI_->functionGetParentClass(method);
101 if (cls == nullptr) {
102 return methodName;
103 }
104 auto cname = implI_->classGetName(cls);
105 auto className = std::string(libabckit::test::helpers::AbckitStringToString(cname));
106 return className + '.' + methodName;
107 }
108
CollectUsageInMethod(AbckitCoreFunction * method,SuspectsMap & suspects)109 void ApiScanner::CollectUsageInMethod(AbckitCoreFunction *method, SuspectsMap &suspects)
110 {
111 auto methodName = GetMethodName(method);
112 auto *sourceRaw = implI_->moduleGetName(implI_->functionGetModule(method));
113 auto source = vh_.GetString(sourceRaw);
114 UsageInfo usage = {source.data(), methodName, 0};
115 vh_.EnumerateFunctionInsts(method, [&](AbckitInst *inst) {
116 for (const auto &[id, apiIndex] : suspects) {
117 if (IsLoadApi(id, apiIndex, inst)) {
118 usage.idx = apiIndex;
119 AddApiUsage(apiIndex, usage);
120 }
121 }
122 });
123 }
124
GetQuatatedStr(const std::string & str)125 static std::string GetQuatatedStr(const std::string &str)
126 {
127 const static std::string QUOTATION = "\"";
128 return QUOTATION + str + QUOTATION;
129 }
130
ApiUsageMapGetString() const131 std::string ApiScanner::ApiUsageMapGetString() const
132 {
133 std::stringstream ss;
134 const static std::string INDENT_1 = " ";
135 const static std::string INDENT_2 = INDENT_1 + INDENT_1;
136 const static std::string INDENT_3 = INDENT_1 + INDENT_2;
137 const static std::string INDENT_4 = INDENT_1 + INDENT_3;
138 const static std::string LEFT_BRACKET = "{";
139 const static std::string RIGHT_BRACKET = "}";
140 const static std::string COLON = ": ";
141 const static std::string COMMA = ",";
142 const static std::string APIINFO = "APIInfo";
143 const static std::string APIINFO_SOURCE = "source";
144 const static std::string APIINFO_OBJNAME = "objName";
145 const static std::string APIINFO_PROPNAME = "propName";
146 const static std::string USAGES = "Usages";
147 const static std::string USAGES_SOURCE = "source";
148 const static std::string USAGES_FUNCNAME = "funcName";
149 const static std::string API_IDX = "indexOfAPI";
150
151 LIBABCKIT_LOG(DEBUG) << LEFT_BRACKET << std::endl;
152 for (const auto &[index, usageInfos] : apiUsages_) {
153 LIBABCKIT_LOG(DEBUG) << INDENT_1 << LEFT_BRACKET << std::endl;
154
155 if (usageInfos.empty()) {
156 continue;
157 }
158 const auto &apiInfo = apiList_[index];
159 LIBABCKIT_LOG(DEBUG) << INDENT_2 << APIINFO << COLON << LEFT_BRACKET << std::endl;
160 LIBABCKIT_LOG(DEBUG) << INDENT_3 << APIINFO_SOURCE << COLON << GetQuatatedStr(apiInfo.source) << COMMA
161 << std::endl;
162 LIBABCKIT_LOG(DEBUG) << INDENT_3 << APIINFO_OBJNAME << COLON << GetQuatatedStr(apiInfo.objName) << COMMA
163 << std::endl;
164 LIBABCKIT_LOG(DEBUG) << INDENT_3 << APIINFO_PROPNAME << COLON << GetQuatatedStr(apiInfo.propName) << COMMA
165 << std::endl;
166 LIBABCKIT_LOG(DEBUG) << INDENT_2 << RIGHT_BRACKET << COMMA << std::endl;
167
168 LIBABCKIT_LOG(DEBUG) << INDENT_2 << USAGES << COLON << LEFT_BRACKET << std::endl;
169 for (const auto &usage : usageInfos) {
170 LIBABCKIT_LOG(DEBUG) << INDENT_3 << LEFT_BRACKET << std::endl;
171 LIBABCKIT_LOG(DEBUG) << INDENT_4 << USAGES_SOURCE << COLON << GetQuatatedStr(usage.source) << COMMA
172 << std::endl;
173 LIBABCKIT_LOG(DEBUG) << INDENT_4 << USAGES_FUNCNAME << COLON << GetQuatatedStr(usage.funcName) << COMMA
174 << std::endl;
175 LIBABCKIT_LOG(DEBUG) << INDENT_4 << API_IDX << COLON << usage.idx << COMMA << std::endl;
176 LIBABCKIT_LOG(DEBUG) << INDENT_3 << RIGHT_BRACKET << COMMA << std::endl;
177 }
178 LIBABCKIT_LOG(DEBUG) << INDENT_2 << RIGHT_BRACKET << std::endl;
179
180 LIBABCKIT_LOG(DEBUG) << INDENT_1 << RIGHT_BRACKET << COMMA << std::endl;
181 }
182 LIBABCKIT_LOG(DEBUG) << RIGHT_BRACKET << std::endl;
183
184 return ss.str();
185 }
186
IsUsagesEqual(const std::vector<UsageInfo> & otherUsages) const187 bool ApiScanner::IsUsagesEqual(const std::vector<UsageInfo> &otherUsages) const
188 {
189 for (auto &otherUsage : otherUsages) {
190 auto usages = apiUsages_.at(otherUsage.idx);
191 auto iter = std::find_if(usages.begin(), usages.end(), [&otherUsage](const UsageInfo &usage) {
192 return (otherUsage.funcName == usage.funcName) && (otherUsage.source == usage.source) &&
193 (otherUsage.idx == usage.idx);
194 });
195 if (iter == usages.end()) {
196 return false;
197 }
198 }
199 return true;
200 }
201