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