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