ROSE 0.11.145.250
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// These have to be included early, before the definitions of the ROSE classes that are serialized
10#ifdef ROSE_ENABLE_BOOST_SERIALIZATION
11#include <boost/archive/binary_iarchive.hpp>
12#include <boost/archive/binary_oarchive.hpp>
13#include <boost/archive/text_iarchive.hpp>
14#include <boost/archive/text_oarchive.hpp>
15#include <boost/archive/xml_iarchive.hpp>
16#include <boost/archive/xml_oarchive.hpp>
17#include <boost/iostreams/device/file_descriptor.hpp>
18#include <boost/iostreams/stream.hpp>
19#endif
20
21#include <Rose/Progress.h>
22#include <Rose/Exception.h>
23#include <ROSE_UNUSED.h>
24
25#include <Sawyer/Message.h>
26#include <Sawyer/ProgressBar.h>
27#include <Sawyer/Synchronization.h>
28
29#include <boost/filesystem.hpp>
30#include <boost/lexical_cast.hpp>
31#include <boost/thread.hpp>
32
33namespace Rose {
34namespace BinaryAnalysis {
35
37// SerialIo
39
97public:
100
102 enum Format {
103 BINARY,
105 TEXT,
108 XML
110 };
111
113 enum Savable {
114 NO_OBJECT = 0x00000000,
115 PARTITIONER = 0x00000001,
116 AST = 0x00000002,
117 END_OF_DATA = 0x0000fffe,
118 ERROR = 0x0000ffff,
119 USER_DEFINED = 0x00010000,
120 USER_DEFINED_LAST = 0xffffffff
121 };
122
125 public:
127 explicit Exception(const std::string &s): Rose::Exception(s) {}
128 ~Exception() throw() {}
129 };
130
131private:
132 mutable SAWYER_THREAD_TRAITS::Mutex mutex_; // protects the following data members
133 Format format_;
134 Progress::Ptr progress_;
135 bool isOpen_;
136 Savable objectType_;
137
138protected:
139 Sawyer::ProgressBar<size_t> progressBar_;
140
141 // We use low-level file descriptors under an std::stream I/O interface in order to provide progress reports. This
142 // allows one thread to be reading from or writing to the file and another thread to monitor the file position to
143 // report progress. The C++ standard library doesn't have an API for wrapping file descriptors in a stream interface,
144 // so we use Boost.
145 int fd_;
146
147protected:
148 SerialIo()
149 : format_(BINARY), progress_(Progress::instance()), isOpen_(false), objectType_(NO_OBJECT),
150 progressBar_(mlog[Sawyer::Message::MARCH]), fd_(-1) {
151 init();
152 progressBar_.suffix(" bytes");
153 }
154
155public:
158
166 virtual ~SerialIo();
167
171 static Savable userSavable(unsigned offset);
172
181 Format format() const;
205 virtual void open(const boost::filesystem::path&) = 0;
206
218 virtual void close() = 0;
219
225 bool isOpen() const;
226
232
233protected:
234 // Set or clear the isOpen flag.
235 void setIsOpen(bool b);
236
237 // Set object type to ERROR to indicate that a read was unsuccessful
238 void objectType(Savable);
239
240private:
241 void init();
242};
243
244
246// SerialOutput
248
252class SerialOutput: public SerialIo {
253public:
256
257private:
258#ifdef ROSE_ENABLE_BOOST_SERIALIZATION
259 boost::iostreams::file_descriptor_sink device_;
260 boost::iostreams::stream<boost::iostreams::file_descriptor_sink> file_;
261 boost::archive::binary_oarchive *binary_archive_;
262 boost::archive::text_oarchive *text_archive_;
263 boost::archive::xml_oarchive *xml_archive_;
264#endif
265
266protected:
267#ifdef ROSE_ENABLE_BOOST_SERIALIZATION
268 SerialOutput(): binary_archive_(NULL), text_archive_(NULL), xml_archive_(NULL) {}
269#else
270 SerialOutput() {}
271#endif
272
273public:
274 ~SerialOutput();
275 void open(const boost::filesystem::path &fileName) override;
276 void close() override;
277
282 static Ptr instance() { return Ptr(new SerialOutput); }
283
294
308private:
309 // The saveAstHelper is what actually gets called for the functions above. It doesn't descriminate between nodes that
310 // support serialization and those that don't, which is why it's private. Use only the public functions because they'll
311 // give you a nice compiler error if you try to save an Ast node type that isn't supported.
312 void saveAstHelper(SgNode*);
313public:
314
329 template<class T>
330 void saveObject(Savable objectTypeId, const T &object) {
331 if (!isOpen())
332 throw Exception("cannot save object when no file is open");
333 if (ERROR == objectType())
334 throw Exception("cannot save object because stream is in error state");
335
336#ifndef ROSE_ENABLE_BOOST_SERIALIZATION
337 ROSE_UNUSED(objectTypeId);
338 ROSE_UNUSED(object);
339 throw Exception("binary state files are not supported in this configuration");
340#elif defined(ROSE_DEBUG_SERIAL_IO)
341 std::string errorMessage;
342 asyncSave(objectTypeId, object, &errorMessage);
343#else
344 // A different thread saves the object while this thread updates the progress
345 std::string errorMessage;
346 boost::thread worker(startWorker<T>, this, objectTypeId, &object, &errorMessage);
347 boost::chrono::milliseconds timeout((unsigned)(1000 * Sawyer::ProgressBarSettings::minimumUpdateInterval()));
348 progressBar_.prefix("writing");
349 while (!worker.try_join_for(timeout)) {
350 off_t cur = ::lseek(fd_, 0, SEEK_CUR);
351 if (-1 == cur) {
352 ++progressBar_; // so a spinner moves
353 } else {
354 progressBar_.value(cur);
355 if (Progress::Ptr p = progress())
356 p->update(Progress::Report(cur, NAN));
357 }
358 }
359 if (!errorMessage.empty())
360 throw Exception(errorMessage);
361#endif
362 }
363
364private:
365 template<class T>
366 static void startWorker(SerialOutput *saver, Savable objectTypeId, const T *object, std::string *errorMessage) {
367 ASSERT_not_null(object);
368 saver->asyncSave(objectTypeId, *object, errorMessage);
369 }
370
371 // This might run in its own thread.
372 template<class T>
373 void asyncSave(Savable objectTypeId, const T &object, std::string *errorMessage) {
374 ASSERT_not_null(errorMessage);
375#ifndef ROSE_ENABLE_BOOST_SERIALIZATION
376 ROSE_UNUSED(objectTypeId);
377 ROSE_UNUSED(object);
378 ASSERT_not_reachable("not supported in this configuration");
379#else
380#if !defined(ROSE_DEBUG_SERIAL_IO)
381 try {
382#endif
383 std::string roseVersion = ROSE_PACKAGE_VERSION;
385 switch (format()) {
386 case BINARY:
387 ASSERT_not_null(binary_archive_);
388 *binary_archive_ <<BOOST_SERIALIZATION_NVP(objectTypeId);
389 *binary_archive_ <<BOOST_SERIALIZATION_NVP(roseVersion);
390 *binary_archive_ <<BOOST_SERIALIZATION_NVP(object);
391 break;
392 case TEXT:
393 ASSERT_not_null(text_archive_);
394 *text_archive_ <<BOOST_SERIALIZATION_NVP(objectTypeId);
395 *text_archive_ <<BOOST_SERIALIZATION_NVP(roseVersion);
396 *text_archive_ <<BOOST_SERIALIZATION_NVP(object);
397 break;
398 case XML:
399 ASSERT_not_null(xml_archive_);
400 *xml_archive_ <<BOOST_SERIALIZATION_NVP(objectTypeId);
401 *xml_archive_ <<BOOST_SERIALIZATION_NVP(roseVersion);
402 *xml_archive_ <<BOOST_SERIALIZATION_NVP(object);
403 break;
404 }
405 objectType(objectTypeId);
406#if !defined(ROSE_DEBUG_SERIAL_IO)
407 } catch (std::exception &e) {
408 *errorMessage = e.what();
409 } catch (...) {
410 *errorMessage = "failed to write object to output stream";
411 }
412#endif
413#endif
414 }
415};
416
417
419// SerialInput
421
425class SerialInput: public SerialIo {
426public:
429
430private:
431#ifdef ROSE_ENABLE_BOOST_SERIALIZATION
432 size_t fileSize_;
433 boost::iostreams::file_descriptor_source device_;
434 boost::iostreams::stream<boost::iostreams::file_descriptor_source> file_;
435 boost::archive::binary_iarchive *binary_archive_;
436 boost::archive::text_iarchive *text_archive_;
437 boost::archive::xml_iarchive *xml_archive_;
438#endif
439
440protected:
441#ifdef ROSE_ENABLE_BOOST_SERIALIZATION
442 SerialInput(): fileSize_(0), binary_archive_(NULL), text_archive_(NULL), xml_archive_(NULL) {}
443#else
444 SerialInput() {}
445#endif
446
447public:
448 ~SerialInput();
449 void open(const boost::filesystem::path &fileName) override;
450 void close() override;
451
456 static Ptr instance() { return Ptr(new SerialInput); }
457
466
474
483
490 template<class T>
491 T loadObject(Savable objectTypeId) {
492 T object;
493 loadObject<T>(objectTypeId, object);
494 return object;
495 }
496 template<class T>
497 void loadObject(Savable objectTypeId, T &object) {
498 if (!isOpen())
499 throw Exception("cannot load object when no file is open");
500
501#ifndef ROSE_ENABLE_BOOST_SERIALIZATION
502 ROSE_UNUSED(objectTypeId);
503 ROSE_UNUSED(object);
504 throw Exception("binary state files are not supported in this configuration");
505#else
506 if (ERROR == objectType())
507 throw Exception("cannot read object because stream is in error state");
508 if (objectType() != objectTypeId) {
509 throw Exception("unexpected object type (expected " + boost::lexical_cast<std::string>(objectTypeId) +
510 " but read " + boost::lexical_cast<std::string>(objectType()) + ")");
511 }
512 objectType(ERROR); // in case of exception
513 std::string errorMessage;
514#ifdef ROSE_DEBUG_SERIAL_IO
515 asyncLoad(object, &errorMessage);
516#else
517 boost::thread worker(startWorker<T>, this, &object, &errorMessage);
518 boost::chrono::milliseconds timeout((unsigned)(1000 * Sawyer::ProgressBarSettings::minimumUpdateInterval()));
519 progressBar_.prefix("reading");
520 while (!worker.try_join_for(timeout)) {
521 if (fileSize_ > 0) {
522 off_t cur = ::lseek(fd_, 0, SEEK_CUR);
523 if (cur != -1) {
524 progressBar_.value(cur);
525 if (Progress::Ptr p = progress())
526 p->update(Progress::Report(cur, fileSize_));
527 }
528 } else {
529 ++progressBar_; // so the spinner moves
530 }
531 }
532 if (!errorMessage.empty())
533 throw Exception(errorMessage);
534#endif
535 advanceObjectType();
536#endif
537 }
540private:
541 template<class T>
542 static void startWorker(SerialInput *loader, T *object, std::string *errorMessage) {
543 loader->asyncLoad(*object, errorMessage);
544 }
545
546 // Might run in its own thread
547 template<class T>
548 void asyncLoad(T &object, std::string *errorMessage) {
549 ASSERT_not_null(errorMessage);
550#ifndef ROSE_ENABLE_BOOST_SERIALIZATION
551 ROSE_UNUSED(object);
552 ASSERT_not_reachable("not supported in this configuration");
553#else
554#if !defined(ROSE_DEBUG_SERIAL_IO)
555 try {
556#endif
557 std::string roseVersion;
558 switch (format()) {
559 case BINARY:
560 ASSERT_not_null(binary_archive_);
561 *binary_archive_ >>BOOST_SERIALIZATION_NVP(roseVersion);
562 checkCompatibility(roseVersion);
563 *binary_archive_ >>BOOST_SERIALIZATION_NVP(object);
564 break;
565 case TEXT:
566 ASSERT_not_null(text_archive_);
567 *text_archive_ >>BOOST_SERIALIZATION_NVP(roseVersion);
568 checkCompatibility(roseVersion);
569 *text_archive_ >>BOOST_SERIALIZATION_NVP(object);
570 break;
571 case XML:
572 ASSERT_not_null(xml_archive_);
573 *xml_archive_ >>BOOST_SERIALIZATION_NVP(roseVersion);
574 checkCompatibility(roseVersion);
575 *xml_archive_ >>BOOST_SERIALIZATION_NVP(object);
576 break;
577 }
578#if !defined(ROSE_DEBUG_SERIAL_IO)
579 } catch (const std::exception &e) {
580 *errorMessage = e.what();
581 } catch (...) {
582 *errorMessage = "failed to read object from input stream";
583 }
584#endif
585#endif
586 }
587
588 void checkCompatibility(const std::string &fileVersion);
589
590protected:
591 // Read the next object type from the input stream
592 void advanceObjectType();
593};
594
595} // namespace
596} // namespace
597
598#endif
599#endif
Input binary analysis state.
Definition SerialIo.h:425
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:428
void loadObject(Savable objectTypeId, T &object)
Load an object from the input stream.
Definition SerialIo.h:497
T loadObject(Savable objectTypeId)
Load an object from the input stream.
Definition SerialIo.h:491
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:456
Errors thrown by this API.
Definition SerialIo.h:124
Exception(const std::string &s)
Construct an exception with an error message.
Definition SerialIo.h:127
Base class for binary state input and output.
Definition SerialIo.h:96
virtual void close()=0
Detach a file.
virtual ~SerialIo()
Destructor.
Format
Format of the state file.
Definition SerialIo.h:102
@ XML
The states are stored as XML, which is a very verbose and slow format.
Definition SerialIo.h:108
@ TEXT
Textual binary state files use a custom format (Boost serialization format) that stores the data as A...
Definition SerialIo.h:105
@ BINARY
Binary state files are smaller and faster than the other formats, but are not portable across archite...
Definition SerialIo.h:103
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:113
@ END_OF_DATA
Marks the end of the data stream.
Definition SerialIo.h:117
@ USER_DEFINED
First user-defined object number.
Definition SerialIo.h:119
@ USER_DEFINED_LAST
Last user-defined object number.
Definition SerialIo.h:120
@ ERROR
Marks that the stream has encountered an error condition.
Definition SerialIo.h:118
@ PARTITIONER
Rose::BinaryAnalysis::Partitioner2::Partitioner.
Definition SerialIo.h:115
@ AST
Abstract syntax tree.
Definition SerialIo.h:116
@ NO_OBJECT
Object type for newly-initialized serializers.
Definition SerialIo.h:114
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:157
void format(Format)
Property: File format.
Output binary analysis state.
Definition SerialIo.h:252
SerialOutputPtr Ptr
Reference counting pointer.
Definition SerialIo.h:255
static Ptr instance()
Factory method to create a new instance.
Definition SerialIo.h:282
void saveObject(Savable objectTypeId, const T &object)
Save an object to the output stream.
Definition SerialIo.h:330
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