ROSE  0.9.10.54
BinarySerialIo.h
1 #ifndef Rose_BinaryAnalysis_SerialIo_H
2 #define Rose_BinaryAnalysis_SerialIo_H
3 
4 #include <Progress.h>
5 #include <boost/filesystem.hpp>
6 #include <boost/lexical_cast.hpp>
7 #include <boost/thread.hpp>
8 #include <Sawyer/Message.h>
9 #include <Sawyer/ProgressBar.h>
10 #include <Sawyer/Synchronization.h>
11 
12 #if defined(BOOST_WINDOWS)
13  // Lacks POSIX file system, so we can't monitor the I/O progress
14  #undef ROSE_SUPPORTS_SERIAL_IO
15 #elif !defined(ROSE_HAVE_BOOST_SERIALIZATION_LIB)
16  // Lacks Boost's serialization library, which is how we convert objects to bytes and vice versa
17  #undef ROSE_SUPPORTS_SERIAL_IO
18 #elif defined(__GNUC__)
19  #if __GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNU_C_PATCHLEVEL__ <= 40204
20  // GCC <= 4.2.4 gets segfaults compiling this file
21  #undef ROSE_SUPPORTS_SERIAL_IO
22  #else
23  #define ROSE_SUPPORTS_SERIAL_IO /*supported*/
24  #endif
25 #else
26  #define ROSE_SUPPORTS_SERIAL_IO /*supported*/
27 #endif
28 
29 #ifdef ROSE_SUPPORTS_SERIAL_IO
30 #include <boost/archive/binary_iarchive.hpp>
31 #include <boost/archive/binary_oarchive.hpp>
32 #include <boost/archive/text_iarchive.hpp>
33 #include <boost/archive/text_oarchive.hpp>
34 #include <boost/archive/xml_iarchive.hpp>
35 #include <boost/archive/xml_oarchive.hpp>
36 #include <boost/iostreams/device/file_descriptor.hpp>
37 #include <boost/iostreams/stream.hpp>
38 #endif
39 
40 namespace Rose {
41 namespace BinaryAnalysis {
42 
43 namespace Partitioner2 {
44 class Partitioner;
45 }
46 
48 // SerialIo
50 
108 public:
111 
113  enum Format {
121  };
122 
124  enum Savable {
125  NO_OBJECT = 0x00000000,
126  PARTITIONER = 0x00000001,
127  AST = 0x00000002,
128  END_OF_DATA = 0x0000fffe,
129  ERROR = 0x0000ffff,
130  USER_DEFINED = 0x00010000,
131  USER_DEFINED_LAST = 0xffffffff
132  };
133 
135  class Exception: public std::runtime_error {
136  public:
138  explicit Exception(const std::string &s): std::runtime_error(s) {}
139  ~Exception() throw() {}
140  };
141 
142 private:
143  mutable SAWYER_THREAD_TRAITS::Mutex mutex_; // protects the following data members
144  Format format_;
145  Progress::Ptr progress_;
146  bool isOpen_;
147  Savable objectType_;
148 
149 protected:
150  Sawyer::ProgressBar<size_t> progressBar_;
151 
152  // We use low-level file descriptors under an std::stream I/O interface in order to provide progress reports. This
153  // allows one thread to be reading from or writing to the file and another thread to monitor the file position to
154  // report progress. The C++ standard library doesn't have an API for wrapping file descriptors in a stream interface,
155  // so we use Boost.
156  int fd_;
157 
158 protected:
159  SerialIo()
160  : format_(BINARY), progress_(Progress::instance()), isOpen_(false), objectType_(NO_OBJECT),
161  progressBar_(mlog[Sawyer::Message::MARCH]), fd_(-1) {
162  init();
163  }
164 
165 public:
168 
176  virtual ~SerialIo();
177 
186  Format format() const;
187  void format(Format);
198  Progress::Ptr progress() const;
199  void progress(const Progress::Ptr&);
210  virtual void open(const boost::filesystem::path&) = 0;
211 
223  virtual void close() = 0;
224 
230  bool isOpen() const;
231 
236  Savable objectType() const;
237 
238 protected:
239  // Set or clear the isOpen flag.
240  void setIsOpen(bool b);
241 
242  // Set object type to ERROR to indicate that a read was unsuccessful
243  void objectType(Savable);
244 
245 private:
246  void init();
247 };
248 
249 
251 // SerialOutput
253 
257 class SerialOutput: public SerialIo {
258 public:
260 
261 private:
262 #ifdef ROSE_SUPPORTS_SERIAL_IO
263  boost::iostreams::file_descriptor_sink device_;
264  boost::iostreams::stream<boost::iostreams::file_descriptor_sink> file_;
265  boost::archive::binary_oarchive *binary_archive_;
266  boost::archive::text_oarchive *text_archive_;
267  boost::archive::xml_oarchive *xml_archive_;
268 #endif
269 
270 protected:
271 #ifdef ROSE_SUPPORTS_SERIAL_IO
272  SerialOutput(): binary_archive_(NULL), text_archive_(NULL), xml_archive_(NULL) {}
273 #else
274  SerialOutput() {}
275 #endif
276 
277 public:
278  ~SerialOutput();
279  void open(const boost::filesystem::path &fileName) ROSE_OVERRIDE;
280  void close() ROSE_OVERRIDE;
281 
286  static Ptr instance() { return Ptr(new SerialOutput); }
287 
298 
310  void saveAst(SgAsmNode*);
311  void saveAst(SgBinaryComposite*);
312 private:
313  // The saveAstHelper is what actually gets called for the functions above. It doesn't descriminate between nodes that
314  // support serialization and those that don't, which is why it's private. Use only the public functions because they'll
315  // give you a nice compiler error if you try to save an Ast node type that isn't supported.
316  void saveAstHelper(SgNode*);
317 public:
318 
333  template<class T>
334  void saveObject(Savable objectTypeId, const T &object) {
335  if (!isOpen())
336  throw Exception("cannot save object when no file is open");
337  if (ERROR == objectType())
338  throw Exception("cannot save object because stream is in error state");
339 
340 #ifndef ROSE_SUPPORTS_SERIAL_IO
341  throw Exception("binary state files are not supported in this configuration");
342 #else
343  // A different thread saves the object while this thread updates the progress
344  std::string errorMessage;
345  boost::thread worker(startWorker<T>, this, objectTypeId, object, &errorMessage);
346  boost::chrono::milliseconds timeout((unsigned)(1000 * Sawyer::ProgressBarSettings::minimumUpdateInterval()));
347  while (!worker.try_join_for(timeout)) {
348  off_t cur = ::lseek(fd_, 0, SEEK_CUR);
349  if (-1 == cur) {
350  ++progressBar_; // so a spinner moves
351  } else {
352  progressBar_.value(cur);
353  if (Progress::Ptr p = progress())
354  p->update(Progress::Report(cur, NAN));
355  }
356  }
357  if (!errorMessage.empty())
358  throw Exception(errorMessage);
359 #endif
360  }
361 
362 private:
363  template<class T>
364  static void startWorker(SerialOutput *saver, Savable objectTypeId, const T &object, std::string *errorMessage) {
365  saver->asyncSave(objectTypeId, object, errorMessage);
366  }
367 
368  // This might run in its own thread.
369  template<class T>
370  void asyncSave(Savable objectTypeId, const T &object, std::string *errorMessage) {
371  ASSERT_not_null(errorMessage);
372 #ifndef ROSE_SUPPORTS_SERIAL_IO
373  ASSERT_not_reachable("not supported in this configuration");
374 #else
375  try {
376  objectType(ERROR);
377  switch (format()) {
378  case BINARY:
379  ASSERT_not_null(binary_archive_);
380  *binary_archive_ <<BOOST_SERIALIZATION_NVP(objectTypeId);
381  *binary_archive_ <<BOOST_SERIALIZATION_NVP(object);
382  break;
383  case TEXT:
384  ASSERT_not_null(text_archive_);
385  *text_archive_ <<BOOST_SERIALIZATION_NVP(objectTypeId);
386  *text_archive_ <<BOOST_SERIALIZATION_NVP(object);
387  break;
388  case XML:
389  ASSERT_not_null(xml_archive_);
390  *xml_archive_ <<BOOST_SERIALIZATION_NVP(objectTypeId);
391  *xml_archive_ <<BOOST_SERIALIZATION_NVP(object);
392  break;
393  }
394  objectType(objectTypeId);
395  } catch (const Exception &e) {
396  *errorMessage = e.what();
397  } catch (...) {
398  *errorMessage = "failed to write object to output stream";
399  }
400 #endif
401  }
402 };
403 
404 
406 // SerialInput
408 
412 class SerialInput: public SerialIo {
413 public:
415 
416 private:
417 #ifdef ROSE_SUPPORTS_SERIAL_IO
418  size_t fileSize_;
419  boost::iostreams::file_descriptor_source device_;
420  boost::iostreams::stream<boost::iostreams::file_descriptor_source> file_;
421  boost::archive::binary_iarchive *binary_archive_;
422  boost::archive::text_iarchive *text_archive_;
423  boost::archive::xml_iarchive *xml_archive_;
424 #endif
425 
426 protected:
427 #ifdef ROSE_SUPPORTS_SERIAL_IO
428  SerialInput(): fileSize_(0), binary_archive_(NULL), text_archive_(NULL), xml_archive_(NULL) {}
429 #else
430  SerialInput() {}
431 #endif
432 
433 public:
434  ~SerialInput();
435  void open(const boost::filesystem::path &fileName) ROSE_OVERRIDE;
436  void close() ROSE_OVERRIDE;
437 
442  static Ptr instance() { return Ptr(new SerialInput); }
443 
452 
460 
468  SgNode* loadAst();
469 
470  template<class T>
471  T loadObject(Savable objectTypeId) {
472  if (!isOpen())
473  throw Exception("cannot load object when no file is open");
474 
475 #ifndef ROSE_SUPPORTS_SERIAL_IO
476  throw Exception("binary state files are not supported in this configuration");
477 #else
478  if (ERROR == objectType())
479  throw Exception("cannot read object because stream is in error state");
480  if (objectType() != objectTypeId) {
481  throw Exception("unexpected object type (expected " + boost::lexical_cast<std::string>(objectTypeId) +
482  " but read " + boost::lexical_cast<std::string>(objectType()) + ")");
483  }
484  objectType(ERROR); // in case of exception
485  T object;
486  std::string errorMessage;
487  boost::thread worker(startWorker<T>, this, &object, &errorMessage);
488  boost::chrono::milliseconds timeout((unsigned)(1000 * Sawyer::ProgressBarSettings::minimumUpdateInterval()));
489  while (!worker.try_join_for(timeout)) {
490  if (fileSize_ > 0) {
491  off_t cur = ::lseek(fd_, 0, SEEK_CUR);
492  if (cur != -1) {
493  progressBar_.value(cur);
494  if (Progress::Ptr p = progress())
495  p->update(Progress::Report(cur, fileSize_));
496  }
497  } else {
498  ++progressBar_; // so the spinner moves
499  }
500  }
501  if (!errorMessage.empty())
502  throw Exception(errorMessage);
503  advanceObjectType();
504  return object;
505 #endif
506  }
507 
508 private:
509  template<class T>
510  static void startWorker(SerialInput *loader, T *object, std::string *errorMessage) {
511  loader->asyncLoad(*object, errorMessage);
512  }
513 
514  // Might run in its own thread
515  template<class T>
516  void asyncLoad(T &object, std::string *errorMessage) {
517  ASSERT_not_null(errorMessage);
518 #ifndef ROSE_SUPPORTS_SERIAL_IO
519  ASSERT_not_reachable("not supported in this configuration");
520 #else
521  try {
522  switch (format()) {
523  case BINARY:
524  ASSERT_not_null(binary_archive_);
525  *binary_archive_ >>object;
526  break;
527  case TEXT:
528  ASSERT_not_null(text_archive_);
529  *text_archive_ >>object;
530  break;
531  case XML:
532  ASSERT_not_null(xml_archive_);
533  *xml_archive_ >>BOOST_SERIALIZATION_NVP(object);
534  break;
535  }
536  } catch (const Exception &e) {
537  *errorMessage = e.what();
538  } catch (...) {
539  *errorMessage = "failed to read object from input stream";
540  }
541 #endif
542  }
543 
544 protected:
545  // Read the next object type from the input stream
546  void advanceObjectType();
547 };
548 
549 } // namespace
550 } // namespace
551 
552 #endif
Savable
Types of objects that can be saved.
A single progress report.
Definition: Progress.h:178
double minimumUpdateInterval()
Minimum time between updates.
Base class for all binary analysis IR nodes.
Textual binary state files use a custom format (Boost serialization format) that stores the data as A...
void savePartitioner(const Partitioner2::Partitioner &)
Save a binary analysis partitioner.
void close() ROSE_OVERRIDE
Detach a file.
Collection of streams.
Definition: Message.h:1579
First user-defined object number.
void saveObject(Savable objectTypeId, const T &object)
Save an object to the output stream.
Format format() const
Property: File format.
Output binary analysis state.
STL namespace.
Rose::BinaryAnalysis::Partitioner2::Partitioner.
void saveAst(SgAsmNode *)
Save a binary AST.
static Sawyer::Message::Facility mlog
Message facility.
Main namespace for the ROSE library.
Base class for binary state input and output.
Progress::Ptr progress() const
Property: Progress reporter.
SgNode * loadAst()
Load an AST from the input stream.
Reference-counting smart pointer.
Definition: SharedPointer.h:34
Name space for the entire library.
Definition: Access.h:13
Object type for newly-initialized serializers.
Savable objectType() const
Type ID for next object.
Exception(const std::string &s)
Construct an exception with an error message.
virtual void open(const boost::filesystem::path &)=0
Attach a file.
Marks that the stream has encountered an error condition.
void open(const boost::filesystem::path &fileName) ROSE_OVERRIDE
Attach a file.
void close() ROSE_OVERRIDE
Detach a file.
static Ptr instance()
Factory method to create a new instance.
The states are stored as XML, which is a very verbose and slow format.
This class represents the base class for all IR nodes within Sage III.
Definition: Cxx_Grammar.h:8672
static Ptr instance()
Factory method to create a new instance.
void open(const boost::filesystem::path &fileName) ROSE_OVERRIDE
Attach a file.
ValueType value() const
Value for the progress bar.
Definition: ProgressBar.h:163
Partitioner2::Partitioner loadPartitioner()
Load a partitioner from the input stream.
bool isOpen() const
Whether a file is attached.
virtual ~SerialIo()
Destructor.
Base class for reference counted objects.
Definition: SharedObject.h:22
A general, thread-safe way to report progress made on some task.
Definition: Progress.h:164
Marks the end of the data stream.
Format
Format of the state file.
Partitions instructions into basic blocks and functions.
Definition: Partitioner.h:292
Binary state files are smaller and faster than the other formats, but are not portable across archite...
virtual void close()=0
Detach a file.
Input binary analysis state.
Sawyer::SharedPointer< SerialIo > Ptr
Reference-counting pointer.
Savable nextObjectType()
Type of next object in the input stream.