ROSE 0.11.145.147
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
43#include <Sawyer/Message.h>
44#include <Sawyer/ProgressBar.h>
45#include <Sawyer/Synchronization.h>
46
47#include <boost/filesystem.hpp>
48#include <boost/lexical_cast.hpp>
49#include <boost/thread.hpp>
50
51namespace Rose {
52namespace BinaryAnalysis {
53
55// SerialIo
57
115public:
118
120 enum Format {
121 BINARY,
123 TEXT,
126 XML
128 };
129
131 enum Savable {
132 NO_OBJECT = 0x00000000,
133 PARTITIONER = 0x00000001,
134 AST = 0x00000002,
135 END_OF_DATA = 0x0000fffe,
136 ERROR = 0x0000ffff,
137 USER_DEFINED = 0x00010000,
138 USER_DEFINED_LAST = 0xffffffff
139 };
140
143 public:
145 explicit Exception(const std::string &s): Rose::Exception(s) {}
146 ~Exception() throw() {}
147 };
148
149private:
150 mutable SAWYER_THREAD_TRAITS::Mutex mutex_; // protects the following data members
151 Format format_;
152 Progress::Ptr progress_;
153 bool isOpen_;
154 Savable objectType_;
155
156protected:
157 Sawyer::ProgressBar<size_t> progressBar_;
158
159 // We use low-level file descriptors under an std::stream I/O interface in order to provide progress reports. This
160 // allows one thread to be reading from or writing to the file and another thread to monitor the file position to
161 // report progress. The C++ standard library doesn't have an API for wrapping file descriptors in a stream interface,
162 // so we use Boost.
163 int fd_;
164
165protected:
166 SerialIo()
167 : format_(BINARY), progress_(Progress::instance()), isOpen_(false), objectType_(NO_OBJECT),
168 progressBar_(mlog[Sawyer::Message::MARCH]), fd_(-1) {
169 init();
170 progressBar_.suffix(" bytes");
171 }
172
173public:
176
184 virtual ~SerialIo();
185
189 static Savable userSavable(unsigned offset);
190
199 Format format() const;
223 virtual void open(const boost::filesystem::path&) = 0;
224
236 virtual void close() = 0;
237
243 bool isOpen() const;
244
250
251protected:
252 // Set or clear the isOpen flag.
253 void setIsOpen(bool b);
254
255 // Set object type to ERROR to indicate that a read was unsuccessful
256 void objectType(Savable);
257
258private:
259 void init();
260};
261
262
264// SerialOutput
266
270class SerialOutput: public SerialIo {
271public:
274
275private:
276#ifdef ROSE_SUPPORTS_SERIAL_IO
277 boost::iostreams::file_descriptor_sink device_;
278 boost::iostreams::stream<boost::iostreams::file_descriptor_sink> file_;
279 boost::archive::binary_oarchive *binary_archive_;
280 boost::archive::text_oarchive *text_archive_;
281 boost::archive::xml_oarchive *xml_archive_;
282#endif
283
284protected:
285#ifdef ROSE_SUPPORTS_SERIAL_IO
286 SerialOutput(): binary_archive_(NULL), text_archive_(NULL), xml_archive_(NULL) {}
287#else
288 SerialOutput() {}
289#endif
290
291public:
292 ~SerialOutput();
293 void open(const boost::filesystem::path &fileName) override;
294 void close() override;
295
300 static Ptr instance() { return Ptr(new SerialOutput); }
301
312
326private:
327 // The saveAstHelper is what actually gets called for the functions above. It doesn't descriminate between nodes that
328 // support serialization and those that don't, which is why it's private. Use only the public functions because they'll
329 // give you a nice compiler error if you try to save an Ast node type that isn't supported.
330 void saveAstHelper(SgNode*);
331public:
332
347 template<class T>
348 void saveObject(Savable objectTypeId, const T &object) {
349 if (!isOpen())
350 throw Exception("cannot save object when no file is open");
351 if (ERROR == objectType())
352 throw Exception("cannot save object because stream is in error state");
353
354#ifndef ROSE_SUPPORTS_SERIAL_IO
355 throw Exception("binary state files are not supported in this configuration");
356#elif defined(ROSE_DEBUG_SERIAL_IO)
357 std::string errorMessage;
358 asyncSave(objectTypeId, object, &errorMessage);
359#else
360 // A different thread saves the object while this thread updates the progress
361 std::string errorMessage;
362 boost::thread worker(startWorker<T>, this, objectTypeId, &object, &errorMessage);
363 boost::chrono::milliseconds timeout((unsigned)(1000 * Sawyer::ProgressBarSettings::minimumUpdateInterval()));
364 progressBar_.prefix("writing");
365 while (!worker.try_join_for(timeout)) {
366 off_t cur = ::lseek(fd_, 0, SEEK_CUR);
367 if (-1 == cur) {
368 ++progressBar_; // so a spinner moves
369 } else {
370 progressBar_.value(cur);
371 if (Progress::Ptr p = progress())
372 p->update(Progress::Report(cur, NAN));
373 }
374 }
375 if (!errorMessage.empty())
376 throw Exception(errorMessage);
377#endif
378 }
379
380private:
381 template<class T>
382 static void startWorker(SerialOutput *saver, Savable objectTypeId, const T *object, std::string *errorMessage) {
383 ASSERT_not_null(object);
384 saver->asyncSave(objectTypeId, *object, errorMessage);
385 }
386
387 // This might run in its own thread.
388 template<class T>
389 void asyncSave(Savable objectTypeId, const T &object, std::string *errorMessage) {
390 ASSERT_not_null(errorMessage);
391#ifndef ROSE_SUPPORTS_SERIAL_IO
392 ASSERT_not_reachable("not supported in this configuration");
393#else
394#if !defined(ROSE_DEBUG_SERIAL_IO)
395 try {
396#endif
397 std::string roseVersion = ROSE_PACKAGE_VERSION;
399 switch (format()) {
400 case BINARY:
401 ASSERT_not_null(binary_archive_);
402 *binary_archive_ <<BOOST_SERIALIZATION_NVP(objectTypeId);
403 *binary_archive_ <<BOOST_SERIALIZATION_NVP(roseVersion);
404 *binary_archive_ <<BOOST_SERIALIZATION_NVP(object);
405 break;
406 case TEXT:
407 ASSERT_not_null(text_archive_);
408 *text_archive_ <<BOOST_SERIALIZATION_NVP(objectTypeId);
409 *text_archive_ <<BOOST_SERIALIZATION_NVP(roseVersion);
410 *text_archive_ <<BOOST_SERIALIZATION_NVP(object);
411 break;
412 case XML:
413 ASSERT_not_null(xml_archive_);
414 *xml_archive_ <<BOOST_SERIALIZATION_NVP(objectTypeId);
415 *xml_archive_ <<BOOST_SERIALIZATION_NVP(roseVersion);
416 *xml_archive_ <<BOOST_SERIALIZATION_NVP(object);
417 break;
418 }
419 objectType(objectTypeId);
420#if !defined(ROSE_DEBUG_SERIAL_IO)
421 } catch (std::exception &e) {
422 *errorMessage = e.what();
423 } catch (...) {
424 *errorMessage = "failed to write object to output stream";
425 }
426#endif
427#endif
428 }
429};
430
431
433// SerialInput
435
439class SerialInput: public SerialIo {
440public:
443
444private:
445#ifdef ROSE_SUPPORTS_SERIAL_IO
446 size_t fileSize_;
447 boost::iostreams::file_descriptor_source device_;
448 boost::iostreams::stream<boost::iostreams::file_descriptor_source> file_;
449 boost::archive::binary_iarchive *binary_archive_;
450 boost::archive::text_iarchive *text_archive_;
451 boost::archive::xml_iarchive *xml_archive_;
452#endif
453
454protected:
455#ifdef ROSE_SUPPORTS_SERIAL_IO
456 SerialInput(): fileSize_(0), binary_archive_(NULL), text_archive_(NULL), xml_archive_(NULL) {}
457#else
458 SerialInput() {}
459#endif
460
461public:
462 ~SerialInput();
463 void open(const boost::filesystem::path &fileName) override;
464 void close() override;
465
470 static Ptr instance() { return Ptr(new SerialInput); }
471
480
488
497
504 template<class T>
505 T loadObject(Savable objectTypeId) {
506 T object;
507 loadObject<T>(objectTypeId, object);
508 return object;
509 }
510 template<class T>
511 void loadObject(Savable objectTypeId, T &object) {
512 if (!isOpen())
513 throw Exception("cannot load object when no file is open");
514
515#ifndef ROSE_SUPPORTS_SERIAL_IO
516 throw Exception("binary state files are not supported in this configuration");
517#else
518 if (ERROR == objectType())
519 throw Exception("cannot read object because stream is in error state");
520 if (objectType() != objectTypeId) {
521 throw Exception("unexpected object type (expected " + boost::lexical_cast<std::string>(objectTypeId) +
522 " but read " + boost::lexical_cast<std::string>(objectType()) + ")");
523 }
524 objectType(ERROR); // in case of exception
525 std::string errorMessage;
526#ifdef ROSE_DEBUG_SERIAL_IO
527 asyncLoad(object, &errorMessage);
528#else
529 boost::thread worker(startWorker<T>, this, &object, &errorMessage);
530 boost::chrono::milliseconds timeout((unsigned)(1000 * Sawyer::ProgressBarSettings::minimumUpdateInterval()));
531 progressBar_.prefix("reading");
532 while (!worker.try_join_for(timeout)) {
533 if (fileSize_ > 0) {
534 off_t cur = ::lseek(fd_, 0, SEEK_CUR);
535 if (cur != -1) {
536 progressBar_.value(cur);
537 if (Progress::Ptr p = progress())
538 p->update(Progress::Report(cur, fileSize_));
539 }
540 } else {
541 ++progressBar_; // so the spinner moves
542 }
543 }
544 if (!errorMessage.empty())
545 throw Exception(errorMessage);
546#endif
547 advanceObjectType();
548#endif
549 }
552private:
553 template<class T>
554 static void startWorker(SerialInput *loader, T *object, std::string *errorMessage) {
555 loader->asyncLoad(*object, errorMessage);
556 }
557
558 // Might run in its own thread
559 template<class T>
560 void asyncLoad(T &object, std::string *errorMessage) {
561 ASSERT_not_null(errorMessage);
562#ifndef ROSE_SUPPORTS_SERIAL_IO
563 ASSERT_not_reachable("not supported in this configuration");
564#else
565#if !defined(ROSE_DEBUG_SERIAL_IO)
566 try {
567#endif
568 std::string roseVersion;
569 switch (format()) {
570 case BINARY:
571 ASSERT_not_null(binary_archive_);
572 *binary_archive_ >>BOOST_SERIALIZATION_NVP(roseVersion);
573 checkCompatibility(roseVersion);
574 *binary_archive_ >>BOOST_SERIALIZATION_NVP(object);
575 break;
576 case TEXT:
577 ASSERT_not_null(text_archive_);
578 *text_archive_ >>BOOST_SERIALIZATION_NVP(roseVersion);
579 checkCompatibility(roseVersion);
580 *text_archive_ >>BOOST_SERIALIZATION_NVP(object);
581 break;
582 case XML:
583 ASSERT_not_null(xml_archive_);
584 *xml_archive_ >>BOOST_SERIALIZATION_NVP(roseVersion);
585 checkCompatibility(roseVersion);
586 *xml_archive_ >>BOOST_SERIALIZATION_NVP(object);
587 break;
588 }
589#if !defined(ROSE_DEBUG_SERIAL_IO)
590 } catch (const std::exception &e) {
591 *errorMessage = e.what();
592 } catch (...) {
593 *errorMessage = "failed to read object from input stream";
594 }
595#endif
596#endif
597 }
598
599 void checkCompatibility(const std::string &fileVersion);
600
601protected:
602 // Read the next object type from the input stream
603 void advanceObjectType();
604};
605
606} // namespace
607} // namespace
608
609#endif
610#endif
Input binary analysis state.
Definition SerialIo.h:439
Partitioner2::PartitionerPtr loadPartitioner()
Load a partitioner from the input stream.
Savable nextObjectType()
Type of next object in the input stream.
SerialInputPtr Ptr
Reference counting pointer.
Definition SerialIo.h:442
void loadObject(Savable objectTypeId, T &object)
Load an object from the input stream.
Definition SerialIo.h:511
T loadObject(Savable objectTypeId)
Load an object from the input stream.
Definition SerialIo.h:505
SgNode * loadAst()
Load an AST from the input stream.
void close() override
Detach a file.
void open(const boost::filesystem::path &fileName) override
Attach a file.
static Ptr instance()
Factory method to create a new instance.
Definition SerialIo.h:470
Errors thrown by this API.
Definition SerialIo.h:142
Exception(const std::string &s)
Construct an exception with an error message.
Definition SerialIo.h:145
Base class for binary state input and output.
Definition SerialIo.h:114
virtual void close()=0
Detach a file.
virtual ~SerialIo()
Destructor.
Format
Format of the state file.
Definition SerialIo.h:120
@ XML
The states are stored as XML, which is a very verbose and slow format.
Definition SerialIo.h:126
@ TEXT
Textual binary state files use a custom format (Boost serialization format) that stores the data as A...
Definition SerialIo.h:123
@ BINARY
Binary state files are smaller and faster than the other formats, but are not portable across archite...
Definition SerialIo.h:121
void progress(const Progress::Ptr &)
Property: Progress reporter.
static Savable userSavable(unsigned offset)
Create a new Savable enum constant.
Format format() const
Property: File format.
Savable
Types of objects that can be saved.
Definition SerialIo.h:131
@ END_OF_DATA
Marks the end of the data stream.
Definition SerialIo.h:135
@ USER_DEFINED
First user-defined object number.
Definition SerialIo.h:137
@ USER_DEFINED_LAST
Last user-defined object number.
Definition SerialIo.h:138
@ ERROR
Marks that the stream has encountered an error condition.
Definition SerialIo.h:136
@ PARTITIONER
Rose::BinaryAnalysis::Partitioner2::Partitioner.
Definition SerialIo.h:133
@ AST
Abstract syntax tree.
Definition SerialIo.h:134
@ NO_OBJECT
Object type for newly-initialized serializers.
Definition SerialIo.h:132
Progress::Ptr progress() const
Property: Progress reporter.
virtual void open(const boost::filesystem::path &)=0
Attach a file.
Savable objectType() const
Type ID for next object.
bool isOpen() const
Whether a file is attached.
static Sawyer::Message::Facility mlog
Message facility.
Definition SerialIo.h:175
void format(Format)
Property: File format.
Output binary analysis state.
Definition SerialIo.h:270
SerialOutputPtr Ptr
Reference counting pointer.
Definition SerialIo.h:273
static Ptr instance()
Factory method to create a new instance.
Definition SerialIo.h:300
void saveObject(Savable objectTypeId, const T &object)
Save an object to the output stream.
Definition SerialIo.h:348
void close() override
Detach a file.
void savePartitioner(const Partitioner2::PartitionerConstPtr &)
Save a binary analysis partitioner.
void open(const boost::filesystem::path &fileName) override
Attach a file.
void saveAst(SgAsmNode *)
Save a binary AST.
Base class for all ROSE exceptions.
A general, thread-safe way to report progress made on some task.
Definition Progress.h:167
Collection of streams.
Definition Message.h:1606
Progress bars.
const std::string & prefix() const
String to show before the beginning of the bar.
Suffix suffix()
Property: suffix.
ValueType value() const
Value for the progress bar.
Base class for reference counted objects.
Reference-counting intrusive smart pointer.
Base class for all binary analysis IR nodes.
This class represents the base class for all IR nodes within Sage III.
Sawyer::SharedPointer< SerialOutput > SerialOutputPtr
Reference counting pointer.
Sawyer::SharedPointer< SerialIo > SerialIoPtr
Reference counting pointer.
Sawyer::SharedPointer< SerialInput > SerialInputPtr
Reference counting pointer.
The ROSE library.
double minimumUpdateInterval()
Minimum time between updates.
Sawyer support library.
A single progress report.
Definition Progress.h:181