ROSE  0.9.11.158
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(__clang__)
23  #define ROSE_SUPPORTS_SERIAL_IO /*supported*/
24 #elif defined(__GNUC__)
25  #if __GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNU_C_PATCHLEVEL__ <= 40204
26  // GCC <= 4.2.4 gets segfaults compiling this file
27  #undef ROSE_SUPPORTS_SERIAL_IO
28  #else
29  #define ROSE_SUPPORTS_SERIAL_IO /*supported*/
30  #endif
31 #else
32  #define ROSE_SUPPORTS_SERIAL_IO /*supported*/
33 #endif
34 
35 #ifdef ROSE_SUPPORTS_SERIAL_IO
36 #include <boost/archive/binary_iarchive.hpp>
37 #include <boost/archive/binary_oarchive.hpp>
38 #include <boost/archive/text_iarchive.hpp>
39 #include <boost/archive/text_oarchive.hpp>
40 #include <boost/archive/xml_iarchive.hpp>
41 #include <boost/archive/xml_oarchive.hpp>
42 #include <boost/iostreams/device/file_descriptor.hpp>
43 #include <boost/iostreams/stream.hpp>
44 #endif
45 
46 namespace Rose {
47 namespace BinaryAnalysis {
48 
49 namespace Partitioner2 {
50 class Partitioner;
51 }
52 
54 // SerialIo
56 
114 public:
117 
119  enum Format {
127  };
128 
130  enum Savable {
131  NO_OBJECT = 0x00000000,
132  PARTITIONER = 0x00000001,
133  AST = 0x00000002,
134  END_OF_DATA = 0x0000fffe,
135  ERROR = 0x0000ffff,
136  USER_DEFINED = 0x00010000,
137  USER_DEFINED_LAST = 0xffffffff
138  };
139 
141  class Exception: public Rose::Exception {
142  public:
144  explicit Exception(const std::string &s): Rose::Exception(s) {}
145  ~Exception() throw() {}
146  };
147 
148 private:
149  mutable SAWYER_THREAD_TRAITS::Mutex mutex_; // protects the following data members
150  Format format_;
151  Progress::Ptr progress_;
152  bool isOpen_;
153  Savable objectType_;
154 
155 protected:
156  Sawyer::ProgressBar<size_t> progressBar_;
157 
158  // We use low-level file descriptors under an std::stream I/O interface in order to provide progress reports. This
159  // allows one thread to be reading from or writing to the file and another thread to monitor the file position to
160  // report progress. The C++ standard library doesn't have an API for wrapping file descriptors in a stream interface,
161  // so we use Boost.
162  int fd_;
163 
164 protected:
165  SerialIo()
166  : format_(BINARY), progress_(Progress::instance()), isOpen_(false), objectType_(NO_OBJECT),
167  progressBar_(mlog[Sawyer::Message::MARCH]), fd_(-1) {
168  init();
169  progressBar_.suffix(" bytes");
170  }
171 
172 public:
174  static Sawyer::Message::Facility mlog;
175 
183  virtual ~SerialIo();
184 
188  static Savable userSavable(unsigned offset);
189 
198  Format format() const;
199  void format(Format);
210  Progress::Ptr progress() const;
211  void progress(const Progress::Ptr&);
222  virtual void open(const boost::filesystem::path&) = 0;
223 
235  virtual void close() = 0;
236 
242  bool isOpen() const;
243 
248  Savable objectType() const;
249 
250 protected:
251  // Set or clear the isOpen flag.
252  void setIsOpen(bool b);
253 
254  // Set object type to ERROR to indicate that a read was unsuccessful
255  void objectType(Savable);
256 
257 private:
258  void init();
259 };
260 
261 
263 // SerialOutput
265 
269 class SerialOutput: public SerialIo {
270 public:
272 
273 private:
274 #ifdef ROSE_SUPPORTS_SERIAL_IO
275  boost::iostreams::file_descriptor_sink device_;
276  boost::iostreams::stream<boost::iostreams::file_descriptor_sink> file_;
277  boost::archive::binary_oarchive *binary_archive_;
278  boost::archive::text_oarchive *text_archive_;
279  boost::archive::xml_oarchive *xml_archive_;
280 #endif
281 
282 protected:
283 #ifdef ROSE_SUPPORTS_SERIAL_IO
284  SerialOutput(): binary_archive_(NULL), text_archive_(NULL), xml_archive_(NULL) {}
285 #else
286  SerialOutput() {}
287 #endif
288 
289 public:
290  ~SerialOutput();
291  void open(const boost::filesystem::path &fileName) ROSE_OVERRIDE;
292  void close() ROSE_OVERRIDE;
293 
298  static Ptr instance() { return Ptr(new SerialOutput); }
299 
310 
322  void saveAst(SgAsmNode*);
323  void saveAst(SgBinaryComposite*);
324 private:
325  // The saveAstHelper is what actually gets called for the functions above. It doesn't descriminate between nodes that
326  // support serialization and those that don't, which is why it's private. Use only the public functions because they'll
327  // give you a nice compiler error if you try to save an Ast node type that isn't supported.
328  void saveAstHelper(SgNode*);
329 public:
330 
345  template<class T>
346  void saveObject(Savable objectTypeId, const T &object) {
347  if (!isOpen())
348  throw Exception("cannot save object when no file is open");
349  if (ERROR == objectType())
350  throw Exception("cannot save object because stream is in error state");
351 
352 #ifndef ROSE_SUPPORTS_SERIAL_IO
353  throw Exception("binary state files are not supported in this configuration");
354 #elif defined(ROSE_DEBUG_SERIAL_IO)
355  std::string errorMessage;
356  asyncSave(objectTypeId, object, &errorMessage);
357 #else
358  // A different thread saves the object while this thread updates the progress
359  std::string errorMessage;
360  boost::thread worker(startWorker<T>, this, objectTypeId, &object, &errorMessage);
361  boost::chrono::milliseconds timeout((unsigned)(1000 * Sawyer::ProgressBarSettings::minimumUpdateInterval()));
362  progressBar_.prefix("writing");
363  while (!worker.try_join_for(timeout)) {
364  off_t cur = ::lseek(fd_, 0, SEEK_CUR);
365  if (-1 == cur) {
366  ++progressBar_; // so a spinner moves
367  } else {
368  progressBar_.value(cur);
369  if (Progress::Ptr p = progress())
370  p->update(Progress::Report(cur, NAN));
371  }
372  }
373  if (!errorMessage.empty())
374  throw Exception(errorMessage);
375 #endif
376  }
377 
378 private:
379  template<class T>
380  static void startWorker(SerialOutput *saver, Savable objectTypeId, const T *object, std::string *errorMessage) {
381  ASSERT_not_null(object);
382  saver->asyncSave(objectTypeId, *object, errorMessage);
383  }
384 
385  // This might run in its own thread.
386  template<class T>
387  void asyncSave(Savable objectTypeId, const T &object, std::string *errorMessage) {
388  ASSERT_not_null(errorMessage);
389 #ifndef ROSE_SUPPORTS_SERIAL_IO
390  ASSERT_not_reachable("not supported in this configuration");
391 #else
392 #if !defined(ROSE_DEBUG_SERIAL_IO)
393  try {
394 #endif
395  objectType(ERROR);
396  switch (format()) {
397  case BINARY:
398  ASSERT_not_null(binary_archive_);
399  *binary_archive_ <<BOOST_SERIALIZATION_NVP(objectTypeId);
400  *binary_archive_ <<BOOST_SERIALIZATION_NVP(object);
401  break;
402  case TEXT:
403  ASSERT_not_null(text_archive_);
404  *text_archive_ <<BOOST_SERIALIZATION_NVP(objectTypeId);
405  *text_archive_ <<BOOST_SERIALIZATION_NVP(object);
406  break;
407  case XML:
408  ASSERT_not_null(xml_archive_);
409  *xml_archive_ <<BOOST_SERIALIZATION_NVP(objectTypeId);
410  *xml_archive_ <<BOOST_SERIALIZATION_NVP(object);
411  break;
412  }
413  objectType(objectTypeId);
414 #if !defined(ROSE_DEBUG_SERIAL_IO)
415  } catch (const Exception &e) {
416  *errorMessage = e.what();
417  } catch (...) {
418  *errorMessage = "failed to write object to output stream";
419  }
420 #endif
421 #endif
422  }
423 };
424 
425 
427 // SerialInput
429 
433 class SerialInput: public SerialIo {
434 public:
436 
437 private:
438 #ifdef ROSE_SUPPORTS_SERIAL_IO
439  size_t fileSize_;
440  boost::iostreams::file_descriptor_source device_;
441  boost::iostreams::stream<boost::iostreams::file_descriptor_source> file_;
442  boost::archive::binary_iarchive *binary_archive_;
443  boost::archive::text_iarchive *text_archive_;
444  boost::archive::xml_iarchive *xml_archive_;
445 #endif
446 
447 protected:
448 #ifdef ROSE_SUPPORTS_SERIAL_IO
449  SerialInput(): fileSize_(0), binary_archive_(NULL), text_archive_(NULL), xml_archive_(NULL) {}
450 #else
451  SerialInput() {}
452 #endif
453 
454 public:
455  ~SerialInput();
456  void open(const boost::filesystem::path &fileName) ROSE_OVERRIDE;
457  void close() ROSE_OVERRIDE;
458 
463  static Ptr instance() { return Ptr(new SerialInput); }
464 
473 
481 
489  SgNode* loadAst();
490 
497  template<class T>
498  T loadObject(Savable objectTypeId) {
499  T object;
500  loadObject<T>(objectTypeId, object);
501  return object;
502  }
503  template<class T>
504  void loadObject(Savable objectTypeId, T &object) {
505  if (!isOpen())
506  throw Exception("cannot load object when no file is open");
507 
508 #ifndef ROSE_SUPPORTS_SERIAL_IO
509  throw Exception("binary state files are not supported in this configuration");
510 #else
511  if (ERROR == objectType())
512  throw Exception("cannot read object because stream is in error state");
513  if (objectType() != objectTypeId) {
514  throw Exception("unexpected object type (expected " + boost::lexical_cast<std::string>(objectTypeId) +
515  " but read " + boost::lexical_cast<std::string>(objectType()) + ")");
516  }
517  objectType(ERROR); // in case of exception
518  std::string errorMessage;
519 #ifdef ROSE_DEBUG_SERIAL_IO
520  asyncLoad(object, &errorMessage);
521 #else
522  boost::thread worker(startWorker<T>, this, &object, &errorMessage);
523  boost::chrono::milliseconds timeout((unsigned)(1000 * Sawyer::ProgressBarSettings::minimumUpdateInterval()));
524  progressBar_.prefix("reading");
525  while (!worker.try_join_for(timeout)) {
526  if (fileSize_ > 0) {
527  off_t cur = ::lseek(fd_, 0, SEEK_CUR);
528  if (cur != -1) {
529  progressBar_.value(cur);
530  if (Progress::Ptr p = progress())
531  p->update(Progress::Report(cur, fileSize_));
532  }
533  } else {
534  ++progressBar_; // so the spinner moves
535  }
536  }
537  if (!errorMessage.empty())
538  throw Exception(errorMessage);
539 #endif
540  advanceObjectType();
541 #endif
542  }
545 private:
546  template<class T>
547  static void startWorker(SerialInput *loader, T *object, std::string *errorMessage) {
548  loader->asyncLoad(*object, errorMessage);
549  }
550 
551  // Might run in its own thread
552  template<class T>
553  void asyncLoad(T &object, std::string *errorMessage) {
554  ASSERT_not_null(errorMessage);
555 #ifndef ROSE_SUPPORTS_SERIAL_IO
556  ASSERT_not_reachable("not supported in this configuration");
557 #else
558 #if !defined(ROSE_DEBUG_SERIAL_IO)
559  try {
560 #endif
561  switch (format()) {
562  case BINARY:
563  ASSERT_not_null(binary_archive_);
564  *binary_archive_ >>object;
565  break;
566  case TEXT:
567  ASSERT_not_null(text_archive_);
568  *text_archive_ >>object;
569  break;
570  case XML:
571  ASSERT_not_null(xml_archive_);
572  *xml_archive_ >>BOOST_SERIALIZATION_NVP(object);
573  break;
574  }
575 #if !defined(ROSE_DEBUG_SERIAL_IO)
576  } catch (const Exception &e) {
577  *errorMessage = e.what();
578  } catch (...) {
579  *errorMessage = "failed to read object from input stream";
580  }
581 #endif
582 #endif
583  }
584 
585 protected:
586  // Read the next object type from the input stream
587  void advanceObjectType();
588 };
589 
590 } // namespace
591 } // namespace
592 
593 #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.
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:8968
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:316
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.