mirror of
				https://github.com/Z3Prover/z3
				synced 2025-10-31 19:52:29 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			287 lines
		
	
	
	
		
			8.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			287 lines
		
	
	
	
		
			8.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*++
 | |
| Copyright (c) 2017 Microsoft Corporation
 | |
| 
 | |
| Module Name:
 | |
| 
 | |
|     main.cpp
 | |
| 
 | |
| Abstract:
 | |
| 
 | |
|     Main file for qprofdiff.
 | |
| 
 | |
| Author:
 | |
| 
 | |
|     Christoph M. Wintersteiger (cwinter)
 | |
| 
 | |
| Revision History:
 | |
| --*/
 | |
| #include<errno.h>
 | |
| #include<limits.h>
 | |
| 
 | |
| #include<string>
 | |
| #include<iostream>
 | |
| #include<fstream>
 | |
| #include<map>
 | |
| #include<vector>
 | |
| #include<set>
 | |
| #include<algorithm>
 | |
| 
 | |
| using namespace std;
 | |
| 
 | |
| set<string> options;
 | |
| 
 | |
| // Profile format:
 | |
| // [quantifier_instances] qname : num_instances : max_generation : max_cost_s
 | |
| const string prefix = "[quantifier_instances]";
 | |
| unsigned prefix_len = prefix.length();
 | |
| typedef struct { unsigned num_instances, max_generation, max_cost; } map_entry;
 | |
| 
 | |
| string trim(string str) {
 | |
|     size_t linx = str.find_first_not_of(' ');
 | |
|     size_t rinx = str.find_last_not_of(' ');
 | |
|     return str.substr(linx, rinx-linx+1);
 | |
| }
 | |
| 
 | |
