void operator()() { if (work.empty()) return; int specimen_id = work.front().specimen_id; // Database connections don't survive over fork() according to SqLite and PostgreSQL documentation, so open it again SqlDatabase::TransactionPtr tx = SqlDatabase::Connection::create(databaseUrl)->transaction(); OutputGroups ogroups; // do not load from database (that might take a very long time) if (opt.verbosity>=LACONIC) { if (opt.verbosity>=EFFUSIVE) std::cerr <<argv0 <<": " <<std::string(100, '#') <<"\n"; std::cerr <<argv0 <<": processing binary specimen \"" <<files.name(specimen_id) <<"\"\n"; } // Parse the specimen SgProject *project = files.load_ast(tx, specimen_id); if (!project) project = open_specimen(tx, files, specimen_id, argv0); if (!project) { std::cerr <<argv0 <<": problems loading specimen\n"; exit(1); } // Get list of specimen functions and initialize the instruction cache std::vector<SgAsmFunction*> all_functions = SageInterface::querySubTree<SgAsmFunction>(project); IdFunctionMap functions = existing_functions(tx, files, all_functions); FunctionIdMap function_ids; AddressIdMap entry2id; // maps function entry address to function ID for (IdFunctionMap::iterator fi=functions.begin(); fi!=functions.end(); ++fi) { function_ids[fi->second] = fi->first; entry2id[fi->second->get_entry_va()] = fi->first; } InstructionProvidor insns = InstructionProvidor(all_functions); // Split the work list into chunks, each containing testsPerChunk except the last, which may contain fewer. static const size_t testsPerChunk = 25; size_t nChunks = (work.size() + testsPerChunk - 1) / testsPerChunk; std::vector<SomeTests> jobs; for (size_t i=0; i<nChunks; ++i) { size_t beginWorkIdx = i * testsPerChunk; size_t endWorkIdx = std::min((i+1)*testsPerChunk, work.size()); Work partWork(work.begin()+beginWorkIdx, work.begin()+endWorkIdx); jobs.push_back(SomeTests(partWork, databaseUrl, functions, function_ids, &insns, cmd_id, &entry2id)); } // Run the parts in parallel using the maximum parallelism specified on the command-line. We must commit our // transaction before forking, otherwise the children won't see the rows we've added to various tables. tx->commit(); tx.reset(); size_t nfailed = runInParallel(jobs, opt.nprocs); if (nfailed!=0) { std::cerr <<"SpecimenProcessor: " <<StringUtility::plural(nfailed, "jobs") <<" failed\n"; exit(1); } }
int main(int argc, char *argv[]) { // Parse command-line opt.nprocs = nProcessors(); int argno = parse_commandline(argc, argv); if (argno+1!=argc) usage(1); std::string databaseUrl = argv[argno++]; SqlDatabase::TransactionPtr tx = SqlDatabase::Connection::create(databaseUrl)->transaction(); int64_t cmd_id = start_command(tx, argc, argv, "running tests"); // Load worklist MultiWork work; load_sorted_work(work/*out*/); if (work.empty()) return 0; // Load information about files. The transaction is not saved anywhere. FilesTable files(tx); // We must commit our transaction before we fork, otherwise the child processes won't be able to see the rows we've // inserted. Specifically, the row in the semantic_history table that says who we are. Destroy the smart pointer so that // the connection is even closed. tx->commit(); tx.reset(); // Process work items for each specimen sequentially BOOST_FOREACH (const Work &workForSpecimen, work) if (forkAndWait(SpecimenProcessor(workForSpecimen, files, databaseUrl, cmd_id))) exit(1); // Indicate that this command is finished tx = SqlDatabase::Connection::create(databaseUrl)->transaction(); finish_command(tx, cmd_id, "ran tests"); tx->commit(); return 0; }
void operator()() { // Database connections don't survive over fork() according to SqLite and PostgreSQL documentation, so open it again SqlDatabase::TransactionPtr tx = SqlDatabase::Connection::create(databaseUrl)->transaction(); // Use zero for the number of tests ran so that this child process doesn't try to update the semantic_history table. // If two or more processes try to change the same row (which they will if there's a non-zero number of tests) then // they will deadlock with each other. static const size_t NO_TESTS_RAN = 0; NameSet builtin_function_names; add_builtin_functions(builtin_function_names/*out*/); InputGroup igroup; WorkItem prevWorkItem; SgAsmInterpretation *prev_interp = NULL; MemoryMap ro_map; Disassembler::AddressSet whitelist_exports; // dynamic functions that should be called PointerDetectors pointers; InsnCoverage insn_coverage; DynamicCallGraph dynamic_cg; Tracer tracer; ConsumedInputs consumed_inputs; FuncAnalyses funcinfo; OutputGroups ogroups; // do not load from database (that might take a very long time) time_t last_checkpoint = time(NULL); for (size_t workIdx=0; workIdx<work.size(); ++workIdx) { WorkItem &workItem = work[workIdx]; // Load the input group from the database if necessary. if (workItem.igroup_id!=prevWorkItem.igroup_id) { if (!igroup.load(tx, workItem.igroup_id)) { std::cerr <<argv0 <<": input group " <<workItem.igroup_id <<" is empty or does not exist\n"; exit(1); } } // Find the function to test IdFunctionMap::iterator func_found = functions.find(workItem.func_id); assert(func_found!=functions.end()); SgAsmFunction *func = func_found->second; if (opt.verbosity>=LACONIC) { if (opt.verbosity>=EFFUSIVE) std::cerr <<argv0 <<": " <<std::string(100, '=') <<"\n"; std::cerr <<argv0 <<": processing function " <<function_to_str(func, function_ids) <<"\n"; } SgAsmInterpretation *interp = SageInterface::getEnclosingNode<SgAsmInterpretation>(func); assert(interp!=NULL); // Do per-interpretation stuff if (interp!=prev_interp) { prev_interp = interp; assert(interp->get_map()!=NULL); ro_map = *interp->get_map(); ro_map.require(MemoryMap::READABLE).prohibit(MemoryMap::WRITABLE).keep(); Disassembler::AddressSet whitelist_imports = get_import_addresses(interp, builtin_function_names); whitelist_exports.clear(); // imports are addresses of import table slots; exports are functions overmap_dynlink_addresses(interp, *insns, opt.params.follow_calls, &ro_map, GOTPLT_VALUE, whitelist_imports, whitelist_exports/*out*/); if (opt.verbosity>=EFFUSIVE) { std::cerr <<argv0 <<": memory map for SgAsmInterpretation:\n"; interp->get_map()->dump(std::cerr, argv0+": "); } } // Run the test assert(insns!=NULL); assert(entry2id!=NULL); std::cerr <<"process " <<getpid() <<" about to run test " <<workIdx <<"/" <<work.size() <<" " <<workItem <<"\n"; runOneTest(tx, workItem, pointers, func, function_ids, insn_coverage, dynamic_cg, tracer, consumed_inputs, interp, whitelist_exports, cmd_id, igroup, funcinfo, *insns, &ro_map, *entry2id, ogroups); ++ntests_ran; // Checkpoint if (opt.checkpoint>0 && time(NULL)-last_checkpoint > opt.checkpoint) { if (!opt.dry_run) tx = checkpoint(tx, ogroups, tracer, insn_coverage, dynamic_cg, consumed_inputs, NULL, NO_TESTS_RAN, cmd_id); last_checkpoint = time(NULL); } prevWorkItem = workItem; } std::cerr <<"process " <<getpid() <<" is done testing; now finishing up...\n"; if (!tx->is_terminated()) { SqlDatabase::StatementPtr stmt = tx->statement("insert into semantic_funcpartials" " (func_id, ncalls, nretused, ntests, nvoids) values" " (?, ?, ?, ?, ?)"); for (FuncAnalyses::iterator fi=funcinfo.begin(); fi!=funcinfo.end(); ++fi) { stmt->bind(0, fi->first); stmt->bind(1, fi->second.ncalls); stmt->bind(2, fi->second.nretused); stmt->bind(3, fi->second.ntests); stmt->bind(4, fi->second.nvoids); stmt->execute(); } } // Cleanup if (!tx->is_terminated() && !opt.dry_run) { std::cerr <<"process " <<getpid() <<" is doing the final checkpoint\n"; checkpoint(tx, ogroups, tracer, insn_coverage, dynamic_cg, consumed_inputs, NULL, NO_TESTS_RAN, cmd_id); } tx.reset(); std::cerr <<"process " <<getpid() <<" finished\n"; }