ROSE  0.11.145.0
SerialIo.h
1 #ifndef ROSE_BinaryAnalysis_SerialIo_H
2 #define ROSE_BinaryAnalysis_SerialIo_H
3 #include <featureTests.h>
4 #ifdef ROSE_ENABLE_BINARY_ANALYSIS
5 
6 // Define this if you need to debug SerialIo -- it causes everything to run in the calling thread and avoid catching exceptions.
7 //#define ROSE_DEBUG_SERIAL_IO
8 
9 #if defined(BOOST_WINDOWS)
10  // Lacks POSIX file system, so we can't monitor the I/O progress
11  #undef ROSE_SUPPORTS_SERIAL_IO
12 #elif !defined(ROSE_HAVE_BOOST_SERIALIZATION_LIB)
13  // Lacks Boost's serialization library, which is how we convert objects to bytes and vice versa
14  #undef ROSE_SUPPORTS_SERIAL_IO
15 #elif defined(__clang__)
16  #define ROSE_SUPPORTS_SERIAL_IO /*supported*/
17 #elif defined(__GNUC__)
18  #if __GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNU_C_PATCHLEVEL__ <= 40204
19  // GCC <= 4.2.4 gets segfaults compiling this file
20  #undef ROSE_SUPPORTS_SERIAL_IO
21  #else
22  #define ROSE_SUPPORTS_SERIAL_IO /*supported*/
23  #endif
24 #else
25  #define ROSE_SUPPORTS_SERIAL_IO /*supported*/
26 #endif
27 
28 // These have to be included early, before the definitions of the ROSE classes that are serialized
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 #include <Rose/Progress.h>
41 #include <Rose/Exception.h>
42 #include <boost/filesystem.hpp>
43 #include <boost/lexical_cast.hpp>
44 #include <boost/thread.hpp>
45 #include <Sawyer/Message.h>
46 #include <Sawyer/ProgressBar.h>
47 #include <Sawyer/Synchronization.h>
48 
49 namespace Rose {
50 namespace BinaryAnalysis {
51 
53 // SerialIo
55 
113 public:
115  using Ptr = SerialIoPtr;
116 
118  enum Format {
126  };
127 
129  enum Savable {
130  NO_OBJECT = 0x00000000,
131  PARTITIONER = 0x00000001,
132  AST = 0x00000002,
133  END_OF_DATA = 0x0000fffe,
134  ERROR = 0x0000ffff,
135  USER_DEFINED = 0x00010000,
136  USER_DEFINED_LAST = 0xffffffff
137  };
138 
140  class Exception: public Rose::Exception {
141  public:
143  explicit Exception(const std::string &s): Rose::Exception(s) {}
144  ~Exception() throw() {}
145  };
146 
147 private:
148  mutable SAWYER_THREAD_TRAITS::Mutex mutex_; // protects the following data members
149  Format format_;
150  Progress::Ptr progress_;
151  bool isOpen_;
152  Savable objectType_;
153 
154 protected:
155  Sawyer::ProgressBar<size_t> progressBar_;
156 
157  // We use low-level file descriptors under an std::stream I/O interface in order to provide progress reports. This
158  // allows one thread to be reading from or writing to the file and another thread to monitor the file position to
159  // report progress. The C++ standard library doesn't have an API for wrapping file descriptors in a stream interface,
160  // so we use Boost.
161  int fd_;
162 
163 protected:
164  SerialIo()
165  : format_(BINARY), progress_(Progress::instance()), isOpen_(false), objectType_(NO_OBJECT),
166  progressBar_(mlog[Sawyer::Message::MARCH]), fd_(-1) {
167  init();
168  progressBar_.suffix(" bytes");
169  }
170 
171 public:
174 
182  virtual ~SerialIo();
183 
187  static Savable userSavable(unsigned offset);
188 
197  Format format() const;
198  void format(Format);
209  Progress::Ptr progress() const;
210  void progress(const Progress::Ptr&);
221  virtual void open(const boost::filesystem::path&) = 0;
222 
234  virtual void close() = 0;
235 
241  bool isOpen() const;
242 
247  Savable objectType() const;
248 
249 protected:
250  // Set or clear the isOpen flag.
251  void setIsOpen(bool b);
252 
253  // Set object type to ERROR to indicate that a read was unsuccessful
254  void objectType(Savable);
255 
256 private:
257  void init();
258 };
259 
260 
262 // SerialOutput
264 
268 class SerialOutput: public SerialIo {
269 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) override;
292  void close() 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  std::string roseVersion = ROSE_PACKAGE_VERSION;
396  objectType(ERROR);
397  switch (format()) {
398  case BINARY:
399  ASSERT_not_null(binary_archive_);
400  *binary_archive_ <<BOOST_SERIALIZATION_NVP(objectTypeId);
401  *binary_archive_ <<BOOST_SERIALIZATION_NVP(roseVersion);
402  *binary_archive_ <<BOOST_SERIALIZATION_NVP(object);
403  break;
404  case TEXT:
405  ASSERT_not_null(text_archive_);
406  *text_archive_ <<BOOST_SERIALIZATION_NVP(objectTypeId);
407  *text_archive_ <<BOOST_SERIALIZATION_NVP(roseVersion);
408  *text_archive_ <<BOOST_SERIALIZATION_NVP(object);
409  break;
410  case XML:
411  ASSERT_not_null(xml_archive_);
412  *xml_archive_ <<BOOST_SERIALIZATION_NVP(objectTypeId);
413  *xml_archive_ <<BOOST_SERIALIZATION_NVP(roseVersion);
414  *xml_archive_ <<BOOST_SERIALIZATION_NVP(object);
415  break;
416  }
417  objectType(objectTypeId);
418 #if !defined(ROSE_DEBUG_SERIAL_IO)
419  } catch (const Exception &e) {
420  *errorMessage = e.what();
421  } catch (...) {
422  *errorMessage = "failed to write object to output stream";
423  }
424 #endif
425 #endif
426  }
427 };
428 
429 
431 // SerialInput
433 
437 class SerialInput: public SerialIo {
438 public:
441 
442 private:
443 #ifdef ROSE_SUPPORTS_SERIAL_IO
444  size_t fileSize_;
445  boost::iostreams::file_descriptor_source device_;
446  boost::iostreams::stream<boost::iostreams::file_descriptor_source> file_;
447  boost::archive::binary_iarchive *binary_archive_;
448  boost::archive::text_iarchive *text_archive_;
449  boost::archive::xml_iarchive *xml_archive_;
450 #endif
451 
452 protected:
453 #ifdef ROSE_SUPPORTS_SERIAL_IO
454  SerialInput(): fileSize_(0), binary_archive_(NULL), text_archive_(NULL), xml_archive_(NULL) {}
455 #else
456  SerialInput() {}
457 #endif
458 
459 public:
460  ~SerialInput();
461  void open(const boost::filesystem::path &fileName) override;
462  void close() override;
463 
468  static Ptr instance() { return Ptr(new SerialInput); }
469 
478 
486 
494  SgNode* loadAst();
495 
502  template<class T>
503  T loadObject(Savable objectTypeId) {
504  T object;
505  loadObject<T>(objectTypeId, object);
506  return object;
507  }
508  template<class T>
509  void loadObject(Savable objectTypeId, T &object) {
510  if (!isOpen())
511  throw Exception("cannot load object when no file is open");
512 
513 #ifndef ROSE_SUPPORTS_SERIAL_IO
514  throw Exception("binary state files are not supported in this configuration");
515 #else
516  if (ERROR == objectType())
517  throw Exception("cannot read object because stream is in error state");
518  if (objectType() != objectTypeId) {
519  throw Exception("unexpected object type (expected " + boost::lexical_cast<std::string>(objectTypeId) +
520  " but read " + boost::lexical_cast<std::string>(objectType()) + ")");
521  }
522  objectType(ERROR); // in case of exception
523  std::string errorMessage;
524 #ifdef ROSE_DEBUG_SERIAL_IO
525  asyncLoad(object, &errorMessage);
526 #else
527  boost::thread worker(startWorker<T>, this, &object, &errorMessage);
528  boost::chrono::milliseconds timeout((unsigned)(1000 * Sawyer::ProgressBarSettings::minimumUpdateInterval()));
529  progressBar_.prefix("reading");
530  while (!worker.try_join_for(timeout)) {
531  if (fileSize_ > 0) {
532  off_t cur = ::lseek(fd_, 0, SEEK_CUR);
533  if (cur != -1) {
534  progressBar_.value(cur);
535  if (Progress::Ptr p = progress())
536  p->update(Progress::Report(cur, fileSize_));
537  }
538  } else {
539  ++progressBar_; // so the spinner moves
540  }
541  }
542  if (!errorMessage.empty())
543  throw Exception(errorMessage);
544 #endif
545  advanceObjectType();
546 #endif
547  }
550 private:
551  template<class T>
552  static void startWorker(SerialInput *loader, T *object, std::string *errorMessage) {
553  loader->asyncLoad(*object, errorMessage);
554  }
555 
556  // Might run in its own thread
557  template<class T>
558  void asyncLoad(T &object, std::string *errorMessage) {
559  ASSERT_not_null(errorMessage);
560 #ifndef ROSE_SUPPORTS_SERIAL_IO
561  ASSERT_not_reachable("not supported in this configuration");
562 #else
563 #if !defined(ROSE_DEBUG_SERIAL_IO)
564  try {
565 #endif
566  std::string roseVersion;
567  switch (format()) {
568  case BINARY:
569  ASSERT_not_null(binary_archive_);
570  *binary_archive_ >>BOOST_SERIALIZATION_NVP(roseVersion);
571  checkCompatibility(roseVersion);
572  *binary_archive_ >>BOOST_SERIALIZATION_NVP(object);
573  break;
574  case TEXT:
575  ASSERT_not_null(text_archive_);
576  *text_archive_ >>BOOST_SERIALIZATION_NVP(roseVersion);
577  checkCompatibility(roseVersion);
578  *text_archive_ >>BOOST_SERIALIZATION_NVP(object);
579  break;
580  case XML:
581  ASSERT_not_null(xml_archive_);
582  *xml_archive_ >>BOOST_SERIALIZATION_NVP(roseVersion);
583  checkCompatibility(roseVersion);
584  *xml_archive_ >>BOOST_SERIALIZATION_NVP(object);
585  break;
586  }
587 #if !defined(ROSE_DEBUG_SERIAL_IO)
588  } catch (const Exception &e) {
589  *errorMessage = e.what();
590  } catch (...) {
591  *errorMessage = "failed to read object from input stream";
592  }
593 #endif
594 #endif
595  }
596 
597  void checkCompatibility(const std::string &fileVersion);
598 
599 protected:
600  // Read the next object type from the input stream
601  void advanceObjectType();
602 };
603 
604 } // namespace
605 } // namespace
606 
607 #endif
608 #endif
Savable
Types of objects that can be saved.
Definition: SerialIo.h:129
A single progress report.
Definition: Progress.h:180
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...
Definition: SerialIo.h:121
static Savable userSavable(unsigned offset)
Create a new Savable enum constant.
Collection of streams.
Definition: Message.h:1606
Sawyer::SharedPointer< SerialOutput > SerialOutputPtr
Reference counting pointer.
First user-defined object number.
Definition: SerialIo.h:135
void saveObject(Savable objectTypeId, const T &object)
Save an object to the output stream.
Definition: SerialIo.h:346
Abstract syntax tree.
Definition: SerialIo.h:132
Format format() const
Property: File format.
Output binary analysis state.
Definition: SerialIo.h:268
Rose::BinaryAnalysis::Partitioner2::Partitioner.
Definition: SerialIo.h:131
void saveAst(SgAsmNode *)
Save a binary AST.
void open(const boost::filesystem::path &fileName) override
Attach a file.
void close() override
Detach a file.
static Sawyer::Message::Facility mlog
Message facility.
Definition: SerialIo.h:173
Main namespace for the ROSE library.
Base class for binary state input and output.
Definition: SerialIo.h:112
void savePartitioner(const Partitioner2::PartitionerConstPtr &)
Save a binary analysis partitioner.
Progress::Ptr progress() const
Property: Progress reporter.
SgNode * loadAst()
Load an AST from the input stream.
Reference-counting intrusive smart pointer.
Definition: SharedPointer.h:68
SerialInputPtr Ptr
Reference counting pointer.
Definition: SerialIo.h:440
Name space for the entire library.
Definition: FeasiblePath.h:767
Object type for newly-initialized serializers.
Definition: SerialIo.h:130
Savable objectType() const
Type ID for next object.
Exception(const std::string &s)
Construct an exception with an error message.
Definition: SerialIo.h:143
virtual void open(const boost::filesystem::path &)=0
Attach a file.
Marks that the stream has encountered an error condition.
Definition: SerialIo.h:134
static Ptr instance()
Factory method to create a new instance.
Definition: SerialIo.h:298
Sawyer::SharedPointer< SerialInput > SerialInputPtr
Reference counting pointer.
void close() override
Detach a file.
The states are stored as XML, which is a very verbose and slow format.
Definition: SerialIo.h:124
This class represents the base class for all IR nodes within Sage III.
Definition: Cxx_Grammar.h:9846
static Ptr instance()
Factory method to create a new instance.
Definition: SerialIo.h:468
Sawyer::SharedPointer< SerialIo > SerialIoPtr
Reference counting pointer.
ValueType value() const
Value for the progress bar.
Definition: ProgressBar.h:159
SerialOutputPtr Ptr
Reference counting pointer.
Definition: SerialIo.h:271
T loadObject(Savable objectTypeId)
Load an object from the input stream.
Definition: SerialIo.h:503
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.
Definition: SerialIo.h:509
virtual ~SerialIo()
Destructor.
Suffix suffix()
Property: suffix.
Definition: ProgressBar.h:190
Base class for reference counted objects.
Definition: SharedObject.h:64
void open(const boost::filesystem::path &fileName) override
Attach a file.
A general, thread-safe way to report progress made on some task.
Definition: Progress.h:166
Marks the end of the data stream.
Definition: SerialIo.h:133
Errors thrown by this API.
Definition: SerialIo.h:140
Format
Format of the state file.
Definition: SerialIo.h:118
Base class for all ROSE exceptions.
Definition: Rose/Exception.h:9
Binary state files are smaller and faster than the other formats, but are not portable across archite...
Definition: SerialIo.h:119
Partitioner2::PartitionerPtr loadPartitioner()
Load a partitioner from the input stream.
Last user-defined object number.
Definition: SerialIo.h:136
virtual void close()=0
Detach a file.
Input binary analysis state.
Definition: SerialIo.h:437
Savable nextObjectType()
Type of next object in the input stream.