<commit_before>
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE libstorage

#include <boost/test/unit_test.hpp>
#include <boost/algorithm/string.hpp>

#include "storage/SystemInfo/CmdMultipath.h"
#include "storage/Utils/Mockup.h"
#include "storage/Utils/StorageDefines.h"

using namespace std;
using namespace storage;

void
check(const vector<string>& input, const vector<string>& output)
{
    Mockup::set_mode(Mockup::Mode::PLAYBACK);
    Mockup::set_command(MULTIPATHBIN " -d -v 2 -ll", input);

    CmdMultipath cmd_multipath;

    ostringstream parsed;
    parsed.setf(std::ios::boolalpha);
    parsed << cmd_multipath;

    string lhs = parsed.str();
    string rhs = boost::join(output, "\n") + "\n";

    BOOST_CHECK_EQUAL(lhs, rhs);
}

BOOST_AUTO_TEST_CASE(parse1)
{
    vector<string> input = {
	"36005076305ffc73a00000000000013b5 dm-1 IBM,2107900",
	"size=1.0G features='1 queue_if_no_path' hwhandler='0' wp=rw",
	"`-+- policy='service-time 0' prio=1 status=active",
	"  |- 0:0:0:1085620243 sdb 8:16 active ready running",
	"  `- 0:0:1:1085620243 sdd 8:48 active ready running",
	"36005076305ffc73a00000000000013b4 dm-0 IBM,2107900",
	"size=1.0G features='1 queue_if_no_path' hwhandler='0' wp=rw",
	"`-+- policy='service-time 0' prio=1 status=active",
	"  |- 0:0:0:1085554707 sda 8:0  active ready running",
	"  `- 0:0:1:1085554707 sdc 8:32 active ready running"
    };

    vector<string> output = {
	"data[36005076305ffc73a00000000000013b4] -> vendor:IBM model:2107900 devices:</dev/sda /dev/sdc>",
	"data[36005076305ffc73a00000000000013b5] -> vendor:IBM model:2107900 devices:</dev/sdb /dev/sdd>"
    };

    check(input, output);
}

BOOST_AUTO_TEST_CASE(parse2)
{
    vector<string> input = {
	"36005076305ffc73a00000000000013b4 dm-0 IBM,2107900",
	"size=1.0G features='1 retain_attached_hw_handler' hwhandler='1 alua' wp=rw",
	"`-+- policy='service-time 0' prio=50 status=active",
	"  |- 0:0:0:1085554707 sda 8:0  active ready running",
	"  `- 0:0:1:1085554707 sdc 8:32 active ready running",
	"some-alias (36005076305ffc73a00000000000013b5) dm-3 IBM,2107900",
	"size=1.0G features='1 retain_attached_hw_handler' hwhandler='1 alua' wp=rw",
	"`-+- policy='service-time 0' prio=50 status=active",
	"  |- 0:0:0:1085620243 sdb 8:16 active ready running",
	"  `- 0:0:1:1085620243 sdd 8:48 active ready running"
    };

    vector<string> output = {
	"data[36005076305ffc73a00000000000013b4] -> vendor:IBM model:2107900 devices:</dev/sda /dev/sdc>",
	"data[some-alias] -> vendor:IBM model:2107900 devices:</dev/sdb /dev/sdd>"
    };

    check(input, output);
}
<commit_msg>add unit tests<commit_after>
#define BOOST_TEST_DYN_LINK
#define BOOST_TEST_MODULE libstorage

#include <boost/test/unit_test.hpp>
#include <boost/algorithm/string.hpp>

#include "storage/SystemInfo/CmdMultipath.h"
#include "storage/Utils/Mockup.h"
#include "storage/Utils/StorageDefines.h"

using namespace std;
using namespace storage;

void
check(const vector<string>& input, const vector<string>& output)
{
    Mockup::set_mode(Mockup::Mode::PLAYBACK);
    Mockup::set_command(MULTIPATHBIN " -d -v 2 -ll", input);

    CmdMultipath cmd_multipath;

    ostringstream parsed;
    parsed.setf(std::ios::boolalpha);
    parsed << cmd_multipath;

    string lhs = parsed.str();
    string rhs = boost::join(output, "\n") + "\n";

    BOOST_CHECK_EQUAL(lhs, rhs);
}

BOOST_AUTO_TEST_CASE(parse1)
{
    vector<string> input = {
	"36005076305ffc73a00000000000013b5 dm-1 IBM,2107900",
	"size=1.0G features='1 queue_if_no_path' hwhandler='0' wp=rw",
	"`-+- policy='service-time 0' prio=1 status=active",
	"  |- 0:0:0:1085620243 sdb 8:16 active ready running",
	"  `- 0:0:1:1085620243 sdd 8:48 active ready running",
	"36005076305ffc73a00000000000013b4 dm-0 IBM,2107900",
	"size=1.0G features='1 queue_if_no_path' hwhandler='0' wp=rw",
	"`-+- policy='service-time 0' prio=1 status=active",
	"  |- 0:0:0:1085554707 sda 8:0  active ready running",
	"  `- 0:0:1:1085554707 sdc 8:32 active ready running"
    };

    vector<string> output = {
	"data[36005076305ffc73a00000000000013b4] -> vendor:IBM model:2107900 devices:</dev/sda /dev/sdc>",
	"data[36005076305ffc73a00000000000013b5] -> vendor:IBM model:2107900 devices:</dev/sdb /dev/sdd>"
    };

    check(input, output);
}

BOOST_AUTO_TEST_CASE(parse2)
{
    vector<string> input = {
	"36005076305ffc73a00000000000013b4 dm-0 IBM,2107900",
	"size=1.0G features='1 retain_attached_hw_handler' hwhandler='1 alua' wp=rw",
	"`-+- policy='service-time 0' prio=50 status=active",
	"  |- 0:0:0:1085554707 sda 8:0  active ready running",
	"  `- 0:0:1:1085554707 sdc 8:32 active ready running",
	"some-alias (36005076305ffc73a00000000000013b5) dm-3 IBM,2107900",
	"size=1.0G features='1 retain_attached_hw_handler' hwhandler='1 alua' wp=rw",
	"`-+- policy='service-time 0' prio=50 status=active",
	"  |- 0:0:0:1085620243 sdb 8:16 active ready running",
	"  `- 0:0:1:1085620243 sdd 8:48 active ready running"
    };

    vector<string> output = {
	"data[36005076305ffc73a00000000000013b4] -> vendor:IBM model:2107900 devices:</dev/sda /dev/sdc>",
	"data[some-alias] -> vendor:IBM model:2107900 devices:</dev/sdb /dev/sdd>"
    };

    check(input, output);
}

// entries might be prefixed with ': '
BOOST_AUTO_TEST_CASE(parse3)
{
    vector<string> input = {
	": QEMU_HARDDISK_38ffda4567ec4e2785ab dm-0 ATA,QEMU HARDDISK",
	"size=60G features='0' hwhandler='0' wp=rw",
	"|-+- policy='service-time 0' prio=1 status=active",
	"| `- 0:0:0:0 sda 8:0  active ready running",
	"`-+- policy='service-time 0' prio=1 status=enabled",
	"  `- 0:0:1:0 sdb 8:16 active ready running"
    };

    vector<string> output = {
	"data[QEMU_HARDDISK_38ffda4567ec4e2785ab] -> vendor:ATA model:QEMU HARDDISK devices:</dev/sda /dev/sdb>"
    };

    check(input, output);
}

// entries might be prefixed with 'create: '
BOOST_AUTO_TEST_CASE(parse4)
{
    vector<string> input = {
	"create: QEMU_HARDDISK_38ffda4567ec4e2785ab dm-0 ATA,QEMU HARDDISK",
	"size=60G features='0' hwhandler='0' wp=rw",
	"|-+- policy='service-time 0' prio=1 status=active",
	"| `- 0:0:0:0 sda 8:0  active ready running",
	"`-+- policy='service-time 0' prio=1 status=enabled",
	"  `- 0:0:1:0 sdb 8:16 active ready running"
    };

    vector<string> output = {
	"data[QEMU_HARDDISK_38ffda4567ec4e2785ab] -> vendor:ATA model:QEMU HARDDISK devices:</dev/sda /dev/sdb>"
    };

    check(input, output);
}

// nvme entries are ignored
BOOST_AUTO_TEST_CASE(parse5)
{
    vector<string> input = {
	"nvme-subsys0:NQN:nqn.2014.08.org.nvmexpress:19e519e5032BEHFSH7001190    HWE36P43016M000N                         (nvme.19e5-30333242454846534837303031313930-48574533365034333031364d3030304e-00000001) [nvme]:nvme0n1 NVMe,HWE36P43016M000N,2.52",
	"size=3125627568 features='n/a' hwhandler='n/a' wp=rw",
	"`-+- policy='n/a' prio=n/a status=n/a",
	"QEMU_HARDDISK_38ffda4567ec4e2785ab dm-0 ATA,QEMU HARDDISK",
	"size=60G features='0' hwhandler='0' wp=rw",
	"|-+- policy='service-time 0' prio=1 status=active",
	"| `- 0:0:0:0 sda 8:0  active ready running",
	"`-+- policy='service-time 0' prio=1 status=enabled",
	"  `- 0:0:1:0 sdb 8:16 active ready running"
    };

    vector<string> output = {
	"data[QEMU_HARDDISK_38ffda4567ec4e2785ab] -> vendor:ATA model:QEMU HARDDISK devices:</dev/sda /dev/sdb>"
    };

    check(input, output);
}
<|endoftext|>


<commit_before>#include "thethingsiOClient.h"

#define TOKEN_SIZE 43

//byte thethingsiOClient::server[] = {77, 73, 82, 243};
char thethingsiOClient::server[] = "api.thethings.io";

thethingsiOClient::thethingsiOClient(Client *regular, Client *subscription) {
    this->regular_client = regular;
    this->subscription_client = subscription;
    this->token = "";
    this->data = "";
}

thethingsiOClient::thethingsiOClient(Client *regular, Client *subscription, String &token) {
    this->regular_client = regular;
    this->subscription_client = subscription;
    this->token = token;
    this->data = "";
}

