pelib  2.0.0
src/GraphML.cpp
Go to the documentation of this file.
00001 /*
00002  Copyright 2015 Nicolas Melot, Johan Janzén
00003 
00004  This file is part of Pelib.
00005 
00006  Pelib is free software: you can redistribute it and/or modify
00007  it under the terms of the GNU General Public License as published by
00008  the Free Software Foundation, either version 3 of the License, or
00009  (at your option) any later version.
00010 
00011  Pelib is distributed in the hope that it will be useful,
00012  but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
00014  GNU General Public License for more details.
00015 
00016  You should have received a copy of the GNU General Public License
00017  along with Pelib. If not, see <http://www.gnu.org/licenses/>.
00018 */
00019 
00020 
00021 #include <libxml++/libxml++.h>
00022 #include <exception>
00023 #include <vector>
00024 #include <sstream>
00025 #include <cstdlib>
00026 #include <boost/algorithm/string.hpp>
00027 
00028 #include <signal.h>
00029 #include <pthread.h>
00030 extern "C"{
00031 #include <igraph.h>
00032 }
00033 
00034 #include <pelib/Taskgraph.hpp>
00035 #include <pelib/GraphML.hpp>
00036 
00037 #include <pelib/Scalar.hpp>
00038 #include <pelib/Vector.hpp>
00039 #include <pelib/Matrix.hpp>
00040 #include <pelib/Set.hpp>
00041 #include <pelib/Task.hpp>
00042 #include <pelib/Link.hpp>
00043 
00044 #include <pelib/CastException.hpp>
00045 #include <pelib/ParseException.hpp>
00046 
00047 #ifndef debug
00048 #define debug(expr) cout << "[" << __FILE__ << ":" << __FUNCTION__ << ":" << __LINE__ << "] " << #expr << " = \"" << expr << "\"." << endl;
00049 #endif
00050 
00051 using namespace pelib;
00052 using namespace std;
00053 using namespace xmlpp;
00054 
00055 struct reader_args
00056 {
00057         FILE *instream;
00058         ostream *o;
00059 };
00060 typedef struct reader_args reader_args_t;
00061 
00062 const string GraphML::producerName = "producer_name";
00063 const string GraphML::consumerName = "consumer_name";
00064 const string GraphML::type = "type";
00065 const string GraphML::producer_rate = "producer_rate";
00066 const string GraphML::consumer_rate = "consumer_rate";
00067 
00068 static void*
00069 thread_reader(void* aux)
00070 {
00071         reader_args_t *args = (reader_args_t*)aux;
00072         
00073         char c;
00074         while ((c = fgetc (args->instream)) != EOF)
00075         {
00076                 (*args->o) << c;
00077         }
00078 
00079         fclose(args->instream);
00080         return NULL;
00081 }
00082 
00083 struct writer_args
00084 {
00085         int file_descr;
00086         istream *i;
00087 };
00088 typedef struct writer_args writer_args_t;
00089 
00090 static void*
00091 thread_writer(void* aux)
00092 {
00093         writer_args_t *args = (writer_args_t*)aux;
00094 
00095         char c = args->i->get();
00096         while(!args->i->eof())
00097         {
00098                 size_t ans = write(args->file_descr, &c, 1);
00099                 
00100                 if(ans < 0)
00101                 {
00102                         throw runtime_error("Error: Pipe has been possessed X-(");
00103                 }
00104                 
00105                 c = args->i->get();
00106         }
00107         
00108         close(args->file_descr);
00109         return NULL;
00110 }
00111 
00112 GraphML::~GraphML()
00113 {
00114         // Do nothing
00115 }
00116 
00117 void
00118 GraphML::dump(ostream& os, const Taskgraph *data, const Platform *arch) const
00119 {
00120         const Taskgraph *tg = dynamic_cast<const Taskgraph* >(data);
00121         if(tg == NULL) throw CastException("Parameter \"data\" was not of type \"Taskgraph*\".");
00122 
00123         igraph_t *graph = new igraph_t();
00124         igraph_i_set_attribute_table(&igraph_cattribute_table); //do this to enable attribute fetching
00125 
00126         /*
00127         igraph_vector_init(tg, 8);
00128         
00129         VECTOR(v1)[2]=1; VECTOR(v1)[3]=2;
00130         VECTOR(v1)[4]=2; VECTOR(v1)[5]=3;
00131         VECTOR(v1)[6]=2; VECTOR(v1)[7]=2;
00132           */
00133         igraph_vector_t edges;
00134         igraph_vector_init(&edges, 0);
00135         //VECTOR(edges)[0]=0; VECTOR(edges)[1]=1;
00136         igraph_create(graph, &edges, 0, true);
00137 
00138         // Add graph attributes
00139         SETGAS(graph,"name", tg->getName().c_str());
00140         SETGAS(graph,"deadline", tg->getDeadlineCalculator().c_str());
00141 
00142         // Add vertices
00143         int ret = igraph_add_vertices(graph, tg->getTasks().size(), 0);
00144         if(ret == IGRAPH_EINVAL) throw CastException("Could not add vertices to igraph.");
00145         if(arch != NULL && !(arch->isHomogeneous()))
00146         {
00147                 throw CastException("Cannot output discretive efficiency function for a heterogeneous platform.");
00148         }
00149 
00150         size_t counter = 0;
00151         for(set<Task>::const_iterator i = tg->getTasks().begin(); i != tg->getTasks().end(); i++, counter++)
00152         {
00153                 Task task = *i;
00154                 
00155                 SETVAS(graph, "name", counter, task.getName().c_str());
00156                 SETVAS(graph, "module", counter, task.getModule().c_str());
00157                 SETVAN(graph, "workload", counter, task.getWorkload());
00158                 SETVAN(graph, "streaming", counter, task.isStreaming());
00159                 stringstream max_width;
00160 
00161                 // If no platform is provided, just dump efficiency and max width as is.
00162                 // Otherwise, compute and output each possible value one by one, and
00163                 // replace any larger value of max_width than number of core, by number of cores 
00164                 if (arch == NULL)
00165                 {
00166                         SETVAS(graph, "efficiency", counter, task.getEfficiencyString().c_str());
00167                         max_width << task.getMaxWidth();
00168                 }
00169                 else
00170                 {
00171                         stringstream ss;
00172                         for(size_t j = 1; j <= arch->getCores().size(); j++)
00173                         {
00174                                 ss << task.getEfficiency(j) << " ";
00175                         }
00176 
00177                         SETVAS(graph, "efficiency", counter, ss.str().c_str());
00178                         if(task.getMaxWidth() >= arch->getCores().size())
00179                         {
00180                                 max_width << arch->getCores().size();
00181                         }
00182                         else
00183                         {
00184                                 max_width << task.getMaxWidth();
00185                         }
00186                 }
00187 
00188                 SETVAS(graph, "max_width", counter, max_width.str().c_str());
00189         }
00190 
00191         counter = 0;
00192         for(set<Link>::const_iterator i = tg->getLinks().begin(); i != tg->getLinks().end(); i++, counter++)
00193         {
00194                 int ret = igraph_add_edge(graph, std::distance(tg->getTasks().begin(), tg->getTasks().find(*i->getProducer())), std::distance(tg->getTasks().begin(), tg->getTasks().find(*i->getConsumer())));
00195                 if(ret == IGRAPH_EINVAL) throw CastException("Could not add vertices to igraph.");
00196                 SETEAS(graph, producerName.c_str(), counter, i->getProducerName().c_str());
00197                 SETEAS(graph, consumerName.c_str(), counter, i->getConsumerName().c_str());
00198                 if(i->getDataType().compare(string()) != 0)
00199                 {
00200                         SETEAS(graph, type.c_str(), counter, i->getDataType().c_str());
00201                 }
00202                 if(i->getProducerRate() > 0)
00203                 {
00204                         SETEAN(graph, GraphML::producer_rate.c_str(), counter, i->getProducerRate());
00205                 }
00206                 if(i->getConsumerRate() > 0)
00207                 {
00208                         SETEAN(graph, GraphML::consumer_rate.c_str(), counter, i->getConsumerRate());
00209                 }
00210         }
00211 
00212         // Dump data to stream os
00213         int p[2];
00214         int ans = pipe(p);
00215         if(ans)
00216         {
00217                 throw runtime_error("Error: no pipe :/");
00218         }
00219 
00220         FILE *fake_fileptr = fdopen(p[1], "w"); 
00221         FILE *instream = fdopen (p[0], "r");
00222 
00223         // Spawn a thread that reads from the pipe as fast as it can.
00224         // This is required if igraph indend to output massive amounts of data
00225         pthread_attr_t attr;
00226         pthread_t thread;
00227         reader_args_t args;
00228         args.instream = instream;
00229         args.o = &os;
00230 
00231         pthread_attr_init(&attr);
00232         pthread_create(&thread, &attr, &thread_reader, (void*) &args);
00233 
00234         igraph_write_graph_graphml(graph, fake_fileptr, true); 
00235         fclose(fake_fileptr);
00236 
00237         pthread_join(thread, NULL);
00238         //fclose (instream);
00239         close(p[0]);
00240 
00241         igraph_destroy(graph);
00242         delete graph;
00243 }
00244 
00245 void
00246 GraphML::dump(ostream& os, const Taskgraph *data) const
00247 {
00248         dump(os, data, NULL);
00249 }
00250 
00251 void
00252 GraphML::dump(ostream& os, const Taskgraph &data) const
00253 {
00254         dump(os, &data, NULL);
00255 }
00256 
00257 void
00258 GraphML::dump(ostream& os, const Taskgraph &data, const Platform &arch) const
00259 {
00260         dump(os, &data, &arch);
00261 }
00262 
00263 Taskgraph*
00264 GraphML::parse(istream &is) const
00265 {
00266         // Open the file and get an igraph record
00267         // Initialize igraph and build a new object
00268         igraph_i_set_attribute_table(&igraph_cattribute_table); //do this to enable attribute fetching
00269         igraph_t *the_graph = new igraph_t();
00270 
00271         // Create a FILE* from istream by building a posix pipe. This wont work in windows...
00272         int p[2];
00273         int ans = pipe(p);
00274         if(ans)
00275         {
00276                 throw runtime_error("Error: no pipe :/");
00277         }
00278 
00279         FILE *fake_fileptr = fdopen(p[0], "r");   
00280 
00281         pthread_attr_t attr;
00282         pthread_t thread;
00283         writer_args_t args;
00284         args.file_descr = p[1];
00285         args.i = &is;
00286 
00287         pthread_attr_init(&attr);
00288         pthread_create(&thread, &attr, &thread_writer, (void*) &args);
00289 
00290 //      char ch;
00291 //      while( ( ch = fgetc(fake_fileptr) ) != EOF )
00292 //                      printf("%c",ch);
00293         // Parse input file
00294         igraph_read_graph_graphml(the_graph, fake_fileptr, 0);
00295 
00296         // Clone the file and wait for the pipe to finish
00297         close(p[0]);
00298         pthread_join(thread, NULL);
00299 
00300         set<Task> tasks;
00301         for(int id = 0; id < igraph_vcount(the_graph); id++)
00302         {
00303                 stringstream estr;
00304                 estr << "task_" << id;
00305                 bool streaming = true;
00306                 if(igraph_cattribute_has_attr(the_graph, IGRAPH_ATTRIBUTE_VERTEX, "streaming"))
00307                 {
00308                         streaming = (bool)VAN(the_graph, "streaming", id);
00309                 }
00310                 Task task(strcmp(VAS(the_graph, "name", id), "") != 0 ? VAS(the_graph, "name", id) : estr.str(), streaming);
00311                 task.setModule(strcmp(VAS(the_graph, "module", id),"") != 0 ? VAS(the_graph, "module", id) : "dummy");
00312                 task.setWorkload(!isnan((float)VAN(the_graph, "workload", id)) ? VAN(the_graph, "workload", id): 1.0);
00313 
00314                 const char *str = VAS(the_graph, "max_width", id);
00315                 string max_width_str(str);
00316                 boost::algorithm::to_lower(max_width_str);
00317                 boost::algorithm::trim(max_width_str);
00318                 float max_width;
00319 
00320                 if(max_width_str.compare("inf") == 0 || max_width_str.compare("+inf") == 0)
00321                 {
00322                         max_width = std::numeric_limits<float>::infinity();
00323                 }
00324                 else
00325                 {
00326                         if(max_width_str.compare("-inf") == 0)
00327                         {
00328                                 max_width = 1;
00329                         }
00330                         else
00331                         {
00332                                 std::istringstream converter(max_width_str);
00333                                 converter >> max_width;
00334 
00335                                 if(converter.fail())
00336                                 {
00337                                         throw ParseException(std::string("Couln't convert literal \"").append(max_width_str).append("\" into type \"").append(typeid(max_width).name()).append("\"."));
00338                                 }
00339                         }
00340                 }
00341                 task.setMaxWidth(max_width);
00342                 //task.setMaxWidth((int)VAN(the_graph, "max_width", id) != INT_MIN ? VAN(the_graph, "max_width", id) : 1);
00343 
00344                 if (strcmp(VAS(the_graph, "efficiency", id), "") != 0)
00345                 {
00346                         task.setEfficiencyString(VAS(the_graph, "efficiency", id));
00347                 }
00348                 else
00349                 {
00350                         stringstream ss;
00351                         ss << task.getMaxWidth();
00352                         task.setEfficiencyString(string("exprtk:p <= ") + ss.str() + "? 1 : 1e-6");
00353                 }
00354 
00355                 tasks.insert(Task(task));
00356         }
00357 
00358         // Add edges between tasks
00359         set<Link> links;
00360         for(int i = 0; i < igraph_ecount(the_graph); i++)
00361         {
00362                 //printf("[%s:%s:%d] Edge number %d.\n", __FILE__, __FUNCTION__, __LINE__, i);
00363                 int producer_id, consumer_id;
00364 
00365                 igraph_edge(the_graph, i, &producer_id, &consumer_id);
00366                 Task producer(VAS(the_graph, "name", producer_id));
00367                 Task consumer(VAS(the_graph, "name", consumer_id));
00368                 string producerName = string(EAS(the_graph, GraphML::producerName.c_str(), i));
00369                 string consumerName = string(EAS(the_graph, GraphML::consumerName.c_str(), i));
00370                 string type("");
00371                 if(igraph_cattribute_has_attr(the_graph, IGRAPH_ATTRIBUTE_EDGE, GraphML::type.c_str()))
00372                 {
00373                         type = string(EAS(the_graph, GraphML::type.c_str(), i));
00374                 }
00375                 size_t producer_rate = 0, consumer_rate = 0;
00376                 if(igraph_cattribute_has_attr(the_graph, IGRAPH_ATTRIBUTE_EDGE, GraphML::producer_rate.c_str()))
00377                 {
00378                         producer_rate = EAN(the_graph, GraphML::producer_rate.c_str(), i);
00379                 }
00380                 if(igraph_cattribute_has_attr(the_graph, IGRAPH_ATTRIBUTE_EDGE, GraphML::consumer_rate.c_str()))
00381                 {
00382                         consumer_rate = EAN(the_graph, GraphML::consumer_rate.c_str(), i);
00383                 }
00384 
00385                 Link link(*tasks.find(producer), *tasks.find(consumer), producerName, consumerName, type, producer_rate, consumer_rate);
00386                 links.insert(link);
00387 
00388                 const Link &link_ref = *links.find(link);
00389 
00390                 // Add the link as producer and consumer links of both endpoint tasks
00391                 Task &producer_ref = (Task&)*tasks.find(producer);
00392                 producer_ref.getConsumers().insert(&link_ref);
00393                 Task &consumer_ref = (Task&)*tasks.find(consumer);
00394                 consumer_ref.getProducers().insert(&link_ref);
00395 
00396                 /*
00397                 trace(producer_ref.getName());
00398                 trace(consumer_ref.getName());
00399                 trace(producer_ref.getConsumers().size());
00400                 trace(consumer_ref.getConsumers().size());
00401                 trace(producer_ref.getProducers().size());
00402                 trace(consumer_ref.getProducers().size());
00403                 */
00404         }
00405 
00406         // Finally build the taskgraph
00407         Taskgraph *tg = new Taskgraph(tasks, links);
00408         tg->setName(GAS(the_graph, "name"));
00409         tg->setDeadlineCalculator(GAS(the_graph, "deadline"));
00410 
00411         // Cleanup
00412         igraph_destroy(the_graph);
00413         delete the_graph;
00414 
00415         return tg;
00416 }
00417 
00418 GraphML*
00419 GraphML::clone() const
00420 {
00421         return new GraphML();
00422 }