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 "libpandabase/os/native_stack.h"
17 #include "monitor.h"
18 #include "os/mutex.h"
19 #include "runtime/include/runtime.h"
20 #include "runtime/include/thread_scopes.h"
21 #include "utils/logger.h"
22 #include "utils/span.h"
23 #include "verification/plugins.h"
24 #include "verification/util/is_system.h"
25 #include "verification/util/optional_ref.h"
26 #include "generated/base_options.h"
27
28 // generated
29 #include "ark_version.h"
30 #include "generated/verifier_options_gen.h"
31
32 #include <csignal>
33 #include <chrono>
34 #include <thread>
35
36 namespace ark::verifier {
37
38 size_t const MAX_THREADS = 64;
39
Worker(PandaDeque<Method * > * queue,os::memory::Mutex * lock,size_t threadNum,std::atomic<bool> * result)40 void Worker(PandaDeque<Method *> *queue, os::memory::Mutex *lock, size_t threadNum, std::atomic<bool> *result)
41 {
42 LOG(DEBUG, VERIFIER) << "Verifier thread " << threadNum << " starting";
43 {
44 std::stringstream ss;
45 ss << "Verifier #" << threadNum;
46 if (os::thread::SetThreadName(os::thread::GetNativeHandle(), ss.str().c_str()) != 0) {
47 LOG(ERROR, VERIFIER) << "Failed to set worker thread name " << ss.str();
48 }
49 }
50
51 auto *service = Runtime::GetCurrent()->GetVerifierService();
52 auto options = Runtime::GetCurrent()->GetOptions();
53 auto mode = options.GetVerificationMode();
54
55 ManagedThread *thread = nullptr;
56 panda_file::SourceLang currentLang = panda_file::SourceLang::INVALID;
57 for (;;) {
58 Method *method;
59 {
60 os::memory::LockHolder lh {*lock};
61 if (queue->empty()) {
62 break;
63 }
64 method = queue->front();
65 queue->pop_front();
66 }
67 auto methodLang = method->GetClass()->GetSourceLang();
68 if (methodLang != currentLang) {
69 if (thread != nullptr) {
70 plugin::GetLanguagePlugin(currentLang)->DestroyManagedThread(thread);
71 }
72 thread = plugin::GetLanguagePlugin(methodLang)->CreateManagedThread();
73 currentLang = methodLang;
74 }
75 if (ark::verifier::Verify(service, method, mode) != Status::OK) {
76 *result = false;
77 LOG(ERROR, VERIFIER) << "Error: method " << method->GetFullName(true) << " failed to verify";
78 }
79 }
80 if (thread != nullptr) {
81 plugin::GetLanguagePlugin(currentLang)->DestroyManagedThread(thread);
82 }
83 LOG(DEBUG, VERIFIER) << "Verifier thread " << threadNum << " finishing";
84 }
85
RunVerifier(const Options & cliOptions)86 bool RunVerifier(const Options &cliOptions)
87 {
88 auto isPerfMeasure = cliOptions.IsPerfMeasure();
89 std::chrono::steady_clock::time_point begin;
90 std::chrono::steady_clock::time_point end;
91
92 PandaDeque<Method *> queue;
93 os::memory::Mutex lock;
94
95 auto &runtime = *Runtime::GetCurrent();
96 auto &classLinker = *runtime.GetClassLinker();
97
98 const std::vector<std::string> &classNames = cliOptions.GetClasses();
99 const std::vector<std::string> &methodNames = cliOptions.GetMethods();
100
101 std::atomic<bool> result = true;
102
103 PandaUnorderedSet<Method *> methodsSet;
104
105 auto enqueueMethod = [&methodsSet, &queue](Method *method) {
106 if (methodsSet.count(method) == 0) {
107 queue.push_back(method);
108 methodsSet.insert(method);
109 }
110 };
111
112 bool verifyLibraries = runtime.GetOptions().IsVerifyRuntimeLibraries();
113
114 auto enqueueClass = [&verifyLibraries, &enqueueMethod](const Class &klass) {
115 if (!verifyLibraries && IsSystemClass(&klass)) {
116 LOG(INFO, VERIFIER) << klass.GetName() << " is a system class, skipping";
117 return;
118 }
119 LOG(INFO, VERIFIER) << "Begin verification of class " << klass.GetName();
120 for (auto &method : klass.GetMethods()) {
121 enqueueMethod(&method);
122 }
123 };
124
125 // we need ScopedManagedCodeThread for the verifier since it can allocate objects
126 {
127 ScopedManagedCodeThread managedObjThread(ManagedThread::GetCurrent());
128 if (classNames.empty() && methodNames.empty()) {
129 PandaVector<const panda_file::File *> pfiles;
130 classLinker.EnumeratePandaFiles([&pfiles](const panda_file::File &file) {
131 pfiles.push_back(&file);
132 return true;
133 });
134
135 auto handleFile = [&result, &classLinker, &enqueueClass](const panda_file::File &file) {
136 LOG(INFO, VERIFIER) << "Processing file" << file.GetFilename();
137 for (auto id : file.GetClasses()) {
138 panda_file::File::EntityId entityId {id};
139 if (!file.IsExternal(entityId)) {
140 auto optLang = panda_file::ClassDataAccessor {file, entityId}.GetSourceLang();
141 if (optLang.has_value() && !IsValidSourceLang(optLang.value())) {
142 LOG(ERROR, VERIFIER) << "Unknown SourceLang";
143 result = false;
144 return false;
145 }
146 ClassLinkerExtension *ext =
147 classLinker.GetExtension(optLang.value_or(panda_file::SourceLang::PANDA_ASSEMBLY));
148 if (ext == nullptr) {
149 LOG(ERROR, VERIFIER) << "Error: Class Linker Extension failed to initialize";
150 result = false;
151 return false;
152 }
153 const Class *klass = ext->GetClass(file, entityId);
154
155 if (klass != nullptr) {
156 enqueueClass(*klass);
157 }
158 }
159 }
160 return true;
161 };
162
163 for (auto pf : pfiles) {
164 if (!handleFile(*pf)) {
165 break;
166 }
167 }
168
169 if (verifyLibraries) {
170 classLinker.EnumerateBootPandaFiles(handleFile);
171 } else if (runtime.GetPandaFiles().empty()) {
172 // in this case the last boot-panda-file and only it is actually not a system file and should be
173 // verified
174 OptionalConstRef<panda_file::File> file;
175 classLinker.EnumerateBootPandaFiles([&file](const panda_file::File &pf) {
176 file = std::cref(pf);
177 return true;
178 });
179 if (file.HasRef()) {
180 handleFile(*file);
181 } else {
182 LOG(ERROR, VERIFIER) << "No files given to verify";
183 }
184 }
185 } else {
186 PandaUnorderedMap<std::string, Class *> classesByName;
187 ClassLinkerContext *ctx =
188 classLinker.GetExtension(runtime.GetLanguageContext(runtime.GetRuntimeType()))->GetBootContext();
189
190 auto getClassByName = [&classesByName, &classLinker, &ctx,
191 &result](const std::string &className) -> Class * {
192 auto it = classesByName.find(className);
193 if (it != classesByName.end()) {
194 return it->second;
195 }
196
197 PandaString descriptor;
198 const uint8_t *classNameBytes =
199 ClassHelper::GetDescriptor(utf::CStringAsMutf8(className.c_str()), &descriptor);
200 Class *klass = classLinker.GetClass(classNameBytes, true, ctx);
201 if (klass == nullptr) {
202 LOG(ERROR, VERIFIER) << "Error: Cannot resolve class with name " << className;
203 result = false;
204 }
205
206 classesByName.emplace(className, klass);
207 return klass;
208 };
209
210 for (const auto &className : classNames) {
211 Class *klass = getClassByName(className);
212 // the bad case is already handled in get_class_by_name
213 if (klass != nullptr) {
214 enqueueClass(*klass);
215 }
216 }
217
218 for (const std::string &fqMethodName : methodNames) {
219 size_t pos = fqMethodName.find_last_of("::");
220 if (pos == std::string::npos) {
221 LOG(ERROR, VERIFIER) << "Error: Fully qualified method name must contain '::', was "
222 << fqMethodName;
223 result = false;
224 break;
225 }
226 std::string className = fqMethodName.substr(0, pos - 1);
227 std::string_view unqualifiedMethodName = std::string_view(fqMethodName).substr(pos + 1);
228 if (std::find(classNames.begin(), classNames.end(), className) != classNames.end()) {
229 // this method was already verified while enumerating class_names
230 continue;
231 }
232 Class *klass = getClassByName(className);
233 if (klass != nullptr) {
234 bool methodFound = false;
235 for (auto &method : klass->GetMethods()) {
236 const char *nameData = utf::Mutf8AsCString(method.GetName().data);
237 if (std::string_view(nameData) == unqualifiedMethodName) {
238 methodFound = true;
239 LOG(INFO, VERIFIER) << "Verification of method '" << fqMethodName << "'";
240 enqueueMethod(&method);
241 }
242 }
243 if (!methodFound) {
244 LOG(ERROR, VERIFIER) << "Error: Cannot resolve method with name " << unqualifiedMethodName
245 << " in class " << className;
246 result = false;
247 }
248 }
249 }
250 }
251 }
252
253 if (isPerfMeasure) {
254 begin = std::chrono::steady_clock::now();
255 }
256
257 size_t nthreads = runtime.GetOptions().GetVerificationThreads();
258 std::vector<std::thread *> threads;
259 for (size_t i = 0; i < nthreads; i++) {
260 auto *worker = runtime.GetInternalAllocator()->New<std::thread>(Worker, &queue, &lock, i, &result);
261 threads.push_back(worker);
262 }
263
264 for (auto *thr : threads) {
265 thr->join();
266 runtime.GetInternalAllocator()->Delete(thr);
267 }
268
269 if (isPerfMeasure) {
270 end = std::chrono::steady_clock::now();
271 std::cout << "Verification time = "
272 << std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count() << " us" << std::endl;
273 }
274
275 return result;
276 }
277
PrintHelp(const PandArgParser & paParser)278 void PrintHelp(const PandArgParser &paParser)
279 {
280 std::string error = paParser.GetErrorString();
281 if (!error.empty()) {
282 error += "\n\n";
283 }
284 std::cerr << error << "Usage: verifier [option...] [file]\n"
285 << "Verify specified Panda files (given by file and --panda-files) "
286 << "or certain classes/methods in them.\n\n"
287 << paParser.GetHelpString() << std::endl;
288 }
289
RunThreads(Options & cliOptions,RuntimeOptions & runtimeOptions)290 static int RunThreads(Options &cliOptions, RuntimeOptions &runtimeOptions)
291 {
292 uint32_t threads = cliOptions.GetThreads();
293 if (threads == 0) {
294 threads = std::thread::hardware_concurrency();
295 // hardware_concurrency can return 0 if the value is not computable or well defined
296 if (threads == 0) {
297 threads = 1;
298 } else if (threads > MAX_THREADS) {
299 threads = MAX_THREADS;
300 }
301 }
302 runtimeOptions.SetVerificationThreads(threads);
303
304 if (!Runtime::Create(runtimeOptions)) {
305 std::cerr << "Error: cannot create runtime" << std::endl;
306 return -1;
307 }
308
309 int ret = RunVerifier(cliOptions) ? 0 : -1;
310
311 if (cliOptions.IsPrintMemoryStatistics()) {
312 std::cout << Runtime::GetCurrent()->GetMemoryStatistics();
313 }
314 if (!Runtime::Destroy()) {
315 std::cerr << "Error: cannot destroy runtime" << std::endl;
316 return -1;
317 }
318 return ret;
319 }
320
Run(Options & cliOptions,RuntimeOptions & runtimeOptions,base_options::Options & baseOptions,PandArg<std::string> & file)321 static int Run(Options &cliOptions, RuntimeOptions &runtimeOptions, base_options::Options &baseOptions,
322 PandArg<std::string> &file)
323 {
324 auto bootPandaFiles = cliOptions.GetBootPandaFiles();
325 auto pandaFiles = cliOptions.GetPandaFiles();
326 std::string mainFileName = file.GetValue();
327 if (!mainFileName.empty()) {
328 if (pandaFiles.empty()) {
329 bootPandaFiles.push_back(mainFileName);
330 } else {
331 auto foundIter = std::find_if(pandaFiles.begin(), pandaFiles.end(),
332 [&mainFileName](auto &fileName) { return mainFileName == fileName; });
333 if (foundIter == pandaFiles.end()) {
334 pandaFiles.push_back(mainFileName);
335 }
336 }
337 }
338
339 runtimeOptions.SetBootPandaFiles(bootPandaFiles);
340 runtimeOptions.SetPandaFiles(pandaFiles);
341 runtimeOptions.SetLoadRuntimes(cliOptions.GetLoadRuntimes());
342 runtimeOptions.SetGcType(cliOptions.GetGcType());
343
344 baseOptions.SetLogComponents(cliOptions.GetLogComponents());
345 baseOptions.SetLogLevel(cliOptions.GetLogLevel());
346 baseOptions.SetLogStream(cliOptions.GetLogStream());
347 baseOptions.SetLogFile(cliOptions.GetLogFile());
348 Logger::Initialize(baseOptions);
349
350 runtimeOptions.SetLimitStandardAlloc(cliOptions.IsLimitStandardAlloc());
351 runtimeOptions.SetInternalAllocatorType(cliOptions.GetInternalAllocatorType());
352 runtimeOptions.SetInternalMemorySizeLimit(cliOptions.GetInternalMemorySizeLimit());
353
354 runtimeOptions.SetVerificationMode(cliOptions.IsDebugMode() ? VerificationMode::DEBUG
355 : VerificationMode::AHEAD_OF_TIME);
356 runtimeOptions.SetVerificationConfigFile(cliOptions.GetConfigFile());
357 runtimeOptions.SetVerificationCacheFile(cliOptions.GetCacheFile());
358 runtimeOptions.SetVerificationUpdateCache(cliOptions.IsUpdateCache());
359 runtimeOptions.SetVerifyRuntimeLibraries(cliOptions.IsVerifyRuntimeLibraries());
360 return RunThreads(cliOptions, runtimeOptions);
361 }
362
Main(int argc,const char ** argv)363 int Main(int argc, const char **argv)
364 {
365 Span<const char *> sp(argv, argc);
366 RuntimeOptions runtimeOptions(sp[0]);
367 Options cliOptions(sp[0]);
368 PandArgParser paParser;
369 base_options::Options baseOptions("");
370
371 PandArg<bool> help("help", false, "Print this message and exit");
372 PandArg<bool> options("options", false, "Print verifier options");
373 // tail argument
374 PandArg<std::string> file("file", "", "path to pandafile");
375
376 cliOptions.AddOptions(&paParser);
377
378 paParser.Add(&help);
379 paParser.Add(&options);
380 paParser.PushBackTail(&file);
381 paParser.EnableTail();
382
383 if (!paParser.Parse(argc, argv)) {
384 PrintHelp(paParser);
385 return 1;
386 }
387
388 if (runtimeOptions.IsVersion()) {
389 PrintPandaVersion();
390 return 0;
391 }
392
393 if (help.GetValue()) {
394 PrintHelp(paParser);
395 return 0;
396 }
397
398 if (options.GetValue()) {
399 std::cout << paParser.GetRegularArgs() << std::endl;
400 return 0;
401 }
402
403 auto cliOptionsErr = cliOptions.Validate();
404 if (cliOptionsErr) {
405 std::cerr << "Error: " << cliOptionsErr.value().GetMessage() << std::endl;
406 return 1;
407 }
408
409 return Run(cliOptions, runtimeOptions, baseOptions, file);
410 }
411
412 } // namespace ark::verifier
413
main(int argc,const char ** argv)414 int main(int argc, const char **argv)
415 {
416 return ark::verifier::Main(argc, argv);
417 }
418