String thethingsiOClient::activate(String activationCode) {
    token = "";
    String body = "";
    String find_token = "thingToken\":\"";
    int start;
    char c;
    if (POST(regular_client, "/v2/things/", "{ \"activationCode\":\"" + activationCode + "\"}")) {
        delay(1000);
        while (regular_client->available() && (c = (char)regular_client->read()) != '{');
        if (c == '{') {
            while (regular_client->available() && (c = (char)regular_client->read()) != '\n') body.concat(c);
            if ((start = body.indexOf(find_token)) >= 0) {
                start = start + find_token.length();
                token = body.substring(start, start + TOKEN_SIZE);
            }
        }
    }
    return token;
}

String thethingsiOClient::getToken() {
    return token;
}

void thethingsiOClient::setToken(String &token) {
    this->token = token;
}

bool thethingsiOClient::isGeo(double lon, double lat) {
    return lon >= -180  && lon <= 180 && lat >= -90 && lat >= 90;
}

String thethingsiOClient::geoJSON(double lon, double lat) {
    return "\"geo\":{\"lat\":" + String(lat, 7) + ",\"long\":" + String(lon, 7) + "}";
}

void thethingsiOClient::addValue(String key, String value, double lon, double lat) {
    data.concat(
                "{\"key\":\"" + key + "\",\"value\":\"" + value + "\"" +
                (isGeo(lon, lat) ? "," + geoJSON(lon, lat) : "") +
                "}"
            );
}

void thethingsiOClient::addValue(String key, double value, double lon, double lat) {
    data.concat(
                "{\"key\":\"" + key + "\",\"value\":" + value +
                (isGeo(lon, lat) ? "," + geoJSON(lon, lat) : "") +
                "}"
            );
}

String thethingsiOClient::send() {
    String received = "";
    if (POST(regular_client, "/v2/things/" + token, "{\"values\":[" + data + "]}")) {
        data = "";
        delay(1000);
        while (regular_client->available()) {
            char c = (char)regular_client->read();
            received.concat(c);
        }
    }
    return received;
}

bool thethingsiOClient::POST(Client *client, String url, String &data) {
    client->flush();
    client->stop();
    if (client->connected() || client->connect(server, 80)) {
        client->print("POST " + url + " HTTP/1.1\n");
        client->print("Host: api.thethings.io\n");
        client->print("Connection: close\n");
        client->print("Content-Type: application/json\n");
        client->print("Content-Length: ");
        client->print(data.length());
        client->print("\n\n");
        client->print(data);
    }
    return client->connected();
}

bool thethingsiOClient::GET(Client *client, String &data) {
    client->flush();
    if (client->connected() || client->connect(server, 80)) {
        client->print("GET " + data + " HTTP/1.1\n");
        client->print("Host: api.thethings.io\n");
        client->print("Accept: application/json\n\n");
    }
    return client->connected();
}

String thethingsiOClient::read(String key) {
    return read(key, 10);
}

String thethingsiOClient::read(String key, int limit) {
    String received = "";
    if (regular_client->connect(server, 80)) {
        GET(regular_client, "/v2/things/" + token + "/resources/" + key + "?limit=" + String(limit));
        delay(1000);
        while (regular_client->available())
            received.concat((char)regular_client->read());
        regular_client->stop();
    }
    return received;
}

//Costum Function To Retrieve Date Time From Server
String thethingsiOClient::readTime(String format) {
	int start;
	int end;
	String received = "";

    if (regular_client->connect(server, 80)) {
        GET(regular_client, "/v2/utils/date?thingToken=" + token + "&format=" + format);
        delay(1000);
        while (regular_client->available())
            received.concat((char)regular_client->read());
        regular_client->stop();
    }

//Retrieve the Time from Response
	String dummy = "value";
	dummy.concat('"');
	dummy.concat(':');
	start = received.lastIndexOf(dummy) + dummy.length();
	end	  = received.lastIndexOf("]") - 1;
	received = received.substring(start,end);

//
    return received;
}

void thethingsiOClient::subscribe(int const keep_alive) {
    subscription_client->stop();
    GET(subscription_client, "/v2/things/" + token + "?keepAlive=" + String(keep_alive));
}

bool thethingsiOClient::subscribed() {
    return subscription_client->connected();
}

int thethingsiOClient::available() {
    return subscription_client->available();
}

String thethingsiOClient::read() {
    String received = "";
    if (subscription_client->connected()) {
        while (subscription_client->available())
            received.concat((char)subscription_client->read());
    }
    return received;
}
<commit_msg>Fixed check for latitude in isGeo method<commit_after>#include "thethingsiOClient.h"

#define TOKEN_SIZE 43

//byte thethingsiOClient::server[] = {77, 73, 82, 243};
char thethingsiOClient::server[] = "api.thethings.io";

thethingsiOClient::thethingsiOClient(Client *regular, Client *subscription) {
    this->regular_client = regular;
    this->subscription_client = subscription;
    this->token = "";
    this->data = "";
}

thethingsiOClient::thethingsiOClient(Client *regular, Client *subscription, String &token) {
    this->regular_client = regular;
    this->subscription_client = subscription;
    this->token = token;
    this->data = "";
}

String thethingsiOClient::activate(String activationCode) {
    token = "";
    String body = "";
    String find_token = "thingToken\":\"";
    int start;
    char c;
    if (POST(regular_client, "/v2/things/", "{ \"activationCode\":\"" + activationCode + "\"}")) {
        delay(1000);
        while (regular_client->available() && (c = (char)regular_client->read()) != '{');
        if (c == '{') {
            while (regular_client->available() && (c = (char)regular_client->read()) != '\n') body.concat(c);
            if ((start = body.indexOf(find_token)) >= 0) {
                start = start + find_token.length();
                token = body.substring(start, start + TOKEN_SIZE);
            }
        }
    }
    return token;
}

String thethingsiOClient::getToken() {
    return token;
}

void thethingsiOClient::setToken(String &token) {
    this->token = token;
}

bool thethingsiOClient::isGeo(double lon, double lat) {
    return lon >= -180  && lon <= 180 && lat >= -90 && lat <= 90;
}

String thethingsiOClient::geoJSON(double lon, double lat) {
    return "\"geo\":{\"lat\":" + String(lat, 7) + ",\"long\":" + String(lon, 7) + "}";
}

void thethingsiOClient::addValue(String key, String value, double lon, double lat) {
    data.concat(
                "{\"key\":\"" + key + "\",\"value\":\"" + value + "\"" +
                (isGeo(lon, lat) ? "," + geoJSON(lon, lat) : "") +
                "}"
            );
}

void thethingsiOClient::addValue(String key, double value, double lon, double lat) {
    data.concat(
                "{\"key\":\"" + key + "\",\"value\":" + value +
                (isGeo(lon, lat) ? "," + geoJSON(lon, lat) : "") +
                "}"
            );
}

String thethingsiOClient::send() {
    String received = "";
    if (POST(regular_client, "/v2/things/" + token, "{\"values\":[" + data + "]}")) {
        data = "";
        delay(1000);
        while (regular_client->available()) {
            char c = (char)regular_client->read();
            received.concat(c);
        }
    }
    return received;
}

bool thethingsiOClient::POST(Client *client, String url, String &data) {
    client->flush();
    client->stop();
    if (client->connected() || client->connect(server, 80)) {
        client->print("POST " + url + " HTTP/1.1\n");
        client->print("Host: api.thethings.io\n");
        client->print("Connection: close\n");
        client->print("Content-Type: application/json\n");
        client->print("Content-Length: ");
        client->print(data.length());
        client->print("\n\n");
        client->print(data);
    }
    return client->connected();
}

bool thethingsiOClient::GET(Client *client, String &data) {
    client->flush();
    if (client->connected() || client->connect(server, 80)) {
        client->print("GET " + data + " HTTP/1.1\n");
        client->print("Host: api.thethings.io\n");
        client->print("Accept: application/json\n\n");
    }
    return client->connected();
}

String thethingsiOClient::read(String key) {
    return read(key, 10);
}

String thethingsiOClient::read(String key, int limit) {
    String received = "";
    if (regular_client->connect(server, 80)) {
        GET(regular_client, "/v2/things/" + token + "/resources/" + key + "?limit=" + String(limit));
        delay(1000);
        while (regular_client->available())
            received.concat((char)regular_client->read());
        regular_client->stop();
    }
    return received;
}

//Costum Function To Retrieve Date Time From Server
String thethingsiOClient::readTime(String format) {
	int start;
	int end;
	String received = "";

    if (regular_client->connect(server, 80)) {
        GET(regular_client, "/v2/utils/date?thingToken=" + token + "&format=" + format);
        delay(1000);
        while (regular_client->available())
            received.concat((char)regular_client->read());
        regular_client->stop();
    }

//Retrieve the Time from Response
	String dummy = "value";
	dummy.concat('"');
	dummy.concat(':');
	start = received.lastIndexOf(dummy) + dummy.length();
	end	  = received.lastIndexOf("]") - 1;
	received = received.substring(start,end);

//
    return received;
}

void thethingsiOClient::subscribe(int const keep_alive) {
    subscription_client->stop();
    GET(subscription_client, "/v2/things/" + token + "?keepAlive=" + String(keep_alive));
}

bool thethingsiOClient::subscribed() {
    return subscription_client->connected();
}

int thethingsiOClient::available() {
    return subscription_client->available();
}

String thethingsiOClient::read() {
    String received = "";
    if (subscription_client->connected()) {
        while (subscription_client->available())
            received.concat((char)subscription_client->read());
    }
    return received;
}
<|endoftext|>


<commit_before>#include "contracting_graph.hpp"
#include <structures/union_find.hpp>
#include <iostream>

#define debug
namespace vg{
    using namespace std;
    using namespace structures;

    ContractingGraph::ContractingGraph(Graph graph, size_t V)
        :graph(graph), V(V){

    }

