ROSE  0.9.11.56
BinarySerialIo.h
1 #ifndef Rose_BinaryAnalysis_SerialIo_H
2 #define Rose_BinaryAnalysis_SerialIo_H
3 
4 #include <Progress.h>
5 #include <RoseException.h>
6 #include <boost/filesystem.hpp>
7 #include <boost/lexical_cast.hpp>
8 #include <boost/thread.hpp>
9 #include <Sawyer/Message.h>
10 #include <Sawyer/ProgressBar.h>
11 #include <Sawyer/Synchronization.h>
12 
13 // Define this if you need to debug SerialIo -- it causes everything to run in the calling thread and avoid catching exceptions.
14 //#define ROSE_DEBUG_SERIAL_IO
15 
16 #if defined(BOOST_WINDOWS)
17  // Lacks POSIX file system, so we can't monitor the I/O progress
18  #undef ROSE_SUPPORTS_SERIAL_IO
19 #elif !defined(ROSE_HAVE_BOOST_SERIALIZATION_LIB)
20  // Lacks Boost's serialization library, which is how we convert objects to bytes and vice versa
21  #undef ROSE_SUPPORTS_SERIAL_IO
22 #elif defined(__GNUC__)
23  #if __GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNU_C_PATCHLEVEL__ <= 40204
24  // GCC <= 4.2.4 gets segfaults compiling this file
25  #undef ROSE_SUPPORTS_SERIAL_IO
26  #else
27  #define ROSE_SUPPORTS_SERIAL_IO /*supported*/
28  #endif
29 #else
30  #define ROSE_SUPPORTS_SERIAL_IO /*supported*/
31 #endif
32 
33 #ifdef ROSE_SUPPORTS_SERIAL_IO
34 #include <boost/archive/binary_iarchive.hpp>
35 #include <boost/archive/binary_oarchive.hpp>
36 #include <boost/archive/text_iarchive.hpp>
37 #include <boost/archive/text_oarchive.hpp>
38 #include <boost/archive/xml_iarchive.hpp>
39 #include <boost/archive/xml_oarchive.hpp>
40 #include <boost/iostreams/device/file_descriptor.hpp>
41 #include <boost/iostreams/stream.hpp>
42 #endif
43 
44 namespace Rose {
45 namespace BinaryAnalysis {
46 
47 namespace Partitioner2 {
48 class Partitioner;
49 }
50 
52 // SerialIo
54 
112 public:
115 
117  enum Format {
125  };
126 
128  enum Savable {
129  NO_OBJECT = 0x00000000,
130  PARTITIONER = 0x00000001,
131  AST = 0x00000002,
132  END_OF_DATA = 0x0000fffe,
133  ERROR = 0x0000ffff,
134  USER_DEFINED = 0x00010000,
135  USER_DEFINED_LAST = 0xffffffff
136  };
137 
139  class Exception: public Rose::Exception {
140  public:
142  explicit Exception(const std::string &s): Rose::Exception(s) {}
143  ~Exception() throw() {}
144  };
145 
146 private:
147  mutable SAWYER_THREAD_TRAITS::Mutex mutex_; // protects the following data members
148  Format format_;
149  Progress::Ptr progress_;
150  bool isOpen_;
151  Savable objectType_;
152 
153 protected:
154  Sawyer::ProgressBar<size_t> progressBar_;
155 
156  // We use low-level file descriptors under an std::stream I/O interface in order to provide progress reports. This
157  // allows one thread to be reading from or writing to the file and another thread to monitor the file position to
158  // report progress. The C++ standard library doesn't have an API for wrapping file descriptors in a stream interface,
159  // so we use Boost.
160  int fd_;
161 
162 protected:
163  SerialIo()
164  : format_(BINARY), progress_(Progress::instance()), isOpen_(false), objectType_(NO_OBJECT),
165  progressBar_(mlog[Sawyer::Message::MARCH]), fd_(-1) {
166  init();
167  progressBar_.suffix(" bytes");
168  }
169 
170 public:
173 
181  virtual ~SerialIo();
182 
186  static Savable userSavable(unsigned offset);
187 
196  Format format() const;
197  void format(Format);
208  Progress::Ptr progress() const;
209  void progress(const Progress::Ptr&);
220  virtual void open(const boost::filesystem::path&) = 0;
221 
233  virtual void close() = 0;
234 
240  bool isOpen() const;
241 
246  Savable objectType() const;
247 
248 protected:
249  // Set or clear the isOpen flag.
250  void setIsOpen(bool b);
251 
252  // Set object type to ERROR to indicate that a read was unsuccessful
253  void objectType(Savable);
254 
255 private:
256  void init();
257 };
258 
259 
261 // SerialOutput
263 
267 class SerialOutput: public SerialIo {
268 public:
270 
271 private:
272 #ifdef ROSE_SUPPORTS_SERIAL_IO
273  boost::iostreams::file_descriptor_sink device_;
274  boost::iostreams::stream<boost::iostreams::file_descriptor_sink> file_;
275  boost::archive::binary_oarchive *binary_archive_;
276  boost::archive::text_oarchive *text_archive_;
277  boost::archive::xml_oarchive *xml_archive_;
278 #endif
279 
280 protected:
281 #ifdef ROSE_SUPPORTS_SERIAL_IO
282  SerialOutput(): binary_archive_(NULL), text_archive_(NULL), xml_archive_(NULL) {}
283 #else
284  SerialOutput() {}
285 #endif
286 
287 public:
288  ~SerialOutput();
289  void open(const boost::filesystem::path &fileName) ROSE_OVERRIDE;
290  void close() ROSE_OVERRIDE;
291 
296  static Ptr instance() { return Ptr(new SerialOutput); }
297 
308 
320  void saveAst(SgAsmNode*);
321  void saveAst(SgBinaryComposite*);
322 private:
323  // The saveAstHelper is what actually gets called for the functions above. It doesn't descriminate between nodes that
324  // support serialization and those that don't, which is why it's private. Use only the public functions because they'll
325  // give you a nice compiler error if you try to save an Ast node type that isn't supported.
326  void saveAstHelper(SgNode*);
327 public:
328 
343  template<class T>
344  void saveObject(Savable objectTypeId, const T &object) {
345  if (!isOpen())
346  throw Exception("cannot save object when no file is open");
347  if (ERROR == objectType())
348  throw Exception("cannot save object because stream is in error state");
349 
350 #ifndef ROSE_SUPPORTS_SERIAL_IO
351  throw Exception("binary state files are not supported in this configuration");
352 #elif defined(ROSE_DEBUG_SERIAL_IO)
353  std::string errorMessage;
354  asyncSave(objectTypeId, object, &errorMessage);
355 #else
356  // A different thread saves the object while this thread updates the progress
357  std::string errorMessage;
358  boost::thread worker(startWorker<T>, this, objectTypeId, &object, &errorMessage);
359  boost::chrono::milliseconds timeout((unsigned)(1000 * Sawyer::ProgressBarSettings::minimumUpdateInterval()));
360  progressBar_.prefix("writing");
361  while (!worker.try_join_for(timeout)) {
362  off_t cur = ::lseek(fd_, 0, SEEK_CUR);
363  if (-1 == cur) {
364  ++progressBar_; // so a spinner moves
365  } else {
366  progressBar_.value(cur);
367  if (Progress::Ptr p = progress())
368  p->update(Progress::Report(cur, NAN));
369  }
370  }
371  if (!errorMessage.empty())
372  throw Exception(errorMessage);
373 #endif
374  }
375 
376 private:
377  template<class T>
378  static void startWorker(SerialOutput *saver, Savable objectTypeId, const T *object, std::string *errorMessage) {
379  ASSERT_not_null(object);
380  saver->asyncSave(objectTypeId, *object, errorMessage);
381  }
382 
383  // This might run in its own thread.
384  template<class T>
385  void asyncSave(Savable objectTypeId, const T &object, std::string *errorMessage) {
386  ASSERT_not_null(errorMessage);
387 #ifndef ROSE_SUPPORTS_SERIAL_IO
388  ASSERT_not_reachable("not supported in this configuration");
389 #else
390 #if !defined(ROSE_DEBUG_SERIAL_IO)
391  try {
392 #endif
393  objectType(ERROR);
394  switch (format()) {
395  case BINARY:
396  ASSERT_not_null(binary_archive_);
397  *binary_archive_ <<BOOST_SERIALIZATION_NVP(objectTypeId);
398  *binary_archive_ <<BOOST_SERIALIZATION_NVP(object);
399  break;
400  case TEXT:
401  ASSERT_not_null(text_archive_);
402  *text_archive_ <<BOOST_SERIALIZATION_NVP(objectTypeId);
403  *text_archive_ <<BOOST_SERIALIZATION_NVP(object);
404  break;
405  case XML:
406  ASSERT_not_null(xml_archive_);
407  *xml_archive_ <<BOOST_SERIALIZATION_NVP(objectTypeId);
408  *xml_archive_ <<BOOST_SERIALIZATION_NVP(object);
409  break;
410  }
411  objectType(objectTypeId);
412 #if !defined(ROSE_DEBUG_SERIAL_IO)
413  } catch (const Exception &e) {
414  *errorMessage = e.what();
415  } catch (...) {
416  *errorMessage = "failed to write object to output stream";
417  }
418 #endif
419 #endif
420  }
421 };
422 
423 
425 // SerialInput
427 
431 class SerialInput: public SerialIo {
432 public:
434 
435 private:
436 #ifdef ROSE_SUPPORTS_SERIAL_IO
437  size_t fileSize_;
438  boost::iostreams::file_descriptor_source device_;
439  boost::iostreams::stream<boost::iostreams::file_descriptor_source> file_;
440  boost::archive::binary_iarchive *binary_archive_;
441  boost::archive::text_iarchive *text_archive_;
442  boost::archive::xml_iarchive *xml_archive_;
443 #endif
444 
445 protected:
446 #ifdef ROSE_SUPPORTS_SERIAL_IO
447  SerialInput(): fileSize_(0), binary_archive_(NULL), text_archive_(NULL), xml_archive_(NULL) {}
448 #else
449  SerialInput() {}
450 #endif
451 
452 public:
453  ~SerialInput();
454  void open(const boost::filesystem::path &fileName) ROSE_OVERRIDE;
455  void close() ROSE_OVERRIDE;
456 
461  static Ptr instance() { return Ptr(new SerialInput); }
462 
471 
479 
487  SgNode* loadAst();
488 
495  template<class T>
496  T loadObject(Savable objectTypeId) {
497  T object;
498  loadObject<T>(objectTypeId, object);
499  return object;
500  }
501  template<class T>
502  void loadObject(Savable objectTypeId, T &object) {
503  if (!isOpen())
504  throw Exception("cannot load object when no file is open");
505 
506 #ifndef ROSE_SUPPORTS_SERIAL_IO
507  throw Exception("binary state files are not supported in this configuration");
508 #else
509  if (ERROR == objectType())
510  throw Exception("cannot read object because stream is in error state");
511  if (objectType() != objectTypeId) {
512  throw Exception("unexpected object type (expected " + boost::lexical_cast<std::string>(objectTypeId) +
513  " but read " + boost::lexical_cast<std::string>(objectType()) + ")");
514  }
515  objectType(ERROR); // in case of exception
516  std::string errorMessage;
517 #ifdef ROSE_DEBUG_SERIAL_IO
518  asyncLoad(object, &errorMessage);
519 #else
520  boost::thread worker(startWorker<T>, this, &object, &errorMessage);
521  boost::chrono::milliseconds timeout((unsigned)(1000 * Sawyer::ProgressBarSettings::minimumUpdateInterval()));
522  progressBar_.prefix("reading");
523  while (!worker.try_join_for(timeout)) {
524  if (fileSize_ > 0) {
525  off_t cur = ::lseek(fd_, 0, SEEK_CUR);
526  if (cur != -1) {
527  progressBar_.value(cur);
528  if (Progress::Ptr p = progress())
529  p->update(Progress::Report(cur, fileSize_));
530  }
531  } else {
532  ++progressBar_; // so the spinner moves
533  }
534  }
535  if (!errorMessage.empty())
536  throw Exception(errorMessage);
537 #endif
538  advanceObjectType();
539 #endif
540  }
543 private:
544  template<class T>
545  static void startWorker(SerialInput *loader, T *object, std::string *errorMessage) {
546  loader->asyncLoad(*object, errorMessage);
547  }
548 
549  // Might run in its own thread
550  template<class T>
551  void asyncLoad(T &object, std::string *errorMessage) {
552  ASSERT_not_null(errorMessage);
553 #ifndef ROSE_SUPPORTS_SERIAL_IO
554  ASSERT_not_reachable("not supported in this configuration");
555 #else
556 #if !defined(ROSE_DEBUG_SERIAL_IO)
557  try {
558 #endif
559  switch (format()) {
560  case BINARY:
561  ASSERT_not_null(binary_archive_);
562  *binary_archive_ >>object;
563  break;
564  case TEXT:
565  ASSERT_not_null(text_archive_);
566  *text_archive_ >>object;
567  break;
568  case XML:
569  ASSERT_not_null(xml_archive_);
570  *xml_archive_ >>BOOST_SERIALIZATION_NVP(object);
571  break;
572  }
573 #if !defined(ROSE_DEBUG_SERIAL_IO)
574  } catch (const Exception &e) {
575  *errorMessage = e.what();
576  } catch (...) {
577  *errorMessage = "failed to read object from input stream";
578  }
579 #endif
580 #endif
581  }
582 
583 protected:
584  // Read the next object type from the input stream
585  void advanceObjectType();
586 };
587 
588 } // namespace
589 } // namespace
590 
591 #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.
static Savable userSavable(unsigned offset)
Create a new Savable enum constant.
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.
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:67
Name space for the entire library.
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:8908
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:159
T loadObject(Savable objectTypeId)
Load an object from the input stream.
Partitioner2::Partitioner loadPartitioner()
Load a partitioner from the input stream.
bool isOpen() const
Whether a file is attached.
const std::string & prefix() const
String to show before the beginning of the bar.
Definition: ProgressBar.h:290
void loadObject(Savable objectTypeId, T &object)
Load an object from the input stream.
virtual ~SerialIo()
Destructor.
Suffix suffix()
Property: suffix.
Definition: ProgressBar.h:190
Base class for reference counted objects.
Definition: SharedObject.h:64
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:317
Base class for all ROSE exceptions.
Definition: RoseException.h:9
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.