• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2025 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 "public.h"
17 #include "include/runtime_options.h"
18 #include "public_internal.h"
19 #include "runtime/include/mem/allocator.h"
20 #include "verification/config/config_load.h"
21 #include "verification/config/context/context.h"
22 #include "verification/cache/results_cache.h"
23 #include "verification/jobs/service.h"
24 #include "verification/jobs/job.h"
25 
26 namespace ark::verifier {
27 
NewConfig()28 Config *NewConfig()
29 {
30     auto result = new Config;
31     result->opts.Initialize();
32     return result;
33 }
34 
LoadConfigFile(Config * config,std::string_view configFileName)35 bool LoadConfigFile(Config *config, std::string_view configFileName)
36 {
37     return ark::verifier::config::LoadConfig(config, configFileName);
38 }
39 
DestroyConfig(Config * config)40 void DestroyConfig(Config *config)
41 {
42     config->opts.Destroy();
43     delete config;
44 }
45 
IsEnabled(Config const * config)46 bool IsEnabled(Config const *config)
47 {
48     ASSERT(config != nullptr);
49     return config->opts.IsEnabled();
50 }
51 
IsOnlyVerify(Config const * config)52 bool IsOnlyVerify(Config const *config)
53 {
54     ASSERT(config != nullptr);
55     return config->opts.IsOnlyVerify();
56 }
57 
CreateService(Config const * config,ark::mem::InternalAllocatorPtr allocator,ClassLinker * linker,std::string const & cacheFileName)58 Service *CreateService(Config const *config, ark::mem::InternalAllocatorPtr allocator, ClassLinker *linker,
59                        std::string const &cacheFileName)
60 {
61     if (!cacheFileName.empty()) {
62         VerificationResultCache::Initialize(cacheFileName);
63     }
64     auto res = allocator->New<Service>();
65     if (res == nullptr) {
66         LOG(ERROR, VERIFIER) << "Verifier Error: res is nullptr";
67         return nullptr;
68     }
69     res->config = config;
70     res->classLinker = linker;
71     res->allocator = allocator;
72     res->verifierService = allocator->New<VerifierService>(allocator, linker);
73     if (res->verifierService == nullptr) {
74         LOG(ERROR, VERIFIER) << "Verifier Error: res->verifierService is nullptr";
75         return nullptr;
76     }
77     res->verifierService->Init();
78     res->debugCtx.SetConfig(&config->debugCfg);
79     return res;
80 }
81 
DestroyService(Service * service,bool updateCacheFile)82 void DestroyService(Service *service, bool updateCacheFile)
83 {
84     if (service == nullptr) {
85         return;
86     }
87     VerificationResultCache::Destroy(updateCacheFile);
88     auto allocator = service->allocator;
89     service->verifierService->Destroy();
90     allocator->Delete(service->verifierService);
91     allocator->Delete(service);
92 }
93 
GetServiceConfig(Service const * service)94 Config const *GetServiceConfig(Service const *service)
95 {
96     return service->config;
97 }
98 
99 // Do we really need this public/private distinction here?
ToPublic(VerificationResultCache::Status status)100 inline Status ToPublic(VerificationResultCache::Status status)
101 {
102     switch (status) {
103         case VerificationResultCache::Status::OK:
104             return Status::OK;
105         case VerificationResultCache::Status::FAILED:
106             return Status::FAILED;
107         case VerificationResultCache::Status::UNKNOWN:
108             return Status::UNKNOWN;
109         default:
110             UNREACHABLE();
111     }
112 }
113 
ReportStatus(Service const * service,Method const * method,std::string const & status)114 static void ReportStatus(Service const *service, Method const *method, std::string const &status)
115 {
116     if (service->config->opts.show.status) {
117         LOG(DEBUG, VERIFIER) << "Verification result for method " << method->GetFullName(true) << ": " << status;
118     }
119 }
120 
VerifyClass(Class * clazz)121 static bool VerifyClass(Class *clazz)
122 {
123     auto *langPlugin = plugin::GetLanguagePlugin(clazz->GetSourceLang());
124     auto result = langPlugin->CheckClass(clazz);
125     if (!result.IsOk()) {
126         LOG(ERROR, VERIFIER) << result.msg << " " << clazz->GetName();
127         clazz->SetState(Class::State::ERRONEOUS);
128         return false;
129     }
130 
131     Class *parent = clazz->GetBase();
132     if (parent != nullptr && parent->IsFinal()) {
133         LOG(ERROR, VERIFIER) << "Cannot inherit from final class: " << clazz->GetName() << "->" << parent->GetName();
134         clazz->SetState(Class::State::ERRONEOUS);
135         return false;
136     }
137 
138     clazz->SetState(Class::State::VERIFIED);
139     return true;
140 }
141 
CheckBeforeVerification(Service * service,ark::Method * method,VerificationMode mode)142 static std::optional<Status> CheckBeforeVerification(Service *service, ark::Method *method, VerificationMode mode)
143 {
144     using VStage = Method::VerificationStage;
145     if (method->IsIntrinsic()) {
146         return Status::OK;
147     }
148 
149     /*
150      *  Races are possible where the same method gets simultaneously verified on different threads.
151      *  But those are harmless, so we ignore them.
152      */
153     auto stage = method->GetVerificationStage();
154     if (stage == VStage::VERIFIED_OK) {
155         return Status::OK;
156     }
157     if (stage == VStage::VERIFIED_FAIL) {
158         return Status::FAILED;
159     }
160 
161     if (!verifier::IsEnabled(mode)) {
162         ReportStatus(service, method, "SKIP");
163         return Status::OK;
164     }
165     if (method->GetInstructions() == nullptr) {
166         LOG(DEBUG, VERIFIER) << method->GetFullName(true) << " has no code, no meaningful verification";
167         return Status::OK;
168     }
169     service->debugCtx.AddMethod(*method, mode == VerificationMode::DEBUG);
170     if (service->debugCtx.SkipVerification(method->GetUniqId())) {
171         LOG(DEBUG, VERIFIER) << "Skipping verification of '" << method->GetFullName()
172                              << "' according to verifier configuration";
173         return Status::OK;
174     }
175 
176     auto uniqId = method->GetUniqId();
177     auto methodName = method->GetFullName();
178     if (VerificationResultCache::Enabled()) {
179         auto cachedStatus = ToPublic(VerificationResultCache::Check(uniqId));
180         if (cachedStatus != Status::UNKNOWN) {
181             LOG(DEBUG, VERIFIER) << "Verification result of method '" << methodName
182                                  << "' was cached: " << (cachedStatus == Status::OK ? "OK" : "FAIL");
183             return cachedStatus;
184         }
185     }
186 
187     return std::nullopt;
188 }
189 
Verify(Service * service,ark::Method * method,VerificationMode mode)190 Status Verify(Service *service, ark::Method *method, VerificationMode mode)
191 {
192     using VStage = Method::VerificationStage;
193     ASSERT(service != nullptr);
194 
195     auto status = CheckBeforeVerification(service, method, mode);
196     if (status) {
197         return status.value();
198     }
199 
200     auto uniqId = method->GetUniqId();
201     auto methodName = method->GetFullName();
202 
203     auto lang = method->GetClass()->GetSourceLang();
204     auto *processor = service->verifierService->GetProcessor(lang);
205 
206     if (processor == nullptr) {
207         LOG(INFO, VERIFIER) << "Attempt to  verify " << method->GetFullName(true)
208                             << "after service started shutdown, ignoring";
209         return Status::FAILED;
210     }
211 
212     auto const &methodOptions = service->config->opts.debug.GetMethodOptions();
213     auto const &verifMethodOptions = methodOptions[methodName];
214     LOG(DEBUG, VERIFIER) << "Verification config for '" << methodName << "': " << verifMethodOptions.GetName();
215     LOG(INFO, VERIFIER) << "Started verification of method '" << method->GetFullName(true) << "'";
216 
217     // class verification can be called concurrently
218     if ((method->GetClass()->GetState() < Class::State::VERIFIED) && !VerifyClass(method->GetClass())) {
219         return Status::FAILED;
220     }
221     Job job {service, method, verifMethodOptions};
222     bool result = job.DoChecks(processor->GetTypeSystem());
223 
224     LOG(DEBUG, VERIFIER) << "Verification result for '" << methodName << "': " << result;
225 
226     service->verifierService->ReleaseProcessor(processor);
227 
228     VerificationResultCache::CacheResult(uniqId, result);
229 
230     if (result) {
231         method->SetVerificationStage(VStage::VERIFIED_OK);
232         ReportStatus(service, method, "OK");
233         return Status::OK;
234     }
235     method->SetVerificationStage(VStage::VERIFIED_FAIL);
236     ReportStatus(service, method, "FAIL");
237     return Status::FAILED;
238 }
239 
IsEnabled(VerificationMode mode)240 bool IsEnabled(VerificationMode mode)
241 {
242     return mode != VerificationMode::DISABLED;
243 }
244 
VerificationModeFromString(const std::string & mode)245 VerificationMode VerificationModeFromString(const std::string &mode)
246 {
247     if (mode == "on-the-fly") {
248         return VerificationMode::ON_THE_FLY;
249     }
250     if (mode == "ahead-of-time") {
251         return VerificationMode::AHEAD_OF_TIME;
252     }
253     if (mode == "debug") {
254         return VerificationMode::DEBUG;
255     }
256     return VerificationMode::DISABLED;
257 }
258 
259 }  // namespace ark::verifier
260