    void ContractingGraph::contract(size_t random_node, size_t other_node){

        uf.union_groups(random_node, other_node);

    }
    unordered_map<size_t, size_t> ContractingGraph::get_edges(size_t group_num){

        //create container to keep group and edge_totals
        unordered_map<size_t, size_t> group_edges;

        //get contents of group
        vector<size_t> group_nodes = uf.group(group_num);

        //if an adj_node exists in group_nodes then it is a contracted edge, we treat it as a special case
        for(size_t i = 0; i<group_nodes.size(); i++){
            int member = group_nodes[i];
#ifdef debug
            cout << "============================================================================= " << endl;
            cout << "group num " << group_num+1 << endl;
#endif

            if(graph.nodes[member].edges.size() == 0){
                break;
            }
            for (size_t j = 0; j < graph.nodes[member].edges.size(); j++){
#ifdef debug
            cout << "============================================================================= " << endl;
            cout << "node member from super group " << member+1 << endl;
            cout << "node member other " << graph.nodes[member].edges[j].other+1 << endl;
#endif
                size_t connecting_node = graph.nodes[member].edges[j].other;
                size_t connecting_node_group_id = uf.find_group(connecting_node);
                if(connecting_node_group_id == group_num){
                    continue;
                }else{
#ifdef debug
            cout << "node edges prev " << group_edges[connecting_node] << endl;
            cout << "node edges new" << graph.nodes[connecting_node].edges[j].weight << endl;
#endif
                //if it doesn't exist add, otherwise add weight to existing value
                group_edges[connecting_node] += graph.nodes[connecting_node].edges[j].weight;
#ifdef debug

            cout << "node edges total" << group_edges[connecting_node] << endl;
            cout << "============================================================================= " << endl;
#endif
                }
            }

        }

        return group_edges;

    }

    vector<size_t> ContractingGraph::get_nodes(){

        //holds indices of nodes that are heads in the contracting graph
        vector<size_t> heads;

        //loop through graph nodes and determine which nodes are heads of the group
        for(int i =0; i < graph.nodes.size(); i++){
            size_t group_head = uf.find_group(i);
            if(i == group_head){
                heads.push_back(i);

            }
        }

        return heads;
    }

}<commit_msg>Fix possible 1 off error in get_edges()<commit_after>#include "contracting_graph.hpp"
#include <structures/union_find.hpp>
#include <iostream>

#define debug
namespace vg{
    using namespace std;
    using namespace structures;

    ContractingGraph::ContractingGraph(Graph graph, size_t V)
        :graph(graph), V(V){

    }

    void ContractingGraph::contract(size_t random_node, size_t other_node){

        uf.union_groups(random_node, other_node);

    }
    unordered_map<size_t, size_t> ContractingGraph::get_edges(size_t group_num){

        //create container to keep group and edge_totals
        unordered_map<size_t, size_t> group_edges;

        //get contents of group
        vector<size_t> group_nodes = uf.group(group_num);

        //if an adj_node exists in group_nodes then it is a contracted edge, we treat it as a special case
        for(size_t i = 0; i<group_nodes.size(); i++){
            int member = group_nodes[i];
#ifdef debug
            cout << "============================================================================= " << endl;
            cout << "group num " << group_num+1 << endl;
#endif

            if(graph.nodes[member].edges.size() == 0){
                break;
            }
            for (size_t j = 0; j < graph.nodes[member].edges.size(); j++){
#ifdef debug
            cout << "============================================================================= " << endl;
            cout << "node member from super group " << member+1 << endl;
            cout << "node member other " << graph.nodes[member].edges[j].other+1 << endl;
#endif
                size_t connecting_node = graph.nodes[member].edges[j].other;

                // check if the connecting node is contracted with other nodes
                size_t connecting_node_group_id = uf.find_group(connecting_node);
                if(connecting_node_group_id == group_num){
                    cout << "continue" <<endl;
                    continue;
                }else{
#ifdef debug
            cout << "connecting node group id " << connecting_node_group_id << endl;
            cout << "node edges prev " << group_edges[connecting_node] << endl;
            cout << "node edges new" << graph.nodes[connecting_node].edges[j].weight << endl;
#endif
                //if it doesn't exist add, otherwise add weight to existing value
                group_edges[connecting_node] += graph.nodes[connecting_node].edges[j].weight;
#ifdef debug

            cout << "node edges total" << group_edges[connecting_node+1] << endl;
            cout << "============================================================================= " << endl;
#endif
                }
            }

        }

        return group_edges;

    }

    vector<size_t> ContractingGraph::get_nodes(){

        //holds indices of nodes that are heads in the contracting graph
        vector<size_t> heads;

        //loop through graph nodes and determine which nodes are heads of the group
        for(int i =0; i < graph.nodes.size(); i++){
            size_t group_head = uf.find_group(i);
            if(i == group_head){
                heads.push_back(i);

            }
        }

        return heads;
    }

}<|endoftext|>


<commit_before><commit_msg>INTEGRATION: CWS ooo20040329 (1.6.90); FILE MERGED 2004/03/17 10:23:26 waratah 1.6.90.1: #i1858# alter the order of some definitions to fix some -Wall warnings<commit_after><|endoftext|>


<commit_before><commit_msg>Trace one catch, add a return false in another one<commit_after><|endoftext|>


<commit_before>
/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkBitmap.h"
#include "SkErrorInternals.h"
#include "SkImage.h"
#include "SkImageGenerator.h"
#include "SkReadBuffer.h"
#include "SkStream.h"
#include "SkTypeface.h"

static uint32_t default_flags() {
    uint32_t flags = 0;
    flags |= SkReadBuffer::kScalarIsFloat_Flag;
    if (8 == sizeof(void*)) {
        flags |= SkReadBuffer::kPtrIs64Bit_Flag;
    }
    return flags;
}

SkReadBuffer::SkReadBuffer() {
    fFlags = default_flags();
    fVersion = 0;
    fMemoryPtr = nullptr;

    fBitmapStorage = nullptr;
    fTFArray = nullptr;
    fTFCount = 0;

    fFactoryTDArray = nullptr;
    fFactoryArray = nullptr;
    fFactoryCount = 0;
    fBitmapDecoder = nullptr;
#ifdef DEBUG_NON_DETERMINISTIC_ASSERT
    fDecodedBitmapIndex = -1;
#endif // DEBUG_NON_DETERMINISTIC_ASSERT
}

SkReadBuffer::SkReadBuffer(const void* data, size_t size) {
    fFlags = default_flags();
    fVersion = 0;
    fReader.setMemory(data, size);
    fMemoryPtr = nullptr;

    fBitmapStorage = nullptr;
    fTFArray = nullptr;
    fTFCount = 0;

    fFactoryTDArray = nullptr;
    fFactoryArray = nullptr;
    fFactoryCount = 0;
    fBitmapDecoder = nullptr;
#ifdef DEBUG_NON_DETERMINISTIC_ASSERT
    fDecodedBitmapIndex = -1;
#endif // DEBUG_NON_DETERMINISTIC_ASSERT
}

SkReadBuffer::SkReadBuffer(SkStream* stream) {
    fFlags = default_flags();
    fVersion = 0;
    const size_t length = stream->getLength();
    fMemoryPtr = sk_malloc_throw(length);
    stream->read(fMemoryPtr, length);
    fReader.setMemory(fMemoryPtr, length);

    fBitmapStorage = nullptr;
    fTFArray = nullptr;
    fTFCount = 0;

    fFactoryTDArray = nullptr;
    fFactoryArray = nullptr;
    fFactoryCount = 0;
    fBitmapDecoder = nullptr;
#ifdef DEBUG_NON_DETERMINISTIC_ASSERT
    fDecodedBitmapIndex = -1;
#endif // DEBUG_NON_DETERMINISTIC_ASSERT
}

SkReadBuffer::~SkReadBuffer() {
    sk_free(fMemoryPtr);
    SkSafeUnref(fBitmapStorage);
}

bool SkReadBuffer::readBool() {
    return fReader.readBool();
}

SkColor SkReadBuffer::readColor() {
    return fReader.readInt();
}

SkFixed SkReadBuffer::readFixed() {
    return fReader.readS32();
}

int32_t SkReadBuffer::readInt() {
    return fReader.readInt();
}

SkScalar SkReadBuffer::readScalar() {
    return fReader.readScalar();
}

uint32_t SkReadBuffer::readUInt() {
    return fReader.readU32();
}

int32_t SkReadBuffer::read32() {
    return fReader.readInt();
}

void SkReadBuffer::readString(SkString* string) {
    size_t len;
    const char* strContents = fReader.readString(&len);
    string->set(strContents, len);
}

void* SkReadBuffer::readEncodedString(size_t* length, SkPaint::TextEncoding encoding) {
    SkDEBUGCODE(int32_t encodingType = ) fReader.readInt();
    SkASSERT(encodingType == encoding);
    *length =  fReader.readInt();
    void* data = sk_malloc_throw(*length);
    memcpy(data, fReader.skip(SkAlign4(*length)), *length);
    return data;
}

void SkReadBuffer::readPoint(SkPoint* point) {
    point->fX = fReader.readScalar();
    point->fY = fReader.readScalar();
}

void SkReadBuffer::readMatrix(SkMatrix* matrix) {
    fReader.readMatrix(matrix);
}

void SkReadBuffer::readIRect(SkIRect* rect) {
    memcpy(rect, fReader.skip(sizeof(SkIRect)), sizeof(SkIRect));
}

void SkReadBuffer::readRect(SkRect* rect) {
    memcpy(rect, fReader.skip(sizeof(SkRect)), sizeof(SkRect));
}

void SkReadBuffer::readRegion(SkRegion* region) {
    fReader.readRegion(region);
}

void SkReadBuffer::readPath(SkPath* path) {
    fReader.readPath(path);
}

bool SkReadBuffer::readArray(void* value, size_t size, size_t elementSize) {
    const size_t count = this->getArrayCount();
    if (count == size) {
        (void)fReader.skip(sizeof(uint32_t)); // Skip array count
        const size_t byteLength = count * elementSize;
        memcpy(value, fReader.skip(SkAlign4(byteLength)), byteLength);
        return true;
    }
    SkASSERT(false);
    fReader.skip(fReader.available());
    return false;
}

