• 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 "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