| int parse(string const & filename, map<string, map_entry> & data) {
 | |
|     ifstream fs(filename.c_str());
 | |
| 
 | |
|     if (!fs.is_open()) {
 | |
|         cout << "Can't open file '" << filename << "'" << endl;
 | |
|         return ENOENT;
 | |
|     }
 | |
| 
 | |
|     string qid;
 | |
|     string tokens[4];
 | |
|     unsigned cur_token = 0;
 | |
| 
 | |
|     while (!fs.eof()) {
 | |
|         string line;
 | |
|         getline(fs, line);
 | |
| 
 | |
|         if (line.substr(0, prefix_len) == prefix) {
 | |
|             line = trim(line.substr(prefix_len));
 | |
|             size_t from = 0, ti = 0;
 | |
|             for (size_t inx = line.find(" : ", from);
 | |
|                 inx != string::npos;
 | |
|                 inx = line.find(" : ", from)) {
 | |
|                 tokens[ti] = trim(line.substr(from, inx-from));
 | |
|                 from = inx+3; //3 is the length of " : "
 | |
|                 ti++;
 | |
|             }
 | |
|             if (from != line.length() && ti < 4)
 | |
|                 tokens[ti] = trim(line.substr(from));
 | |
| 
 | |
|             qid = tokens[0];
 | |
| 
 | |
|             if (data.find(qid) == data.end()) {
 | |
|                 map_entry & entry = data[qid];
 | |
|                 entry.num_instances = entry.max_generation = entry.max_cost = 0;
 | |
|             }
 | |
| 
 | |
|             // Existing entries represent previous occurrences of quantifiers
 | |
|             // that, at some point, were removed (e.g. backtracked). We sum
 | |
|             // up instances from all occurrences of the same qid.
 | |
|             map_entry & entry = data[qid];
 | |
|             entry.num_instances += atoi(tokens[1].c_str());
 | |
|             entry.max_generation = max(entry.max_generation, (unsigned)atoi(tokens[2].c_str()));
 | |
|             entry.max_cost = max(entry.max_cost, (unsigned)atoi(tokens[3].c_str()));
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     fs.close();
 | |
| 
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| void display_data(map<string, map_entry> & data) {
 | |
|     for (map<string, map_entry>::iterator it = data.begin();
 | |
|          it != data.end();
 | |
|          it++)
 | |
|         cout << it->first << ": " << it->second.num_instances <<
 | |
|                              ", " << it->second.max_generation <<
 | |
|                              ", " << it->second.max_cost << endl;
 | |
| }
 | |
| 
 | |
| 
 | |
| typedef struct {
 | |
|     int d_num_instances, d_max_generation, d_max_cost;
 | |
|     bool left_only, right_only;
 | |
| } diff_entry;
 | |
| 
 | |
| typedef struct { string qid; diff_entry e; } diff_item;
 | |
| 
 | |
| #define DIFF_LT(X) bool diff_item_lt_ ## X (diff_item const & l, diff_item const & r) { \
 | |
|     int l_lt_r = l.e.d_ ## X < r.e.d_ ## X; \
 | |
|     int l_eq_r = l.e.d_ ## X == r.e.d_ ## X; \
 | |
|     return \
 | |
|         l.e.left_only ? (r.e.left_only ? ((l_eq_r) ? l.qid < r.qid : l_lt_r) : false) : \
 | |
|         l.e.right_only ? (r.e.right_only ? ((l_eq_r) ? l.qid < r.qid : l_lt_r) : true) : \
 | |
|         r.e.right_only ? false : \
 | |
|         r.e.left_only ? true : \
 | |
|         l_lt_r; \
 | |
| }
 | |
| 
 | |
| DIFF_LT(num_instances)
 | |
| DIFF_LT(max_generation)
 | |
| DIFF_LT(max_cost)
 | |
| 
 | |
| int indicate(diff_entry const & e, bool suppress_unchanged) {
 | |
|     if (e.left_only) {
 | |
|         cout << "< ";
 | |
|         return INT_MIN;
 | |
|     }
 | |
|     else if (e.right_only) {
 | |
|         cout << "> ";
 | |
|         return INT_MAX;
 | |
|     }
 | |
|     else {
 | |
|         int const & delta =
 | |
|             (options.find("-si") != options.end()) ? e.d_num_instances :
 | |
|             (options.find("-sg") != options.end()) ? e.d_max_generation :
 | |
|             (options.find("-sc") != options.end()) ? e.d_max_cost :
 | |
|             e.d_num_instances;
 | |
| 
 | |
|         if (delta < 0)
 | |
|             cout << "+ ";
 | |
|         else if (delta > 0)
 | |
|             cout << "- ";
 | |
|         else if (delta == 0 && !suppress_unchanged)
 | |
|             cout << "= ";
 | |
| 
 | |
|         return delta;
 | |
|     }
 | |
| }
 | |
| 
 | |
| void diff(map<string, map_entry> & left, map<string, map_entry> & right) {
 | |
|     map<string, diff_entry> diff_data;
 | |
| 
 | |
|     for (map<string, map_entry>::const_iterator lit = left.begin();
 | |
|         lit != left.end();
 | |
|         lit++) {
 | |
|         string const & qid = lit->first;
 | |
|         map_entry const & lentry = lit->second;
 | |
| 
 | |
|         map<string, map_entry>::const_iterator rit = right.find(qid);
 | |
|         if (rit != right.end()) {
 | |
|             map_entry const & rentry = rit->second;
 | |
|             diff_entry & de = diff_data[qid];
 | |
| 
 | |
|             de.left_only = de.right_only = false;
 | |
|             de.d_num_instances = lentry.num_instances - rentry.num_instances;
 | |
|             de.d_max_generation = lentry.max_generation - rentry.max_generation;
 | |
|             de.d_max_cost = lentry.max_cost - rentry.max_cost;
 | |
|         }
 | |
|         else {
 | |
|             diff_entry & de = diff_data[qid];
 | |
|             de.left_only = true;
 | |
|             de.right_only = false;
 | |
|             de.d_num_instances = lentry.num_instances;
 | |
|             de.d_max_generation = lentry.max_generation;
 | |
|             de.d_max_cost = lentry.max_cost;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     for (map<string, map_entry>::const_iterator rit = right.begin();
 | |
|         rit != right.end();
 | |
|         rit++) {
 | |
|         string const & qid = rit->first;
 | |
|         map_entry const & rentry = rit->second;
 | |
| 
 | |
|         map<string, map_entry>::const_iterator lit = left.find(qid);
 | |
|         if (lit == left.end()) {
 | |
|             diff_entry & de = diff_data[qid];
 | |
|             de.left_only = false;
 | |
|             de.right_only = true;
 | |
|             de.d_num_instances = -(int)rentry.num_instances;
 | |
|             de.d_max_generation = -(int)rentry.max_generation;
 | |
|             de.d_max_cost = -(int)rentry.max_cost;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     vector<diff_item> flat_data;
 | |
|     for (map<string, diff_entry>::const_iterator it = diff_data.begin();
 | |
|         it != diff_data.end();
 | |
|         it++) {
 | |
|         flat_data.push_back(diff_item());
 | |
|         flat_data.back().qid = it->first;
 | |
|         flat_data.back().e = it->second;
 | |
|     }
 | |
| 
 | |
|     stable_sort(flat_data.begin(), flat_data.end(),
 | |
|         options.find("-si") != options.end() ? diff_item_lt_num_instances :
 | |
|         options.find("-sg") != options.end() ? diff_item_lt_max_generation :
 | |
|         options.find("-sc") != options.end() ? diff_item_lt_max_cost :
 | |
|         diff_item_lt_num_instances);
 | |
| 
 | |
|     bool suppress_unchanged = options.find("-n") != options.end();
 | |
| 
 | |
|     for (vector<diff_item>::const_iterator it = flat_data.begin();
 | |
|         it != flat_data.end();
 | |
|         it++) {
 | |
|         diff_item const & d = *it;
 | |
|         string const & qid = d.qid;
 | |
|         diff_entry const & e = d.e;
 | |
| 
 | |
|         int delta = indicate(e, suppress_unchanged);
 | |
| 
 | |
|         if (!(delta == 0 && suppress_unchanged))
 | |
|             cout << qid << " (" <<
 | |
|                 (e.d_num_instances > 0 ? "" : "+") << -e.d_num_instances << " inst., " <<
 | |
|                 (e.d_max_generation > 0 ? "" : "+") << -e.d_max_generation << " max. gen., " <<
 | |
|                 (e.d_max_cost > 0 ? "" : "+") << -e.d_max_cost << " max. cost)" <<
 | |
|                 endl;
 | |
|     }
 | |
| }
 | |
| 
 | |
| void display_usage() {
 | |
|     cout << "Usage: qprofdiff [options] <filename> <filename>" << endl;
 | |
|     cout << "Options:" << endl;
 | |
|     cout << " -n      Suppress unchanged items" << endl;
 | |
|     cout << " -si     Sort by difference in number of instances" << endl;
 | |
|     cout << " -sg     Sort by difference in max. generation" << endl;
 | |
|     cout << " -sc     Sort by difference in max. cost" << endl;
 | |
| }
 | |
| 
 | |
| int main(int argc, char ** argv) {
 | |
|     char * filename1 = 0;
 | |
|     char * filename2 = 0;
 | |
| 
 | |
|     for (int i = 1; i < argc; i++) {
 | |
|         int len = string(argv[i]).length();
 | |
|         if (len > 1 && argv[i][0] == '-') {
 | |
|             options.insert(string(argv[i]));
 | |
|         }
 | |
|         else if (filename1 == 0)
 | |
|             filename1 = argv[i];
 | |
|         else if (filename2 == 0)
 | |
|             filename2 = argv[i];
 | |
|         else {
 | |
|             cout << "Invalid argument: " << argv[i] << endl << endl;
 | |
|             display_usage();
 | |
|             return EINVAL;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     if (filename1 == 0 || filename2 == 0) {
 | |
|         cout << "Two filenames required." << endl << endl;
 | |
|         display_usage();
 | |
|         return EINVAL;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     cout << "Comparing " << filename1 << " to " << filename2 << endl;
 | |
| 
 | |
|     map<string, map_entry> data1, data2;
 | |
| 
 | |
|     int r = parse(filename1, data1);
 | |
|     if (r != 0) return r;
 | |
|     r = parse(filename2, data2);
 | |
|     if (r != 0) return r;
 | |
| 
 | |
|     // display_data(data1);
 | |
|     // display_data(data2);
 | |
| 
 | |
|     diff(data1, data2);
 | |
| 
 | |
|     return 0;
 | |
| }
 |