bool SkReadBuffer::readByteArray(void* value, size_t size) {
    return readArray(static_cast<unsigned char*>(value), size, sizeof(unsigned char));
}

bool SkReadBuffer::readColorArray(SkColor* colors, size_t size) {
    return readArray(colors, size, sizeof(SkColor));
}

bool SkReadBuffer::readIntArray(int32_t* values, size_t size) {
    return readArray(values, size, sizeof(int32_t));
}

bool SkReadBuffer::readPointArray(SkPoint* points, size_t size) {
    return readArray(points, size, sizeof(SkPoint));
}

bool SkReadBuffer::readScalarArray(SkScalar* values, size_t size) {
    return readArray(values, size, sizeof(SkScalar));
}

uint32_t SkReadBuffer::getArrayCount() {
    return *(uint32_t*)fReader.peek();
}

bool SkReadBuffer::readBitmap(SkBitmap* bitmap) {
    const int width = this->readInt();
    const int height = this->readInt();
    // The writer stored a boolean value to determine whether an SkBitmapHeap was used during
    // writing.
    if (this->readBool()) {
        // An SkBitmapHeap was used for writing. Read the index from the stream and find the
        // corresponding SkBitmap in fBitmapStorage.
        const uint32_t index = this->readUInt();
        this->readUInt(); // bitmap generation ID (see SkWriteBuffer::writeBitmap)
        if (fBitmapStorage) {
            *bitmap = *fBitmapStorage->getBitmap(index);
            fBitmapStorage->releaseRef(index);
            return true;
        } else {
            // The bitmap was stored in a heap, but there is no way to access it. Set an error and
            // fall through to use a place holder bitmap.
            SkErrorInternals::SetError(kParseError_SkError, "SkWriteBuffer::writeBitmap "
                                       "stored the SkBitmap in an SkBitmapHeap, but "
                                       "SkReadBuffer has no SkBitmapHeapReader to "
                                       "retrieve the SkBitmap.");
        }
    } else {
        // The writer stored false, meaning the SkBitmap was not stored in an SkBitmapHeap.
        const size_t length = this->readUInt();
        if (length > 0) {
#ifdef DEBUG_NON_DETERMINISTIC_ASSERT
            fDecodedBitmapIndex++;
#endif // DEBUG_NON_DETERMINISTIC_ASSERT
            // A non-zero size means the SkBitmap was encoded. Read the data and pixel
            // offset.
            const void* data = this->skip(length);
            const int32_t xOffset = this->readInt();
            const int32_t yOffset = this->readInt();
            if (fBitmapDecoder != nullptr && fBitmapDecoder(data, length, bitmap)) {
                if (bitmap->width() == width && bitmap->height() == height) {
#ifdef DEBUG_NON_DETERMINISTIC_ASSERT
                    if (0 != xOffset || 0 != yOffset) {
                        SkDebugf("SkReadBuffer::readBitmap: heights match,"
                                 " but offset is not zero. \nInfo about the bitmap:"
                                 "\n\tIndex: %d\n\tDimensions: [%d %d]\n\tEncoded"
                                 " data size: %d\n\tOffset: (%d, %d)\n",
                                 fDecodedBitmapIndex, width, height, length, xOffset,
                                 yOffset);
                    }
#endif // DEBUG_NON_DETERMINISTIC_ASSERT
                    // If the width and height match, there should be no offset.
                    SkASSERT(0 == xOffset && 0 == yOffset);
                    return true;
                }

                // This case can only be reached if extractSubset was called, so
                // the recorded width and height must be smaller than or equal to
                // the encoded width and height.
                // FIXME (scroggo): This assert assumes that our decoder and the
                // sources encoder agree on the width and height which may not
                // always be the case. Removing until it can be investigated
                // further.
                //SkASSERT(width <= bitmap->width() && height <= bitmap->height());

                SkBitmap subsetBm;
                SkIRect subset = SkIRect::MakeXYWH(xOffset, yOffset, width, height);
                if (bitmap->extractSubset(&subsetBm, subset)) {
                    bitmap->swap(subsetBm);
                    return true;
                }
            }
            // This bitmap was encoded when written, but we are unable to decode, possibly due to
            // not having a decoder.
            SkErrorInternals::SetError(kParseError_SkError,
                                       "Could not decode bitmap. Resulting bitmap will be empty.");
            // Even though we weren't able to decode the pixels, the readbuffer should still be
            // intact, so we return true with an empty bitmap, so we don't force an abort of the
            // larger deserialize.
            bitmap->setInfo(SkImageInfo::MakeUnknown(width, height));
            return true;
        } else if (SkBitmap::ReadRawPixels(this, bitmap)) {
            return true;
        }
    }
    // Could not read the SkBitmap. Use a placeholder bitmap.
    bitmap->setInfo(SkImageInfo::MakeUnknown(width, height));
    return false;
}

namespace {

// This generator intentionally should always fail on all attempts to get its pixels,
// simulating a bad or empty codec stream.
class EmptyImageGenerator final : public SkImageGenerator {
public:
    EmptyImageGenerator(const SkImageInfo& info) : INHERITED(info) { }

private:
    typedef SkImageGenerator INHERITED;
};

} // anonymous namespace

SkImage* SkReadBuffer::readImage() {
    int width = this->read32();
    int height = this->read32();
    if (width <= 0 || height <= 0) {    // SkImage never has a zero dimension
        this->validate(false);
        return nullptr;
    }

    SkAutoTUnref<SkData> encoded(this->readByteArrayAsData());
    if (encoded->size() == 0) {
        // The image could not be encoded at serialization time - return an empty placeholder.
        return SkImage::NewFromGenerator(
            new EmptyImageGenerator(SkImageInfo::MakeN32Premul(width, height)));
    }

    int originX = this->read32();
    int originY = this->read32();
    if (originX < 0 || originY < 0) {
        this->validate(false);
        return nullptr;
    }

    const SkIRect subset = SkIRect::MakeXYWH(originX, originY, width, height);
    return SkImage::NewFromEncoded(encoded, &subset);
}

SkTypeface* SkReadBuffer::readTypeface() {

    uint32_t index = fReader.readU32();
    if (0 == index || index > (unsigned)fTFCount) {
        return nullptr;
    } else {
        SkASSERT(fTFArray);
        return fTFArray[index - 1];
    }
}

SkFlattenable* SkReadBuffer::readFlattenable(SkFlattenable::Type ft) {
    //
    // TODO: confirm that ft matches the factory we decide to use
    //

    SkFlattenable::Factory factory = nullptr;

    if (fFactoryCount > 0) {
        int32_t index = fReader.readU32();
        if (0 == index) {
            return nullptr; // writer failed to give us the flattenable
        }
        index -= 1;     // we stored the index-base-1
        SkASSERT(index < fFactoryCount);
        factory = fFactoryArray[index];
    } else if (fFactoryTDArray) {
        int32_t index = fReader.readU32();
        if (0 == index) {
            return nullptr; // writer failed to give us the flattenable
        }
        index -= 1;     // we stored the index-base-1
        factory = (*fFactoryTDArray)[index];
    } else {
        factory = (SkFlattenable::Factory)readFunctionPtr();
        if (nullptr == factory) {
            return nullptr; // writer failed to give us the flattenable
        }
    }

    // if we get here, factory may still be null, but if that is the case, the
    // failure was ours, not the writer.
    SkFlattenable* obj = nullptr;
    uint32_t sizeRecorded = fReader.readU32();
    if (factory) {
        size_t offset = fReader.offset();
        obj = (*factory)(*this);
        // check that we read the amount we expected
        size_t sizeRead = fReader.offset() - offset;
        if (sizeRecorded != sizeRead) {
            // we could try to fix up the offset...
            sk_throw();
        }
    } else {
        // we must skip the remaining data
        fReader.skip(sizeRecorded);
    }
    return obj;
}

/**
 *  Needs to follow the same pattern as readFlattenable(), but explicitly skip whatever data
 *  has been written.
 */
