1 /**
2 * Copyright (c) 2021-2022 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 panda::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 panda::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,panda::mem::InternalAllocatorPtr allocator,ClassLinker * linker,std::string const & cacheFileName)57 Service *CreateService(Config const *config, panda::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->config = config;
68 res->verifierService = allocator->New<VerifierService>(allocator, linker);
69 res->verifierService->Init();
70 res->debugCtx.SetConfig(&config->debugCfg);
71 return res;
72 }
73
DestroyService(Service * service,bool updateCacheFile)74 void DestroyService(Service *service, bool updateCacheFile)
75 {
76 if (service == nullptr) {
77 return;
78 }
79 VerificationResultCache::Destroy(updateCacheFile);
80 auto allocator = service->allocator;
81 service->verifierService->Destroy();
82 allocator->Delete(service->verifierService);
83 allocator->Delete(service);
84 }
85
GetServiceConfig(Service const * service)86 Config const *GetServiceConfig(Service const *service)
87 {
88 return service->config;
89 }
90
91 // Do we really need this public/private distinction here?
ToPublic(VerificationResultCache::Status status)92 inline Status ToPublic(VerificationResultCache::Status status)
93 {
94 switch (status) {
95 case VerificationResultCache::Status::OK:
96 return Status::OK;
97 case VerificationResultCache::Status::FAILED:
98 return Status::FAILED;
99 case VerificationResultCache::Status::UNKNOWN:
100 return Status::UNKNOWN;
101 default:
102 UNREACHABLE();
103 }
104 }
105
ReportStatus(Service const * service,Method const * method,std::string const & status)106 static void ReportStatus(Service const *service, Method const *method, std::string const &status)
107 {
108 if (service->config->opts.show.status) {
109 LOG(DEBUG, VERIFIER) << "Verification result for method " << method->GetFullName(true) << ": " << status;
110 }
111 }
112
VerifyClass(Class * clazz)113 static bool VerifyClass(Class *clazz)
114 {
115 auto *langPlugin = plugin::GetLanguagePlugin(clazz->GetSourceLang());
116 auto result = langPlugin->CheckClass(clazz);
117 if (!result.IsOk()) {
118 LOG(ERROR, VERIFIER) << result.msg << " " << clazz->GetName();
119 clazz->SetState(Class::State::ERRONEOUS);
120 return false;
121 }
122
123 Class *parent = clazz->GetBase();
124 if (parent != nullptr && parent->IsFinal()) {
125 LOG(ERROR, VERIFIER) << "Cannot inherit from final class: " << clazz->GetName() << "->" << parent->GetName();
126 clazz->SetState(Class::State::ERRONEOUS);
127 return false;
128 }
129
130 clazz->SetState(Class::State::VERIFIED);
131 return true;
132 }
133
Verify(Service * service,panda::Method * method,VerificationMode mode)134 Status Verify(Service *service, panda::Method *method, VerificationMode mode)
135 {
136 using VStage = Method::VerificationStage;
137 ASSERT(service != nullptr);
138
139 if (method->IsIntrinsic()) {
140 return Status::OK;
141 }
142
143 /*
144 * Races are possible where the same method gets simultaneously verified on different threads.
145 * But those are harmless, so we ignore them.
146 */
147 auto stage = method->GetVerificationStage();
148 if (stage == VStage::VERIFIED_OK) {
149 return Status::OK;
150 }
151 if (stage == VStage::VERIFIED_FAIL) {
152 return Status::FAILED;
153 }
154
155 if (!IsEnabled(mode)) {
156 ReportStatus(service, method, "SKIP");
157 return Status::OK;
158 }
159 if (method->GetInstructions() == nullptr) {
160 LOG(DEBUG, VERIFIER) << method->GetFullName(true) << " has no code, no meaningful verification";
161 return Status::OK;
162 }
163 service->debugCtx.AddMethod(*method, mode == VerificationMode::DEBUG);
164 if (service->debugCtx.SkipVerification(method->GetUniqId())) {
165 LOG(DEBUG, VERIFIER) << "Skipping verification of '" << method->GetFullName()
166 << "' according to verifier configuration";
167 return Status::OK;
168 }
169
170 auto uniqId = method->GetUniqId();
171 auto methodName = method->GetFullName();
172 if (VerificationResultCache::Enabled()) {
173 auto cachedStatus = ToPublic(VerificationResultCache::Check(uniqId));
174 if (cachedStatus != Status::UNKNOWN) {
175 LOG(DEBUG, VERIFIER) << "Verification result of method '" << methodName
176 << "' was cached: " << (cachedStatus == Status::OK ? "OK" : "FAIL");
177 return cachedStatus;
178 }
179 }
180
181 auto lang = method->GetClass()->GetSourceLang();
182 auto *processor = service->verifierService->GetProcessor(lang);
183
184 if (processor == nullptr) {
185 LOG(INFO, VERIFIER) << "Attempt to verify " << method->GetFullName(true)
186 << "after service started shutdown, ignoring";
187 return Status::FAILED;
188 }
189
190 auto const &methodOptions = service->config->opts.debug.GetMethodOptions();
191 auto const &verifMethodOptions = methodOptions[methodName];
192 LOG(DEBUG, VERIFIER) << "Verification config for '" << methodName << "': " << verifMethodOptions.GetName();
193 LOG(INFO, VERIFIER) << "Started verification of method '" << method->GetFullName(true) << "'";
194
195 // class verification can be called concurrently
196 if ((method->GetClass()->GetState() < Class::State::VERIFIED) && !VerifyClass(method->GetClass())) {
197 return Status::FAILED;
198 }
199 Job job {service, method, verifMethodOptions};
200 bool result = job.DoChecks(processor->GetTypeSystem());
201
202 LOG(DEBUG, VERIFIER) << "Verification result for '" << methodName << "': " << result;
203
204 service->verifierService->ReleaseProcessor(processor);
205
206 VerificationResultCache::CacheResult(uniqId, result);
207
208 if (result) {
209 method->SetVerificationStage(VStage::VERIFIED_OK);
210 ReportStatus(service, method, "OK");
211 return Status::OK;
212 }
213 method->SetVerificationStage(VStage::VERIFIED_FAIL);
214 ReportStatus(service, method, "FAIL");
215 return Status::FAILED;
216 }
217
218 } // namespace panda::verifier
219