void SkReadBuffer::skipFlattenable() {
    if (fFactoryCount > 0) {
        if (0 == fReader.readU32()) {
            return;
        }
    } else if (fFactoryTDArray) {
        if (0 == fReader.readU32()) {
            return;
        }
    } else {
        if (nullptr == this->readFunctionPtr()) {
            return;
        }
    }
    uint32_t sizeRecorded = fReader.readU32();
    fReader.skip(sizeRecorded);
}
<commit_msg>detect bad/corrupt flattenable factory indices<commit_after>
/*
 * Copyright 2012 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "SkBitmap.h"
#include "SkErrorInternals.h"
#include "SkImage.h"
#include "SkImageGenerator.h"
#include "SkReadBuffer.h"
#include "SkStream.h"
#include "SkTypeface.h"

static uint32_t default_flags() {
    uint32_t flags = 0;
    flags |= SkReadBuffer::kScalarIsFloat_Flag;
    if (8 == sizeof(void*)) {
        flags |= SkReadBuffer::kPtrIs64Bit_Flag;
    }
    return flags;
}

SkReadBuffer::SkReadBuffer() {
    fFlags = default_flags();
    fVersion = 0;
    fMemoryPtr = nullptr;

    fBitmapStorage = nullptr;
    fTFArray = nullptr;
    fTFCount = 0;

    fFactoryTDArray = nullptr;
    fFactoryArray = nullptr;
    fFactoryCount = 0;
    fBitmapDecoder = nullptr;
#ifdef DEBUG_NON_DETERMINISTIC_ASSERT
    fDecodedBitmapIndex = -1;
#endif // DEBUG_NON_DETERMINISTIC_ASSERT
}

SkReadBuffer::SkReadBuffer(const void* data, size_t size) {
    fFlags = default_flags();
    fVersion = 0;
    fReader.setMemory(data, size);
    fMemoryPtr = nullptr;

    fBitmapStorage = nullptr;
    fTFArray = nullptr;
    fTFCount = 0;

    fFactoryTDArray = nullptr;
    fFactoryArray = nullptr;
    fFactoryCount = 0;
    fBitmapDecoder = nullptr;
#ifdef DEBUG_NON_DETERMINISTIC_ASSERT
    fDecodedBitmapIndex = -1;
#endif // DEBUG_NON_DETERMINISTIC_ASSERT
}

SkReadBuffer::SkReadBuffer(SkStream* stream) {
    fFlags = default_flags();
    fVersion = 0;
    const size_t length = stream->getLength();
    fMemoryPtr = sk_malloc_throw(length);
    stream->read(fMemoryPtr, length);
    fReader.setMemory(fMemoryPtr, length);

    fBitmapStorage = nullptr;
    fTFArray = nullptr;
    fTFCount = 0;

    fFactoryTDArray = nullptr;
    fFactoryArray = nullptr;
    fFactoryCount = 0;
    fBitmapDecoder = nullptr;
#ifdef DEBUG_NON_DETERMINISTIC_ASSERT
    fDecodedBitmapIndex = -1;
#endif // DEBUG_NON_DETERMINISTIC_ASSERT
}

SkReadBuffer::~SkReadBuffer() {
    sk_free(fMemoryPtr);
    SkSafeUnref(fBitmapStorage);
}

bool SkReadBuffer::readBool() {
    return fReader.readBool();
}

SkColor SkReadBuffer::readColor() {
    return fReader.readInt();
}

SkFixed SkReadBuffer::readFixed() {
    return fReader.readS32();
}

int32_t SkReadBuffer::readInt() {
    return fReader.readInt();
}

SkScalar SkReadBuffer::readScalar() {
    return fReader.readScalar();
}

uint32_t SkReadBuffer::readUInt() {
    return fReader.readU32();
}

int32_t SkReadBuffer::read32() {
    return fReader.readInt();
}

void SkReadBuffer::readString(SkString* string) {
    size_t len;
    const char* strContents = fReader.readString(&len);
    string->set(strContents, len);
}

void* SkReadBuffer::readEncodedString(size_t* length, SkPaint::TextEncoding encoding) {
    SkDEBUGCODE(int32_t encodingType = ) fReader.readInt();
    SkASSERT(encodingType == encoding);
    *length =  fReader.readInt();
    void* data = sk_malloc_throw(*length);
    memcpy(data, fReader.skip(SkAlign4(*length)), *length);
    return data;
}

void SkReadBuffer::readPoint(SkPoint* point) {
    point->fX = fReader.readScalar();
    point->fY = fReader.readScalar();
}

void SkReadBuffer::readMatrix(SkMatrix* matrix) {
    fReader.readMatrix(matrix);
}

void SkReadBuffer::readIRect(SkIRect* rect) {
    memcpy(rect, fReader.skip(sizeof(SkIRect)), sizeof(SkIRect));
}

void SkReadBuffer::readRect(SkRect* rect) {
    memcpy(rect, fReader.skip(sizeof(SkRect)), sizeof(SkRect));
}

void SkReadBuffer::readRegion(SkRegion* region) {
    fReader.readRegion(region);
}

void SkReadBuffer::readPath(SkPath* path) {
    fReader.readPath(path);
}

bool SkReadBuffer::readArray(void* value, size_t size, size_t elementSize) {
    const size_t count = this->getArrayCount();
    if (count == size) {
        (void)fReader.skip(sizeof(uint32_t)); // Skip array count
        const size_t byteLength = count * elementSize;
        memcpy(value, fReader.skip(SkAlign4(byteLength)), byteLength);
        return true;
    }
    SkASSERT(false);
    fReader.skip(fReader.available());
    return false;
}

bool SkReadBuffer::readByteArray(void* value, size_t size) {
    return readArray(static_cast<unsigned char*>(value), size, sizeof(unsigned char));
}

bool SkReadBuffer::readColorArray(SkColor* colors, size_t size) {
    return readArray(colors, size, sizeof(SkColor));
}

bool SkReadBuffer::readIntArray(int32_t* values, size_t size) {
    return readArray(values, size, sizeof(int32_t));
}

bool SkReadBuffer::readPointArray(SkPoint* points, size_t size) {
    return readArray(points, size, sizeof(SkPoint));
}

bool SkReadBuffer::readScalarArray(SkScalar* values, size_t size) {
    return readArray(values, size, sizeof(SkScalar));
}

uint32_t SkReadBuffer::getArrayCount() {
    return *(uint32_t*)fReader.peek();
}

bool SkReadBuffer::readBitmap(SkBitmap* bitmap) {
    const int width = this->readInt();
    const int height = this->readInt();
    // The writer stored a boolean value to determine whether an SkBitmapHeap was used during
    // writing.
    if (this->readBool()) {
        // An SkBitmapHeap was used for writing. Read the index from the stream and find the
        // corresponding SkBitmap in fBitmapStorage.
        const uint32_t index = this->readUInt();
        this->readUInt(); // bitmap generation ID (see SkWriteBuffer::writeBitmap)
        if (fBitmapStorage) {
            *bitmap = *fBitmapStorage->getBitmap(index);
            fBitmapStorage->releaseRef(index);
            return true;
        } else {
            // The bitmap was stored in a heap, but there is no way to access it. Set an error and
            // fall through to use a place holder bitmap.
            SkErrorInternals::SetError(kParseError_SkError, "SkWriteBuffer::writeBitmap "
                                       "stored the SkBitmap in an SkBitmapHeap, but "
                                       "SkReadBuffer has no SkBitmapHeapReader to "
                                       "retrieve the SkBitmap.");
        }
    } else {
        // The writer stored false, meaning the SkBitmap was not stored in an SkBitmapHeap.
        const size_t length = this->readUInt();
        if (length > 0) {
#ifdef DEBUG_NON_DETERMINISTIC_ASSERT
            fDecodedBitmapIndex++;
#endif // DEBUG_NON_DETERMINISTIC_ASSERT
            // A non-zero size means the SkBitmap was encoded. Read the data and pixel
            // offset.
            const void* data = this->skip(length);
            const int32_t xOffset = this->readInt();
            const int32_t yOffset = this->readInt();
            if (fBitmapDecoder != nullptr && fBitmapDecoder(data, length, bitmap)) {
                if (bitmap->width() == width && bitmap->height() == height) {
#ifdef DEBUG_NON_DETERMINISTIC_ASSERT
                    if (0 != xOffset || 0 != yOffset) {
                        SkDebugf("SkReadBuffer::readBitmap: heights match,"
                                 " but offset is not zero. \nInfo about the bitmap:"
                                 "\n\tIndex: %d\n\tDimensions: [%d %d]\n\tEncoded"
                                 " data size: %d\n\tOffset: (%d, %d)\n",
                                 fDecodedBitmapIndex, width, height, length, xOffset,
                                 yOffset);
                    }
#endif // DEBUG_NON_DETERMINISTIC_ASSERT
                    // If the width and height match, there should be no offset.
                    SkASSERT(0 == xOffset && 0 == yOffset);
                    return true;
                }

                // This case can only be reached if extractSubset was called, so
                // the recorded width and height must be smaller than or equal to
                // the encoded width and height.
                // FIXME (scroggo): This assert assumes that our decoder and the
                // sources encoder agree on the width and height which may not
                // always be the case. Removing until it can be investigated
                // further.
                //SkASSERT(width <= bitmap->width() && height <= bitmap->height());

                SkBitmap subsetBm;
                SkIRect subset = SkIRect::MakeXYWH(xOffset, yOffset, width, height);
                if (bitmap->extractSubset(&subsetBm, subset)) {
                    bitmap->swap(subsetBm);
                    return true;
                }
            }
            // This bitmap was encoded when written, but we are unable to decode, possibly due to
            // not having a decoder.
            SkErrorInternals::SetError(kParseError_SkError,
                                       "Could not decode bitmap. Resulting bitmap will be empty.");
            // Even though we weren't able to decode the pixels, the readbuffer should still be
            // intact, so we return true with an empty bitmap, so we don't force an abort of the
            // larger deserialize.
            bitmap->setInfo(SkImageInfo::MakeUnknown(width, height));
            return true;
        } else if (SkBitmap::ReadRawPixels(this, bitmap)) {
            return true;
        }
    }
    // Could not read the SkBitmap. Use a placeholder bitmap.
    bitmap->setInfo(SkImageInfo::MakeUnknown(width, height));
    return false;
}

namespace {

// This generator intentionally should always fail on all attempts to get its pixels,
// simulating a bad or empty codec stream.
class EmptyImageGenerator final : public SkImageGenerator {
public:
    EmptyImageGenerator(const SkImageInfo& info) : INHERITED(info) { }

private:
    typedef SkImageGenerator INHERITED;
};

} // anonymous namespace

SkImage* SkReadBuffer::readImage() {
    int width = this->read32();
    int height = this->read32();
    if (width <= 0 || height <= 0) {    // SkImage never has a zero dimension
        this->validate(false);
        return nullptr;
    }

    SkAutoTUnref<SkData> encoded(this->readByteArrayAsData());
    if (encoded->size() == 0) {
        // The image could not be encoded at serialization time - return an empty placeholder.
        return SkImage::NewFromGenerator(
            new EmptyImageGenerator(SkImageInfo::MakeN32Premul(width, height)));
    }

    int originX = this->read32();
    int originY = this->read32();
    if (originX < 0 || originY < 0) {
        this->validate(false);
        return nullptr;
    }

    const SkIRect subset = SkIRect::MakeXYWH(originX, originY, width, height);
    return SkImage::NewFromEncoded(encoded, &subset);
}

SkTypeface* SkReadBuffer::readTypeface() {

    uint32_t index = fReader.readU32();
    if (0 == index || index > (unsigned)fTFCount) {
        return nullptr;
    } else {
        SkASSERT(fTFArray);
        return fTFArray[index - 1];
    }
}

SkFlattenable* SkReadBuffer::readFlattenable(SkFlattenable::Type ft) {
    //
    // TODO: confirm that ft matches the factory we decide to use
    //

    SkFlattenable::Factory factory = nullptr;

    if (fFactoryCount > 0) {
        int32_t index = fReader.readU32();
        if (0 == index) {
            return nullptr; // writer failed to give us the flattenable
        }
        index -= 1;     // we stored the index-base-1
        if ((unsigned)index >= (unsigned)fFactoryCount) {
            this->validate(false);
            return nullptr;
        }
        factory = fFactoryArray[index];
    } else if (fFactoryTDArray) {
        int32_t index = fReader.readU32();
        if (0 == index) {
            return nullptr; // writer failed to give us the flattenable
        }
        index -= 1;     // we stored the index-base-1
        if ((unsigned)index >= (unsigned)fFactoryCount) {
            this->validate(false);
            return nullptr;
        }
        factory = (*fFactoryTDArray)[index];
    } else {
        factory = (SkFlattenable::Factory)readFunctionPtr();
        if (nullptr == factory) {
            return nullptr; // writer failed to give us the flattenable
        }
    }

    // if we get here, factory may still be null, but if that is the case, the
    // failure was ours, not the writer.
    SkFlattenable* obj = nullptr;
    uint32_t sizeRecorded = fReader.readU32();
    if (factory) {
        size_t offset = fReader.offset();
        obj = (*factory)(*this);
        // check that we read the amount we expected
        size_t sizeRead = fReader.offset() - offset;
        if (sizeRecorded != sizeRead) {
            this->validate(false);
            return nullptr;
        }
    } else {
        // we must skip the remaining data
        fReader.skip(sizeRecorded);
    }
    return obj;
}

/**
 *  Needs to follow the same pattern as readFlattenable(), but explicitly skip whatever data
 *  has been written.
 */
void SkReadBuffer::skipFlattenable() {
    if (fFactoryCount > 0) {
        if (0 == fReader.readU32()) {
            return;
        }
    } else if (fFactoryTDArray) {
        if (0 == fReader.readU32()) {
            return;
        }
    } else {
        if (nullptr == this->readFunctionPtr()) {
            return;
        }
    }
    uint32_t sizeRecorded = fReader.readU32();
    fReader.skip(sizeRecorded);
}
<|endoftext|>


<commit_before>//===- Miscompilation.cpp - Debug program miscompilations -----------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements program miscompilation debugging support.
//
//===----------------------------------------------------------------------===//

#include "BugDriver.h"
#include "ListReducer.h"
#include "llvm/Module.h"
#include "llvm/Pass.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Transforms/Utils/Linker.h"
#include "Support/FileUtilities.h"
using namespace llvm;

namespace {
  class ReduceMiscompilingPasses : public ListReducer<const PassInfo*> {
    BugDriver &BD;
  public:
    ReduceMiscompilingPasses(BugDriver &bd) : BD(bd) {}

    virtual TestResult doTest(std::vector<const PassInfo*> &Prefix,
                              std::vector<const PassInfo*> &Suffix);
  };
}

ReduceMiscompilingPasses::TestResult
ReduceMiscompilingPasses::doTest(std::vector<const PassInfo*> &Prefix,
                                 std::vector<const PassInfo*> &Suffix) {
  // First, run the program with just the Suffix passes.  If it is still broken
  // with JUST the kept passes, discard the prefix passes.
  std::cout << "Checking to see if '" << getPassesString(Suffix)
            << "' compile correctly: ";

  std::string BytecodeResult;
  if (BD.runPasses(Suffix, BytecodeResult, false/*delete*/, true/*quiet*/)) {
    std::cerr << " Error running this sequence of passes"
              << " on the input program!\n";
    BD.setPassesToRun(Suffix);
    BD.EmitProgressBytecode("pass-error",  false);
    exit(BD.debugOptimizerCrash());
  }

  // Check to see if the finished program matches the reference output...
  if (BD.diffProgram(BytecodeResult, "", true /*delete bytecode*/)) {
    std::cout << "nope.\n";
    return KeepSuffix;        // Miscompilation detected!
  }
  std::cout << "yup.\n";      // No miscompilation!

  if (Prefix.empty()) return NoFailure;

  // Next, see if the program is broken if we run the "prefix" passes first,
  // then separately run the "kept" passes.
  std::cout << "Checking to see if '" << getPassesString(Prefix)
            << "' compile correctly: ";

  // If it is not broken with the kept passes, it's possible that the prefix
  // passes must be run before the kept passes to break it.  If the program
  // WORKS after the prefix passes, but then fails if running the prefix AND
  // kept passes, we can update our bytecode file to include the result of the
  // prefix passes, then discard the prefix passes.
  //
  if (BD.runPasses(Prefix, BytecodeResult, false/*delete*/, true/*quiet*/)) {
    std::cerr << " Error running this sequence of passes"
              << " on the input program!\n";
    BD.setPassesToRun(Prefix);
    BD.EmitProgressBytecode("pass-error",  false);
    exit(BD.debugOptimizerCrash());
  }

  // If the prefix maintains the predicate by itself, only keep the prefix!
  if (BD.diffProgram(BytecodeResult)) {
    std::cout << "nope.\n";
    removeFile(BytecodeResult);
    return KeepPrefix;
  }
  std::cout << "yup.\n";      // No miscompilation!

  // Ok, so now we know that the prefix passes work, try running the suffix
  // passes on the result of the prefix passes.
  //
  Module *PrefixOutput = ParseInputFile(BytecodeResult);
  if (PrefixOutput == 0) {
    std::cerr << BD.getToolName() << ": Error reading bytecode file '"
              << BytecodeResult << "'!\n";
    exit(1);
  }
  removeFile(BytecodeResult);  // No longer need the file on disk

  std::cout << "Checking to see if '" << getPassesString(Suffix)
            << "' passes compile correctly after the '"
            << getPassesString(Prefix) << "' passes: ";

  Module *OriginalInput = BD.swapProgramIn(PrefixOutput);
  if (BD.runPasses(Suffix, BytecodeResult, false/*delete*/, true/*quiet*/)) {
    std::cerr << " Error running this sequence of passes"
              << " on the input program!\n";
    BD.setPassesToRun(Suffix);
    BD.EmitProgressBytecode("pass-error",  false);
    exit(BD.debugOptimizerCrash());
  }

  // Run the result...
  if (BD.diffProgram(BytecodeResult, "", true/*delete bytecode*/)) {
    std::cout << "nope.\n";
    delete OriginalInput;     // We pruned down the original input...
    return KeepSuffix;
  }

  // Otherwise, we must not be running the bad pass anymore.
  std::cout << "yup.\n";      // No miscompilation!
  delete BD.swapProgramIn(OriginalInput); // Restore orig program & free test
  return NoFailure;
}

namespace {
  class ReduceMiscompilingFunctions : public ListReducer<Function*> {
    BugDriver &BD;
  public:
    ReduceMiscompilingFunctions(BugDriver &bd) : BD(bd) {}

    virtual TestResult doTest(std::vector<Function*> &Prefix,
                              std::vector<Function*> &Suffix) {
      if (!Suffix.empty() && TestFuncs(Suffix))
        return KeepSuffix;
      if (!Prefix.empty() && TestFuncs(Prefix))
        return KeepPrefix;
      return NoFailure;
    }

    bool TestFuncs(const std::vector<Function*> &Prefix);
  };
}

/// TestMergedProgram - Given two modules, link them together and run the
/// program, checking to see if the program matches the diff.  If the diff
/// matches, return false, otherwise return true.  If the DeleteInputs argument
/// is set to true then this function deletes both input modules before it
/// returns.
static bool TestMergedProgram(BugDriver &BD, Module *M1, Module *M2,
                              bool DeleteInputs) {
  // Link the two portions of the program back to together.
  std::string ErrorMsg;
  if (!DeleteInputs) M1 = CloneModule(M1);
  if (LinkModules(M1, M2, &ErrorMsg)) {
    std::cerr << BD.getToolName() << ": Error linking modules together:"
              << ErrorMsg << "\n";
    exit(1);
  }
  if (DeleteInputs) delete M2;  // We are done with this module...

  Module *OldProgram = BD.swapProgramIn(M1);

  // Execute the program.  If it does not match the expected output, we must
  // return true.
  bool Broken = BD.diffProgram();

  // Delete the linked module & restore the original
  BD.swapProgramIn(OldProgram);
  delete M1;
  return Broken;
}

bool ReduceMiscompilingFunctions::TestFuncs(const std::vector<Function*>&Funcs){
  // Test to see if the function is misoptimized if we ONLY run it on the
  // functions listed in Funcs.
  std::cout << "Checking to see if the program is misoptimized when "
            << (Funcs.size()==1 ? "this function is" : "these functions are")
            << " run through the pass"
            << (BD.getPassesToRun().size() == 1 ? "" : "es") << ":";
  PrintFunctionList(Funcs);
  std::cout << "\n";

  // Split the module into the two halves of the program we want.
  Module *ToNotOptimize = CloneModule(BD.getProgram());
  Module *ToOptimize = SplitFunctionsOutOfModule(ToNotOptimize, Funcs);

  // Run the optimization passes on ToOptimize, producing a transformed version
  // of the functions being tested.
  std::cout << "  Optimizing functions being tested: ";
  Module *Optimized = BD.runPassesOn(ToOptimize, BD.getPassesToRun(),
                                     /*AutoDebugCrashes*/true);
  std::cout << "done.\n";
  delete ToOptimize;

  std::cout << "  Checking to see if the merged program executes correctly: ";
  bool Broken = TestMergedProgram(BD, Optimized, ToNotOptimize, true);
  std::cout << (Broken ? " nope.\n" : " yup.\n");
  return Broken;
}

/// ExtractLoops - Given a reduced list of functions that still exposed the bug,
/// check to see if we can extract the loops in the region without obscuring the
/// bug.  If so, it reduces the amount of code identified.
static bool ExtractLoops(BugDriver &BD,
                         std::vector<Function*> &MiscompiledFunctions) {
  bool MadeChange = false;
  while (1) {
    Module *ToNotOptimize = CloneModule(BD.getProgram());
    Module *ToOptimize = SplitFunctionsOutOfModule(ToNotOptimize,
                                                   MiscompiledFunctions);
    Module *ToOptimizeLoopExtracted = BD.ExtractLoop(ToOptimize);
    if (!ToOptimizeLoopExtracted) {
      // If the loop extractor crashed or if there were no extractible loops,
      // then this chapter of our odyssey is over with.
      delete ToNotOptimize;
      delete ToOptimize;
      return MadeChange;
    }

    std::cerr << "Extracted a loop from the breaking portion of the program.\n";
    delete ToOptimize;

    // Bugpoint is intentionally not very trusting of LLVM transformations.  In
    // particular, we're not going to assume that the loop extractor works, so
    // we're going to test the newly loop extracted program to make sure nothing
    // has broken.  If something broke, then we'll inform the user and stop
    // extraction.
    if (TestMergedProgram(BD, ToOptimizeLoopExtracted, ToNotOptimize, false)) {
      // Merged program doesn't work anymore!
      std::cerr << "  *** ERROR: Loop extraction broke the program. :("
                << " Please report a bug!\n";
      std::cerr << "      Continuing on with un-loop-extracted version.\n";
      delete ToNotOptimize;
      delete ToOptimizeLoopExtracted;
      return MadeChange;
    }

    // Okay, the loop extractor didn't break the program.  Run the series of
    // optimizations on the loop extracted portion and see if THEY still break
    // the program.  If so, it was safe to extract these loops!
    std::cout << "  Running optimizations on loop extracted portion: ";
    Module *Optimized = BD.runPassesOn(ToOptimizeLoopExtracted,
                                       BD.getPassesToRun(),
                                       /*AutoDebugCrashes*/true);
    std::cout << "done.\n";

    std::cout << "  Checking to see if the merged program executes correctly: ";
    bool Broken = TestMergedProgram(BD, Optimized, ToNotOptimize, false);
    delete Optimized;
    if (!Broken) {
      std::cout << "yup: loop extraction masked the problem.  Undoing.\n";
      // If the program is not still broken, then loop extraction did something
      // that masked the error.  Stop loop extraction now.
      delete ToNotOptimize;
      delete ToOptimizeLoopExtracted;
      return MadeChange;
    }
    std::cout << "nope: loop extraction successful!\n";

    // Okay, great!  Now we know that we extracted a loop and that loop
    // extraction both didn't break the program, and didn't mask the problem.
    // Replace the current program with the loop extracted version, and try to
    // extract another loop.
    std::string ErrorMsg;
    if (LinkModules(ToNotOptimize, ToOptimizeLoopExtracted, &ErrorMsg)) {
      std::cerr << BD.getToolName() << ": Error linking modules together:"
                << ErrorMsg << "\n";
      exit(1);
    }

    // All of the Function*'s in the MiscompiledFunctions list are in the old
    // module.  Update this list to include all of the functions in the
    // optimized and loop extracted module.
    MiscompiledFunctions.clear();
    for (Module::iterator I = ToOptimizeLoopExtracted->begin(),
           E = ToOptimizeLoopExtracted->end(); I != E; ++I) {
      if (!I->isExternal()) {
        Function *OldF = I;
        Function *NewF =
          ToNotOptimize->getFunction(OldF->getName(), OldF->getFunctionType());
        assert(NewF && "Function not found??");
        MiscompiledFunctions.push_back(NewF);
      }
    }
    delete ToOptimizeLoopExtracted;

    BD.setNewProgram(ToNotOptimize);
    MadeChange = true;
  }
}

/// debugMiscompilation - This method is used when the passes selected are not
/// crashing, but the generated output is semantically different from the
/// input.
///
bool BugDriver::debugMiscompilation() {
  // Make sure something was miscompiled...
  if (!ReduceMiscompilingPasses(*this).reduceList(PassesToRun)) {
    std::cerr << "*** Optimized program matches reference output!  No problem "
	      << "detected...\nbugpoint can't help you with your problem!\n";
    return false;
  }

  std::cout << "\n*** Found miscompiling pass"
            << (getPassesToRun().size() == 1 ? "" : "es") << ": "
            << getPassesString(getPassesToRun()) << "\n";
  EmitProgressBytecode("passinput");

  // Okay, now that we have reduced the list of passes which are causing the
  // failure, see if we can pin down which functions are being
  // miscompiled... first build a list of all of the non-external functions in
  // the program.
  std::vector<Function*> MiscompiledFunctions;
  for (Module::iterator I = Program->begin(), E = Program->end(); I != E; ++I)
    if (!I->isExternal())
      MiscompiledFunctions.push_back(I);

  // Do the reduction...
  ReduceMiscompilingFunctions(*this).reduceList(MiscompiledFunctions);

  std::cout << "\n*** The following function"
            << (MiscompiledFunctions.size() == 1 ? " is" : "s are")
            << " being miscompiled: ";
  PrintFunctionList(MiscompiledFunctions);
  std::cout << "\n";

  // See if we can rip any loops out of the miscompiled functions and still
  // trigger the problem.
  if (ExtractLoops(*this, MiscompiledFunctions)) {
    // Okay, we extracted some loops and the problem still appears.  See if we
    // can eliminate some of the created functions from being candidates.

    // Do the reduction...
    ReduceMiscompilingFunctions(*this).reduceList(MiscompiledFunctions);

    std::cout << "\n*** The following function"
              << (MiscompiledFunctions.size() == 1 ? " is" : "s are")
              << " being miscompiled: ";
    PrintFunctionList(MiscompiledFunctions);
    std::cout << "\n";
  }

  // Output a bunch of bytecode files for the user...
  std::cout << "Outputting reduced bytecode files which expose the problem:\n";
  Module *ToNotOptimize = CloneModule(getProgram());
  Module *ToOptimize = SplitFunctionsOutOfModule(ToNotOptimize,
                                                 MiscompiledFunctions);

  std::cout << "  Non-optimized portion: ";
  std::swap(Program, ToNotOptimize);
  EmitProgressBytecode("tonotoptimize", true);
  setNewProgram(ToNotOptimize);   // Delete hacked module.

  std::cout << "  Portion that is input to optimizer: ";
  std::swap(Program, ToOptimize);
  EmitProgressBytecode("tooptimize");
  setNewProgram(ToOptimize);      // Delete hacked module.

  return false;
}

<commit_msg>minor formatting change<commit_after>//===- Miscompilation.cpp - Debug program miscompilations -----------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file was developed by the LLVM research group and is distributed under
// the University of Illinois Open Source License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements program miscompilation debugging support.
//
//===----------------------------------------------------------------------===//

#include "BugDriver.h"
#include "ListReducer.h"
#include "llvm/Module.h"
#include "llvm/Pass.h"
#include "llvm/Transforms/Utils/Cloning.h"
#include "llvm/Transforms/Utils/Linker.h"
#include "Support/FileUtilities.h"
using namespace llvm;

namespace {
  class ReduceMiscompilingPasses : public ListReducer<const PassInfo*> {
    BugDriver &BD;
  public:
    ReduceMiscompilingPasses(BugDriver &bd) : BD(bd) {}

    virtual TestResult doTest(std::vector<const PassInfo*> &Prefix,
                              std::vector<const PassInfo*> &Suffix);
  };
}

ReduceMiscompilingPasses::TestResult
ReduceMiscompilingPasses::doTest(std::vector<const PassInfo*> &Prefix,
                                 std::vector<const PassInfo*> &Suffix) {
  // First, run the program with just the Suffix passes.  If it is still broken
  // with JUST the kept passes, discard the prefix passes.
  std::cout << "Checking to see if '" << getPassesString(Suffix)
            << "' compile correctly: ";

  std::string BytecodeResult;
  if (BD.runPasses(Suffix, BytecodeResult, false/*delete*/, true/*quiet*/)) {
    std::cerr << " Error running this sequence of passes"
              << " on the input program!\n";
    BD.setPassesToRun(Suffix);
    BD.EmitProgressBytecode("pass-error",  false);
    exit(BD.debugOptimizerCrash());
  }

  // Check to see if the finished program matches the reference output...
  if (BD.diffProgram(BytecodeResult, "", true /*delete bytecode*/)) {
    std::cout << "nope.\n";
    return KeepSuffix;        // Miscompilation detected!
  }
  std::cout << "yup.\n";      // No miscompilation!

  if (Prefix.empty()) return NoFailure;

  // Next, see if the program is broken if we run the "prefix" passes first,
  // then separately run the "kept" passes.
  std::cout << "Checking to see if '" << getPassesString(Prefix)
            << "' compile correctly: ";

  // If it is not broken with the kept passes, it's possible that the prefix
  // passes must be run before the kept passes to break it.  If the program
  // WORKS after the prefix passes, but then fails if running the prefix AND
  // kept passes, we can update our bytecode file to include the result of the
  // prefix passes, then discard the prefix passes.
  //
  if (BD.runPasses(Prefix, BytecodeResult, false/*delete*/, true/*quiet*/)) {
    std::cerr << " Error running this sequence of passes"
              << " on the input program!\n";
    BD.setPassesToRun(Prefix);
    BD.EmitProgressBytecode("pass-error",  false);
    exit(BD.debugOptimizerCrash());
  }

  // If the prefix maintains the predicate by itself, only keep the prefix!
  if (BD.diffProgram(BytecodeResult)) {
    std::cout << "nope.\n";
    removeFile(BytecodeResult);
    return KeepPrefix;
  }
  std::cout << "yup.\n";      // No miscompilation!

  // Ok, so now we know that the prefix passes work, try running the suffix
  // passes on the result of the prefix passes.
  //
  Module *PrefixOutput = ParseInputFile(BytecodeResult);
  if (PrefixOutput == 0) {
    std::cerr << BD.getToolName() << ": Error reading bytecode file '"
              << BytecodeResult << "'!\n";
    exit(1);
  }
  removeFile(BytecodeResult);  // No longer need the file on disk

  std::cout << "Checking to see if '" << getPassesString(Suffix)
            << "' passes compile correctly after the '"
            << getPassesString(Prefix) << "' passes: ";

  Module *OriginalInput = BD.swapProgramIn(PrefixOutput);
  if (BD.runPasses(Suffix, BytecodeResult, false/*delete*/, true/*quiet*/)) {
    std::cerr << " Error running this sequence of passes"
              << " on the input program!\n";
    BD.setPassesToRun(Suffix);
    BD.EmitProgressBytecode("pass-error",  false);
    exit(BD.debugOptimizerCrash());
  }

  // Run the result...
  if (BD.diffProgram(BytecodeResult, "", true/*delete bytecode*/)) {
    std::cout << "nope.\n";
    delete OriginalInput;     // We pruned down the original input...
    return KeepSuffix;
  }

  // Otherwise, we must not be running the bad pass anymore.
  std::cout << "yup.\n";      // No miscompilation!
  delete BD.swapProgramIn(OriginalInput); // Restore orig program & free test
  return NoFailure;
}

namespace {
  class ReduceMiscompilingFunctions : public ListReducer<Function*> {
    BugDriver &BD;
  public:
    ReduceMiscompilingFunctions(BugDriver &bd) : BD(bd) {}

    virtual TestResult doTest(std::vector<Function*> &Prefix,
                              std::vector<Function*> &Suffix) {
      if (!Suffix.empty() && TestFuncs(Suffix))
        return KeepSuffix;
      if (!Prefix.empty() && TestFuncs(Prefix))
        return KeepPrefix;
      return NoFailure;
    }

    bool TestFuncs(const std::vector<Function*> &Prefix);
  };
}

/// TestMergedProgram - Given two modules, link them together and run the
/// program, checking to see if the program matches the diff.  If the diff
/// matches, return false, otherwise return true.  If the DeleteInputs argument
/// is set to true then this function deletes both input modules before it
/// returns.
static bool TestMergedProgram(BugDriver &BD, Module *M1, Module *M2,
                              bool DeleteInputs) {
  // Link the two portions of the program back to together.
  std::string ErrorMsg;
  if (!DeleteInputs) M1 = CloneModule(M1);
  if (LinkModules(M1, M2, &ErrorMsg)) {
    std::cerr << BD.getToolName() << ": Error linking modules together:"
              << ErrorMsg << "\n";
    exit(1);
  }
  if (DeleteInputs) delete M2;  // We are done with this module...

  Module *OldProgram = BD.swapProgramIn(M1);

  // Execute the program.  If it does not match the expected output, we must
  // return true.
  bool Broken = BD.diffProgram();

  // Delete the linked module & restore the original
  BD.swapProgramIn(OldProgram);
  delete M1;
  return Broken;
}

bool ReduceMiscompilingFunctions::TestFuncs(const std::vector<Function*>&Funcs){
  // Test to see if the function is misoptimized if we ONLY run it on the
  // functions listed in Funcs.
  std::cout << "Checking to see if the program is misoptimized when "
            << (Funcs.size()==1 ? "this function is" : "these functions are")
            << " run through the pass"
            << (BD.getPassesToRun().size() == 1 ? "" : "es") << ":";
  PrintFunctionList(Funcs);
  std::cout << "\n";

  // Split the module into the two halves of the program we want.
  Module *ToNotOptimize = CloneModule(BD.getProgram());
  Module *ToOptimize = SplitFunctionsOutOfModule(ToNotOptimize, Funcs);

  // Run the optimization passes on ToOptimize, producing a transformed version
  // of the functions being tested.
  std::cout << "  Optimizing functions being tested: ";
  Module *Optimized = BD.runPassesOn(ToOptimize, BD.getPassesToRun(),
                                     /*AutoDebugCrashes*/true);
  std::cout << "done.\n";
  delete ToOptimize;

  std::cout << "  Checking to see if the merged program executes correctly: ";
  bool Broken = TestMergedProgram(BD, Optimized, ToNotOptimize, true);
  std::cout << (Broken ? " nope.\n" : " yup.\n");
  return Broken;
}

/// ExtractLoops - Given a reduced list of functions that still exposed the bug,
/// check to see if we can extract the loops in the region without obscuring the
/// bug.  If so, it reduces the amount of code identified.
static bool ExtractLoops(BugDriver &BD,
                         std::vector<Function*> &MiscompiledFunctions) {
  bool MadeChange = false;
  while (1) {
    Module *ToNotOptimize = CloneModule(BD.getProgram());
    Module *ToOptimize = SplitFunctionsOutOfModule(ToNotOptimize,
                                                   MiscompiledFunctions);
    Module *ToOptimizeLoopExtracted = BD.ExtractLoop(ToOptimize);
    if (!ToOptimizeLoopExtracted) {
      // If the loop extractor crashed or if there were no extractible loops,
      // then this chapter of our odyssey is over with.
      delete ToNotOptimize;
      delete ToOptimize;
      return MadeChange;
    }

    std::cerr << "Extracted a loop from the breaking portion of the program.\n";
    delete ToOptimize;

    // Bugpoint is intentionally not very trusting of LLVM transformations.  In
    // particular, we're not going to assume that the loop extractor works, so
    // we're going to test the newly loop extracted program to make sure nothing
    // has broken.  If something broke, then we'll inform the user and stop
    // extraction.
    if (TestMergedProgram(BD, ToOptimizeLoopExtracted, ToNotOptimize, false)) {
      // Merged program doesn't work anymore!
      std::cerr << "  *** ERROR: Loop extraction broke the program. :("
                << " Please report a bug!\n";
      std::cerr << "      Continuing on with un-loop-extracted version.\n";
      delete ToNotOptimize;
      delete ToOptimizeLoopExtracted;
      return MadeChange;
    }

    // Okay, the loop extractor didn't break the program.  Run the series of
    // optimizations on the loop extracted portion and see if THEY still break
    // the program.  If so, it was safe to extract these loops!
    std::cout << "  Running optimizations on loop extracted portion: ";
    Module *Optimized = BD.runPassesOn(ToOptimizeLoopExtracted,
                                       BD.getPassesToRun(),
                                       /*AutoDebugCrashes*/true);
    std::cout << "done.\n";

    std::cout << "  Checking to see if the merged program executes correctly: ";
    bool Broken = TestMergedProgram(BD, Optimized, ToNotOptimize, false);
    delete Optimized;
    if (!Broken) {
      std::cout << "yup: loop extraction masked the problem.  Undoing.\n";
      // If the program is not still broken, then loop extraction did something
      // that masked the error.  Stop loop extraction now.
      delete ToNotOptimize;
      delete ToOptimizeLoopExtracted;
      return MadeChange;
    }
    std::cout << "nope: loop extraction successful!\n";

    // Okay, great!  Now we know that we extracted a loop and that loop
    // extraction both didn't break the program, and didn't mask the problem.
    // Replace the current program with the loop extracted version, and try to
    // extract another loop.
    std::string ErrorMsg;
    if (LinkModules(ToNotOptimize, ToOptimizeLoopExtracted, &ErrorMsg)) {
      std::cerr << BD.getToolName() << ": Error linking modules together:"
                << ErrorMsg << "\n";
      exit(1);
    }

    // All of the Function*'s in the MiscompiledFunctions list are in the old
    // module.  Update this list to include all of the functions in the
    // optimized and loop extracted module.
    MiscompiledFunctions.clear();
    for (Module::iterator I = ToOptimizeLoopExtracted->begin(),
           E = ToOptimizeLoopExtracted->end(); I != E; ++I) {
      if (!I->isExternal()) {
        Function *NewF = ToNotOptimize->getFunction(I->getName(),
                                                    I->getFunctionType());
        assert(NewF && "Function not found??");
        MiscompiledFunctions.push_back(NewF);
      }
    }
    delete ToOptimizeLoopExtracted;

    BD.setNewProgram(ToNotOptimize);
    MadeChange = true;
  }
}

/// debugMiscompilation - This method is used when the passes selected are not
/// crashing, but the generated output is semantically different from the
/// input.
///
bool BugDriver::debugMiscompilation() {
  // Make sure something was miscompiled...
  if (!ReduceMiscompilingPasses(*this).reduceList(PassesToRun)) {
    std::cerr << "*** Optimized program matches reference output!  No problem "
	      << "detected...\nbugpoint can't help you with your problem!\n";
    return false;
  }

  std::cout << "\n*** Found miscompiling pass"
            << (getPassesToRun().size() == 1 ? "" : "es") << ": "
            << getPassesString(getPassesToRun()) << "\n";
  EmitProgressBytecode("passinput");

  // Okay, now that we have reduced the list of passes which are causing the
  // failure, see if we can pin down which functions are being
  // miscompiled... first build a list of all of the non-external functions in
  // the program.
  std::vector<Function*> MiscompiledFunctions;
  for (Module::iterator I = Program->begin(), E = Program->end(); I != E; ++I)
    if (!I->isExternal())
      MiscompiledFunctions.push_back(I);

  // Do the reduction...
  ReduceMiscompilingFunctions(*this).reduceList(MiscompiledFunctions);

  std::cout << "\n*** The following function"
            << (MiscompiledFunctions.size() == 1 ? " is" : "s are")
            << " being miscompiled: ";
  PrintFunctionList(MiscompiledFunctions);
  std::cout << "\n";

  // See if we can rip any loops out of the miscompiled functions and still
  // trigger the problem.
  if (ExtractLoops(*this, MiscompiledFunctions)) {
    // Okay, we extracted some loops and the problem still appears.  See if we
    // can eliminate some of the created functions from being candidates.

    // Do the reduction...
    ReduceMiscompilingFunctions(*this).reduceList(MiscompiledFunctions);

    std::cout << "\n*** The following function"
              << (MiscompiledFunctions.size() == 1 ? " is" : "s are")
              << " being miscompiled: ";
    PrintFunctionList(MiscompiledFunctions);
    std::cout << "\n";
  }

  // Output a bunch of bytecode files for the user...
  std::cout << "Outputting reduced bytecode files which expose the problem:\n";
  Module *ToNotOptimize = CloneModule(getProgram());
  Module *ToOptimize = SplitFunctionsOutOfModule(ToNotOptimize,
                                                 MiscompiledFunctions);

  std::cout << "  Non-optimized portion: ";
  std::swap(Program, ToNotOptimize);
  EmitProgressBytecode("tonotoptimize", true);
  setNewProgram(ToNotOptimize);   // Delete hacked module.

  std::cout << "  Portion that is input to optimizer: ";
  std::swap(Program, ToOptimize);
  EmitProgressBytecode("tooptimize");
  setNewProgram(ToOptimize);      // Delete hacked module.

  return false;
}

<|endoftext|>