+++ /dev/null
-/* Copyright (c) 2007-2018. The SimGrid Team. All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#include <cstddef>
-#include <cstdint>
-
-#include <vector>
-
-#include "xbt/asserts.h"
-#include "xbt/misc.h"
-
-#include "src/mc/AddressSpace.hpp"
-#include "src/mc/ChunkedData.hpp"
-#include "src/mc/PageStore.hpp"
-
-namespace simgrid {
-namespace mc {
-
-/** Take a per-page snapshot of a region
- *
- * @param addr The start of the region (must be at the beginning of a page)
- * @param page_count Number of pages of the region
- * @return Snapshot page numbers of this new snapshot
- */
-ChunkedData::ChunkedData(PageStore& store, AddressSpace& as,
- RemotePtr<void> addr, std::size_t page_count)
-{
- store_ = &store;
- this->pagenos_.resize(page_count);
- std::vector<char> buffer(xbt_pagesize);
-
- for (size_t i = 0; i != page_count; ++i) {
-
- RemotePtr<void> page = remote((void*)
- simgrid::mc::mmu::join(i, addr.address()));
- xbt_assert(simgrid::mc::mmu::split(page.address()).second == 0,
- "Not at the beginning of a page");
-
- /* Adding another copy (and a syscall) will probably slow things a lot.
- TODO, optimize this somehow (at least by grouping the syscalls)
- if needed. Either:
- - reduce the number of syscalls
- - let the application snapshot itself
- - move the segments in shared memory (this will break `fork` however)
- */
-
- as.read_bytes(buffer.data(), xbt_pagesize, page, simgrid::mc::ProcessIndexDisabled);
-
- pagenos_[i] = store_->store_page(buffer.data());
-
- }
-}
-
-}
-}
-/* Copyright (c) 2008-2018. The SimGrid Team.
- * All rights reserved. */
+/* Copyright (c) 2008-2018. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "simgrid/sg_config.hpp"
#include "src/mc/ModelChecker.hpp"
-#include "src/mc/PageStore.hpp"
#include "src/mc/Transition.hpp"
#include "src/mc/checker/Checker.hpp"
#include "src/mc/mc_exit.hpp"
#include "src/mc/mc_record.hpp"
#include "src/mc/remote/RemoteClient.hpp"
#include "src/mc/remote/mc_protocol.h"
+#include "src/mc/sosp/PageStore.hpp"
XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_ModelChecker, mc, "ModelChecker");
#include <sys/types.h>
-#include "src/mc/PageStore.hpp"
#include "src/mc/mc_forward.hpp"
#include "src/mc/remote/mc_protocol.h"
+#include "src/mc/sosp/PageStore.hpp"
namespace simgrid {
namespace mc {
+++ /dev/null
-/* Copyright (c) 2007-2018. The SimGrid Team.
- * All rights reserved. */
-
-/* This program is free software; you can redistribute it and/or modify it
- * under the terms of the license (GNU LGPL) which comes with this package. */
-
-#include <cstdlib>
-
-#include <sys/mman.h>
-#ifdef __FreeBSD__
-# define MAP_POPULATE MAP_PREFAULT_READ
-#endif
-
-#include "mc/mc.h"
-#include "src/mc/mc_config.hpp"
-#include "src/mc/mc_snapshot.hpp"
-
-#include "src/mc/ChunkedData.hpp"
-#include "src/mc/RegionSnapshot.hpp"
-
-XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_RegionSnaphot, mc,
- "Logging specific to region snapshots");
-
-namespace simgrid {
-namespace mc {
-
-static inline
-const char* to_cstr(RegionType region)
-{
- switch (region) {
- case RegionType::Unknown:
- return "unknown";
- case RegionType::Heap:
- return "Heap";
- case RegionType::Data:
- return "Data";
- default:
- return "?";
- }
-}
-
-Buffer::Buffer(std::size_t size, Type type) : size_(size), type_(type)
-{
- switch(type_) {
- case Type::Malloc:
- data_ = ::operator new(size_);
- break;
- case Type::Mmap:
- data_ = ::mmap(nullptr, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_POPULATE, -1, 0);
- if (data_ == MAP_FAILED) {
- data_ = nullptr;
- size_ = 0;
- type_ = Type::Malloc;
- throw std::bad_alloc();
- }
- break;
- default:
- abort();
- }
-}
-
-void Buffer::clear() noexcept
-{
- switch(type_) {
- case Type::Malloc:
- ::operator delete(data_);
- break;
- case Type::Mmap:
- if (munmap(data_, size_) != 0)
- abort();
- break;
- default:
- abort();
- }
- data_ = nullptr;
- size_ = 0;
- type_ = Type::Malloc;
-}
-
-RegionSnapshot dense_region(
- RegionType region_type,
- void *start_addr, void* permanent_addr, size_t size)
-{
- // When KSM support is enables, we allocate memory using mmap:
- // * we don't want to advise bits of the heap as mergable
- // * mmap gives data aligned on page boundaries which is merge friendly
- simgrid::mc::Buffer data;
- if (_sg_mc_ksm)
- data = Buffer::mmap(size);
- else
- data = Buffer::malloc(size);
-
- mc_model_checker->process().read_bytes(data.get(), size,
- remote(permanent_addr),
- simgrid::mc::ProcessIndexDisabled);
-
-#ifdef __linux__
- if (_sg_mc_ksm)
- // Mark the region as mergeable *after* we have written into it.
- // Trying to merge them before is useless/counterproductive.
- madvise(data.get(), size, MADV_MERGEABLE);
-#endif
-
- simgrid::mc::RegionSnapshot region(
- region_type, start_addr, permanent_addr, size);
- region.flat_data(std::move(data));
-
- XBT_DEBUG("New region : type : %s, data : %p (real addr %p), size : %zu",
- to_cstr(region_type), region.flat_data().get(), permanent_addr, size);
- return region;
-}
-
-/** @brief Take a snapshot of a given region
- *
- * @param type
- * @param start_addr Address of the region in the simulated process
- * @param permanent_addr Permanent address of this data (for privatized variables, this is the virtual address of the privatized mapping)
- * @param size Size of the data*
- */
-RegionSnapshot region(
- RegionType type, void *start_addr, void* permanent_addr, size_t size)
-{
- if (_sg_mc_sparse_checkpoint)
- return sparse_region(type, start_addr, permanent_addr, size);
- else
- return dense_region(type, start_addr, permanent_addr, size);
-}
-
-RegionSnapshot sparse_region(RegionType region_type,
- void *start_addr, void* permanent_addr, size_t size)
-{
- simgrid::mc::RemoteClient* process = &mc_model_checker->process();
- assert(process != nullptr);
-
- xbt_assert((((uintptr_t)start_addr) & (xbt_pagesize-1)) == 0,
- "Not at the beginning of a page");
- xbt_assert((((uintptr_t)permanent_addr) & (xbt_pagesize-1)) == 0,
- "Not at the beginning of a page");
- size_t page_count = simgrid::mc::mmu::chunkCount(size);
-
- simgrid::mc::ChunkedData page_data(mc_model_checker->page_store(), *process, RemotePtr<void>(permanent_addr),
- page_count);
-
- simgrid::mc::RegionSnapshot region(
- region_type, start_addr, permanent_addr, size);
- region.page_data(std::move(page_data));
- return region;
-}
-
-}
-}
-/* Copyright (c) 2007-2018. The SimGrid Team.
- * All rights reserved. */
+/* Copyright (c) 2007-2018. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#ifndef SIMGRID_MC_VISITED_STATE_HPP
#define SIMGRID_MC_VISITED_STATE_HPP
-#include <cstddef>
+#include "src/mc/mc_state.hpp"
+#include "src/mc/sosp/mc_snapshot.hpp"
+#include <cstddef>
#include <memory>
-#include "src/mc/mc_snapshot.hpp"
-#include "src/mc/mc_state.hpp"
-
namespace simgrid {
namespace mc {
#include "src/mc/mc_forward.hpp"
#include "src/mc/mc_private.hpp"
#include "src/mc/mc_smx.hpp"
-#include "src/mc/mc_snapshot.hpp"
+#include "src/mc/sosp/mc_snapshot.hpp"
XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_compare, xbt, "Logging specific to mc_compare in mc");
#include "src/mc/mc_request.hpp"
#include "src/mc/mc_safety.hpp"
#include "src/mc/mc_smx.hpp"
-#include "src/mc/mc_snapshot.hpp"
#include "src/mc/mc_unw.hpp"
+#include "src/mc/sosp/mc_snapshot.hpp"
#include <libunwind.h>
#endif
-/* Copyright (c) 2014-2018. The SimGrid Team.
- * All rights reserved. */
+/* Copyright (c) 2014-2018. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "mc/datatypes.h"
#include "src/mc/mc_hash.hpp"
#include "src/mc/mc_private.hpp"
-#include "src/mc/mc_snapshot.hpp"
+#include "src/mc/sosp/mc_snapshot.hpp"
#include <mc/mc.h>
XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_hash, mc, "Logging specific to mc_hash");
#include <memory>
#include "src/mc/mc_record.hpp"
-#include "src/mc/mc_snapshot.hpp"
+#include "src/mc/sosp/mc_snapshot.hpp"
#include "src/kernel/activity/CommImpl.hpp"
#include "src/mc/Transition.hpp"
#include <xbt/mmalloc.h>
#include "src/mc/mc_smx.hpp"
-#include "src/mc/mc_snapshot.hpp"
#include "src/mc/mc_unw.hpp"
+#include "src/mc/sosp/mc_snapshot.hpp"
#include "src/mc/AddressSpace.hpp"
#include "src/mc/ObjectInformation.hpp"
--- /dev/null
+/* Copyright (c) 2007-2018. The SimGrid Team. All rights reserved. */
+
+/* This program is free software; you can redistribute it and/or modify it
+ * under the terms of the license (GNU LGPL) which comes with this package. */
+
+#include <cstddef>
+#include <cstdint>
+
+#include <vector>
+
+#include "xbt/asserts.h"
+#include "xbt/misc.h"
+
+#include "src/mc/AddressSpace.hpp"
+#include "src/mc/sosp/ChunkedData.hpp"
+#include "src/mc/sosp/PageStore.hpp"
+
+namespace simgrid {
+namespace mc {
+
+/** Take a per-page snapshot of a region
+ *
+ * @param addr The start of the region (must be at the beginning of a page)
+ * @param page_count Number of pages of the region
+ * @return Snapshot page numbers of this new snapshot
+ */
+ChunkedData::ChunkedData(PageStore& store, AddressSpace& as, RemotePtr<void> addr, std::size_t page_count)
+{
+ store_ = &store;
+ this->pagenos_.resize(page_count);
+ std::vector<char> buffer(xbt_pagesize);
+
+ for (size_t i = 0; i != page_count; ++i) {
+
+ RemotePtr<void> page = remote((void*)simgrid::mc::mmu::join(i, addr.address()));
+ xbt_assert(simgrid::mc::mmu::split(page.address()).second == 0, "Not at the beginning of a page");
+
+ /* Adding another copy (and a syscall) will probably slow things a lot.
+ TODO, optimize this somehow (at least by grouping the syscalls)
+ if needed. Either:
+ - reduce the number of syscalls
+ - let the application snapshot itself
+ - move the segments in shared memory (this will break `fork` however)
+ */
+
+ as.read_bytes(buffer.data(), xbt_pagesize, page, simgrid::mc::ProcessIndexDisabled);
+
+ pagenos_[i] = store_->store_page(buffer.data());
+ }
+}
+
+} // namespace mc
+} // namespace simgrid
#include <vector>
#include "src/mc/mc_forward.hpp"
-#include "src/mc/PageStore.hpp"
+#include "src/mc/sosp/PageStore.hpp"
namespace simgrid {
namespace mc {
PageStore* store_ = nullptr;
/** Indices of the chunks in the `PageStore` */
std::vector<std::size_t> pagenos_;
-public:
+public:
ChunkedData() = default;
void clear()
{
store_->unref_page(pageno);
pagenos_.clear();
}
- ~ChunkedData()
- {
- clear();
- }
+ ~ChunkedData() { clear(); }
// Copy and move
- ChunkedData(ChunkedData const& that)
- : store_ (that.store_)
- , pagenos_(that.pagenos_)
+ ChunkedData(ChunkedData const& that) : store_(that.store_), pagenos_(that.pagenos_)
{
for (std::size_t const& pageno : pagenos_)
store_->ref_page(pageno);
}
- ChunkedData(ChunkedData&& that)
- : store_(that.store_)
- , pagenos_(std::move(that.pagenos_))
+ ChunkedData(ChunkedData&& that) : store_(that.store_), pagenos_(std::move(that.pagenos_))
{
that.store_ = nullptr;
that.pagenos_.clear();
ChunkedData& operator=(ChunkedData const& that)
{
this->clear();
- store_ = that.store_;
+ store_ = that.store_;
pagenos_ = that.pagenos_;
for (std::size_t const& pageno : pagenos_)
store_->ref_page(pageno);
return *this;
}
- ChunkedData& operator=(ChunkedData && that)
+ ChunkedData& operator=(ChunkedData&& that)
{
this->clear();
- store_ = that.store_;
+ store_ = that.store_;
that.store_ = nullptr;
- pagenos_ = std::move(that.pagenos_);
+ pagenos_ = std::move(that.pagenos_);
that.pagenos_.clear();
return *this;
}
/** How many pages are used */
- std::size_t page_count() const { return pagenos_.size(); }
+ std::size_t page_count() const { return pagenos_.size(); }
/** Get a chunk index */
std::size_t pageno(std::size_t i) const { return pagenos_[i]; }
/** Get a view of the chunk indices */
- const std::size_t* pagenos() const { return pagenos_.data(); }
+ const std::size_t* pagenos() const { return pagenos_.data(); }
/** Get a a pointer to a chunk */
- const void* page(std::size_t i) const
- {
- return store_->get_page(pagenos_[i]);
- }
+ const void* page(std::size_t i) const { return store_->get_page(pagenos_[i]); }
- ChunkedData(PageStore& store, AddressSpace& as,
- RemotePtr<void> addr, std::size_t page_count);
+ ChunkedData(PageStore& store, AddressSpace& as, RemotePtr<void> addr, std::size_t page_count);
};
-}
-}
+} // namespace mc
+} // namespace simgrid
#endif
#include <sys/mman.h>
#ifdef __FreeBSD__
-# define MAP_POPULATE MAP_PREFAULT_READ
+#define MAP_POPULATE MAP_PREFAULT_READ
#endif
#include "xbt/base.h"
#include "src/internal_config.h"
-#include "src/mc/PageStore.hpp"
+#include "src/mc/sosp/PageStore.hpp"
#include "src/mc/mc_mmu.hpp"
*/
static XBT_ALWAYS_INLINE PageStore::hash_type mc_hash_page(const void* data)
{
- const std::uint64_t* values = (const uint64_t*) data;
- std::size_t n = xbt_pagesize / sizeof(uint64_t);
+ const std::uint64_t* values = (const uint64_t*)data;
+ std::size_t n = xbt_pagesize / sizeof(uint64_t);
// This djb2:
std::uint64_t hash = 5381;
PageStore::PageStore(size_t size) : memory_(nullptr), capacity_(size), top_index_(0)
{
// Using mmap in order to be able to expand the region by relocating it somewhere else in the virtual memory space:
- void* memory = ::mmap(nullptr, size << xbt_pagebits, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_POPULATE, -1, 0);
+ void* memory =
+ ::mmap(nullptr, size << xbt_pagebits, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0);
if (memory == MAP_FAILED)
xbt_die("Could not mmap initial snapshot pages.");
this->top_index_ = 0;
- this->memory_ = memory;
+ this->memory_ = memory;
this->page_counts_.resize(size);
}
{
size_t old_bytesize = this->capacity_ << xbt_pagebits;
size_t new_bytesize = size << xbt_pagebits;
- void *new_memory;
+ void* new_memory;
// Expand the memory region by moving it into another
// virtual memory address if necessary:
#else
if (new_bytesize > old_bytesize) {
// Grow: first try to add new space after current map
- new_memory = mmap((char *)this->memory_ + old_bytesize,
- new_bytesize-old_bytesize,
- PROT_READ|PROT_WRITE,
- MAP_PRIVATE|MAP_ANONYMOUS|MAP_POPULATE,
- -1, 0);
+ new_memory = mmap((char*)this->memory_ + old_bytesize, new_bytesize - old_bytesize, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0);
if (new_memory == MAP_FAILED)
xbt_die("Could not mremap snapshot pages.");
// Check if expanding worked
- if (new_memory != (char *)this->memory_ + old_bytesize) {
+ if (new_memory != (char*)this->memory_ + old_bytesize) {
// New memory segment could not be put at the end of this->memory_,
// so cancel this one and try to rellocate everything and copy data
- munmap(new_memory, new_bytesize-old_bytesize);
- new_memory = mmap(nullptr,
- new_bytesize,
- PROT_READ|PROT_WRITE,
- MAP_PRIVATE|MAP_ANONYMOUS|MAP_POPULATE,
- -1, 0);
+ munmap(new_memory, new_bytesize - old_bytesize);
+ new_memory =
+ mmap(nullptr, new_bytesize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0);
if (new_memory == MAP_FAILED)
xbt_die("Could not mremap snapshot pages.");
memcpy(new_memory, this->memory_, old_bytesize);
munmap(this->memory_, old_bytesize);
}
- }
- else {
+ } else {
// We don't have functions to shrink a mapping, so leave memory as
// it is for now
new_memory = this->memory_;
#endif
this->capacity_ = size;
- this->memory_ = new_memory;
+ this->memory_ = new_memory;
this->page_counts_.resize(size, 0);
}
{
this->free_pages_.push_back(pageno);
const void* page = this->get_page(pageno);
- hash_type hash = mc_hash_page(page);
+ hash_type hash = mc_hash_page(page);
this->hash_index_[hash].erase(pageno);
}
// If a page with the same content is already in the page store it's reused and its refcount is incremented.
page_counts_[pageno]++;
return pageno;
-
}
}
// Otherwise, a new page is allocated in the page store and the content of the page is `memcpy()`-ed to this new page.
std::size_t pageno = alloc_page();
- xbt_assert(this->page_counts_[pageno]==0, "Allocated page is already used");
- void* snapshot_page = (void*) this->get_page(pageno);
+ xbt_assert(this->page_counts_[pageno] == 0, "Allocated page is already used");
+ void* snapshot_page = (void*)this->get_page(pageno);
memcpy(snapshot_page, page, xbt_pagesize);
page_set.insert(pageno);
page_counts_[pageno]++;
return pageno;
}
-}
-}
+} // namespace mc
+} // namespace simgrid
#ifdef SIMGRID_TEST
-#include <cstring>
#include <cstdint>
+#include <cstring>
-#include <unistd.h>
#include <sys/mman.h>
+#include <unistd.h>
#include <memory>
static void* getpage()
{
- return mmap(nullptr, getpagesize(), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+ return mmap(nullptr, getpagesize(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
}
XBT_TEST_SUITE("mc_page_store", "Page store");
using simgrid::mc::PageStore;
xbt_test_add("Init");
- std::size_t pagesize = (size_t) getpagesize();
+ std::size_t pagesize = (size_t)getpagesize();
std::unique_ptr<PageStore> store = std::unique_ptr<PageStore>(new simgrid::mc::PageStore(500));
- void* data = getpage();
- xbt_test_assert(store->size()==0, "Bad size");
+ void* data = getpage();
+ xbt_test_assert(store->size() == 0, "Bad size");
xbt_test_add("Store the page once");
new_content(data, pagesize);
size_t pageno1 = store->store_page(data);
- xbt_test_assert(store->get_ref(pageno1)==1, "Bad refcount");
+ xbt_test_assert(store->get_ref(pageno1) == 1, "Bad refcount");
const void* copy = store->get_page(pageno1);
- xbt_test_assert(::memcmp(data, copy, pagesize)==0, "Page data should be the same");
- xbt_test_assert(store->size()==1, "Bad size");
+ xbt_test_assert(::memcmp(data, copy, pagesize) == 0, "Page data should be the same");
+ xbt_test_assert(store->size() == 1, "Bad size");
xbt_test_add("Store the same page again");
size_t pageno2 = store->store_page(data);
- xbt_test_assert(pageno1==pageno2, "Page should be the same");
- xbt_test_assert(store->get_ref(pageno1)==2, "Bad refcount");
- xbt_test_assert(store->size()==1, "Bad size");
+ xbt_test_assert(pageno1 == pageno2, "Page should be the same");
+ xbt_test_assert(store->get_ref(pageno1) == 2, "Bad refcount");
+ xbt_test_assert(store->size() == 1, "Bad size");
xbt_test_add("Store a new page");
new_content(data, pagesize);
size_t pageno3 = store->store_page(data);
xbt_test_assert(pageno1 != pageno3, "New page should be different");
- xbt_test_assert(store->size()==2, "Bad size");
+ xbt_test_assert(store->size() == 2, "Bad size");
xbt_test_add("Unref pages");
store->unref_page(pageno1);
- xbt_assert(store->get_ref(pageno1)==1, "Bad refcount");
- xbt_assert(store->size()==2, "Bad size");
+ xbt_assert(store->get_ref(pageno1) == 1, "Bad refcount");
+ xbt_assert(store->size() == 2, "Bad size");
store->unref_page(pageno2);
- xbt_test_assert(store->size()==1, "Bad size");
+ xbt_test_assert(store->size() == 1, "Bad size");
xbt_test_add("Reallocate page");
new_content(data, pagesize);
size_t pageno4 = store->store_page(data);
xbt_test_assert(pageno1 == pageno4, "Page was not reused");
- xbt_test_assert(store->get_ref(pageno4)==1, "Bad refcount");
- xbt_test_assert(store->size()==2, "Bad size");
+ xbt_test_assert(store->get_ref(pageno4) == 1, "Bad refcount");
+ xbt_test_assert(store->size() == 2, "Bad size");
}
#endif /* SIMGRID_TEST */
* The capacity is expanded by a system call (mremap).
* */
std::size_t capacity();
-
};
XBT_ALWAYS_INLINE void PageStore::unref_page(std::size_t pageno)
XBT_ALWAYS_INLINE const void* PageStore::get_page(std::size_t pageno) const
{
- return (void*) simgrid::mc::mmu::join(pageno, (std::uintptr_t) this->memory_);
+ return (void*)simgrid::mc::mmu::join(pageno, (std::uintptr_t)this->memory_);
}
XBT_ALWAYS_INLINE std::size_t PageStore::get_ref(std::size_t pageno)
return this->capacity_;
}
-}
-}
+} // namespace mc
+} // namespace simgrid
#endif
--- /dev/null
+/* Copyright (c) 2007-2018. The SimGrid Team. All rights reserved. */
+
+/* This program is free software; you can redistribute it and/or modify it
+ * under the terms of the license (GNU LGPL) which comes with this package. */
+
+#include <cstdlib>
+
+#include <sys/mman.h>
+#ifdef __FreeBSD__
+#define MAP_POPULATE MAP_PREFAULT_READ
+#endif
+
+#include "mc/mc.h"
+#include "src/mc/mc_config.hpp"
+#include "src/mc/sosp/mc_snapshot.hpp"
+
+#include "src/mc/sosp/ChunkedData.hpp"
+#include "src/mc/sosp/RegionSnapshot.hpp"
+
+XBT_LOG_NEW_DEFAULT_SUBCATEGORY(mc_RegionSnaphot, mc, "Logging specific to region snapshots");
+
+namespace simgrid {
+namespace mc {
+
+static inline const char* to_cstr(RegionType region)
+{
+ switch (region) {
+ case RegionType::Unknown:
+ return "unknown";
+ case RegionType::Heap:
+ return "Heap";
+ case RegionType::Data:
+ return "Data";
+ default:
+ return "?";
+ }
+}
+
+Buffer::Buffer(std::size_t size, Type type) : size_(size), type_(type)
+{
+ switch (type_) {
+ case Type::Malloc:
+ data_ = ::operator new(size_);
+ break;
+ case Type::Mmap:
+ data_ = ::mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0);
+ if (data_ == MAP_FAILED) {
+ data_ = nullptr;
+ size_ = 0;
+ type_ = Type::Malloc;
+ throw std::bad_alloc();
+ }
+ break;
+ default:
+ abort();
+ }
+}
+
+void Buffer::clear() noexcept
+{
+ switch (type_) {
+ case Type::Malloc:
+ ::operator delete(data_);
+ break;
+ case Type::Mmap:
+ if (munmap(data_, size_) != 0)
+ abort();
+ break;
+ default:
+ abort();
+ }
+ data_ = nullptr;
+ size_ = 0;
+ type_ = Type::Malloc;
+}
+
+RegionSnapshot dense_region(RegionType region_type, void* start_addr, void* permanent_addr, size_t size)
+{
+ // When KSM support is enables, we allocate memory using mmap:
+ // * we don't want to advise bits of the heap as mergable
+ // * mmap gives data aligned on page boundaries which is merge friendly
+ simgrid::mc::Buffer data;
+ if (_sg_mc_ksm)
+ data = Buffer::mmap(size);
+ else
+ data = Buffer::malloc(size);
+
+ mc_model_checker->process().read_bytes(data.get(), size, remote(permanent_addr), simgrid::mc::ProcessIndexDisabled);
+
+#ifdef __linux__
+ if (_sg_mc_ksm)
+ // Mark the region as mergeable *after* we have written into it.
+ // Trying to merge them before is useless/counterproductive.
+ madvise(data.get(), size, MADV_MERGEABLE);
+#endif
+
+ simgrid::mc::RegionSnapshot region(region_type, start_addr, permanent_addr, size);
+ region.flat_data(std::move(data));
+
+ XBT_DEBUG("New region : type : %s, data : %p (real addr %p), size : %zu", to_cstr(region_type),
+ region.flat_data().get(), permanent_addr, size);
+ return region;
+}
+
+/** @brief Take a snapshot of a given region
+ *
+ * @param type
+ * @param start_addr Address of the region in the simulated process
+ * @param permanent_addr Permanent address of this data (for privatized variables, this is the virtual address of the
+ * privatized mapping)
+ * @param size Size of the data*
+ */
+RegionSnapshot region(RegionType type, void* start_addr, void* permanent_addr, size_t size)
+{
+ if (_sg_mc_sparse_checkpoint)
+ return sparse_region(type, start_addr, permanent_addr, size);
+ else
+ return dense_region(type, start_addr, permanent_addr, size);
+}
+
+RegionSnapshot sparse_region(RegionType region_type, void* start_addr, void* permanent_addr, size_t size)
+{
+ simgrid::mc::RemoteClient* process = &mc_model_checker->process();
+ assert(process != nullptr);
+
+ xbt_assert((((uintptr_t)start_addr) & (xbt_pagesize - 1)) == 0, "Not at the beginning of a page");
+ xbt_assert((((uintptr_t)permanent_addr) & (xbt_pagesize - 1)) == 0, "Not at the beginning of a page");
+ size_t page_count = simgrid::mc::mmu::chunkCount(size);
+
+ simgrid::mc::ChunkedData page_data(mc_model_checker->page_store(), *process, RemotePtr<void>(permanent_addr),
+ page_count);
+
+ simgrid::mc::RegionSnapshot region(region_type, start_addr, permanent_addr, size);
+ region.page_data(std::move(page_data));
+ return region;
+}
+
+} // namespace mc
+} // namespace simgrid
#include "xbt/base.h"
#include "src/mc/AddressSpace.hpp"
-#include "src/mc/ChunkedData.hpp"
-#include "src/mc/PageStore.hpp"
#include "src/mc/remote/RemotePtr.hpp"
+#include "src/mc/sosp/ChunkedData.hpp"
+#include "src/mc/sosp/PageStore.hpp"
namespace simgrid {
namespace mc {
-enum class RegionType {
- Unknown = 0,
- Heap = 1,
- Data = 2
-};
+enum class RegionType { Unknown = 0, Heap = 1, Data = 2 };
-enum class StorageType {
- NoData = 0,
- Flat = 1,
- Chunked = 2,
- Privatized = 3
-};
+enum class StorageType { NoData = 0, Flat = 1, Chunked = 2, Privatized = 3 };
class Buffer {
private:
- enum class Type {
- Malloc,
- Mmap
- };
+ enum class Type { Malloc, Mmap };
void* data_ = nullptr;
std::size_t size_;
Type type_ = Type::Malloc;
Buffer(std::size_t size, Type type = Type::Malloc);
- Buffer(void* data, std::size_t size, Type type = Type::Malloc) :
- data_(data), size_(size), type_(type) {}
+ Buffer(void* data, std::size_t size, Type type = Type::Malloc) : data_(data), size_(size), type_(type) {}
+
public:
Buffer() = default;
void clear() noexcept;
~Buffer() noexcept { clear(); }
- static Buffer malloc(std::size_t size)
- {
- return Buffer(size, Type::Malloc);
- }
- static Buffer mmap(std::size_t size)
- {
- return Buffer(size, Type::Mmap);
- }
+ static Buffer malloc(std::size_t size) { return Buffer(size, Type::Malloc); }
+ static Buffer mmap(std::size_t size) { return Buffer(size, Type::Mmap); }
// No copy
Buffer(Buffer const& buffer) = delete;
Buffer& operator=(Buffer const& buffer) = delete;
// Move
- Buffer(Buffer&& that) noexcept
- : data_(that.data_), size_(that.size_), type_(that.type_)
+ Buffer(Buffer&& that) noexcept : data_(that.data_), size_(that.size_), type_(that.type_)
{
that.data_ = nullptr;
that.size_ = 0;
Buffer& operator=(Buffer&& that) noexcept
{
clear();
- data_ = that.data_;
- size_ = that.size_;
- type_ = that.type_;
+ data_ = that.data_;
+ size_ = that.size_;
+ type_ = that.type_;
that.data_ = nullptr;
that.size_ = 0;
that.type_ = Type::Malloc;
return *this;
}
- void* get() { return data_; }
- const void* get() const { return data_; }
+ void* get() { return data_; }
+ const void* get() const { return data_; }
std::size_t size() const { return size_; }
};
class RegionSnapshot {
public:
static const RegionType UnknownRegion = RegionType::Unknown;
- static const RegionType HeapRegion = RegionType::Heap;
- static const RegionType DataRegion = RegionType::Data;
+ static const RegionType HeapRegion = RegionType::Heap;
+ static const RegionType DataRegion = RegionType::Data;
+
private:
RegionType region_type_;
StorageType storage_type_;
simgrid::mc::ObjectInformation* object_info_;
/** @brief Virtual address of the region in the simulated process */
- void *start_addr_;
+ void* start_addr_;
/** @brief Size of the data region in bytes */
std::size_t size_;
* on the region of the global variables.
*
* */
- void *permanent_addr_;
+ void* permanent_addr_;
Buffer flat_data_;
ChunkedData page_numbers_;
std::vector<RegionSnapshot> privatized_regions_;
+
public:
- RegionSnapshot() :
- region_type_(UnknownRegion),
- storage_type_(StorageType::NoData),
- object_info_(nullptr),
- start_addr_(nullptr),
- size_(0),
- permanent_addr_(nullptr)
- {}
- RegionSnapshot(RegionType type, void *start_addr, void* permanent_addr, size_t size) :
- region_type_(type),
- storage_type_(StorageType::NoData),
- object_info_(nullptr),
- start_addr_(start_addr),
- size_(size),
- permanent_addr_(permanent_addr)
- {}
+ RegionSnapshot()
+ : region_type_(UnknownRegion)
+ , storage_type_(StorageType::NoData)
+ , object_info_(nullptr)
+ , start_addr_(nullptr)
+ , size_(0)
+ , permanent_addr_(nullptr)
+ {
+ }
+ RegionSnapshot(RegionType type, void* start_addr, void* permanent_addr, size_t size)
+ : region_type_(type)
+ , storage_type_(StorageType::NoData)
+ , object_info_(nullptr)
+ , start_addr_(start_addr)
+ , size_(size)
+ , permanent_addr_(permanent_addr)
+ {
+ }
~RegionSnapshot() = default;
RegionSnapshot(RegionSnapshot const&) = default;
RegionSnapshot& operator=(RegionSnapshot const&) = default;
}
RegionSnapshot& operator=(RegionSnapshot&& that)
{
- region_type_ = that.region_type_;
- storage_type_ = that.storage_type_;
- object_info_ = that.object_info_;
- start_addr_ = that.start_addr_;
- size_ = that.size_;
- permanent_addr_ = that.permanent_addr_;
- flat_data_ = std::move(that.flat_data_);
- page_numbers_ = std::move(that.page_numbers_);
+ region_type_ = that.region_type_;
+ storage_type_ = that.storage_type_;
+ object_info_ = that.object_info_;
+ start_addr_ = that.start_addr_;
+ size_ = that.size_;
+ permanent_addr_ = that.permanent_addr_;
+ flat_data_ = std::move(that.flat_data_);
+ page_numbers_ = std::move(that.page_numbers_);
privatized_regions_ = std::move(that.privatized_regions_);
that.clear();
return *this;
void clear()
{
- region_type_ = UnknownRegion;
+ region_type_ = UnknownRegion;
storage_type_ = StorageType::NoData;
privatized_regions_.clear();
page_numbers_.clear();
flat_data_.clear();
- object_info_ = nullptr;
- start_addr_ = nullptr;
- size_ = 0;
+ object_info_ = nullptr;
+ start_addr_ = nullptr;
+ size_ = 0;
permanent_addr_ = nullptr;
}
void flat_data(Buffer data)
{
storage_type_ = StorageType::Flat;
- flat_data_ = std::move(data);
+ flat_data_ = std::move(data);
page_numbers_.clear();
privatized_regions_.clear();
}
const Buffer& flat_data() const { return flat_data_; }
- Buffer& flat_data() { return flat_data_; }
+ Buffer& flat_data() { return flat_data_; }
void page_data(ChunkedData page_data)
{
page_numbers_.clear();
privatized_regions_ = std::move(data);
}
- std::vector<RegionSnapshot> const& privatized_data() const
- {
- return privatized_regions_;
- }
- std::vector<RegionSnapshot>& privatized_data()
- {
- return privatized_regions_;
- }
+ std::vector<RegionSnapshot> const& privatized_data() const { return privatized_regions_; }
+ std::vector<RegionSnapshot>& privatized_data() { return privatized_regions_; }
simgrid::mc::ObjectInformation* object_info() const { return object_info_; }
void object_info(simgrid::mc::ObjectInformation* info) { object_info_ = info; }
StorageType storage_type() const { return storage_type_; }
RegionType region_type() const { return region_type_; }
- bool contain(RemotePtr<void> p) const
- {
- return p >= start() && p < end();
- }
+ bool contain(RemotePtr<void> p) const { return p >= start() && p < end(); }
};
-RegionSnapshot privatized_region(
- RegionType region_type, void *start_addr, void* permanent_addr,
- std::size_t size);
-RegionSnapshot dense_region(
- RegionType type, void *start_addr, void* data_addr, std::size_t size);
-simgrid::mc::RegionSnapshot sparse_region(
- RegionType type, void *start_addr, void* data_addr, std::size_t size);
-simgrid::mc::RegionSnapshot region(
- RegionType type, void *start_addr, void* data_addr, std::size_t size);
-
-}
-}
+RegionSnapshot privatized_region(RegionType region_type, void* start_addr, void* permanent_addr, std::size_t size);
+RegionSnapshot dense_region(RegionType type, void* start_addr, void* data_addr, std::size_t size);
+simgrid::mc::RegionSnapshot sparse_region(RegionType type, void* start_addr, void* data_addr, std::size_t size);
+simgrid::mc::RegionSnapshot region(RegionType type, void* start_addr, void* data_addr, std::size_t size);
+
+} // namespace mc
+} // namespace simgrid
typedef simgrid::mc::RegionSnapshot s_mc_mem_region_t;
typedef s_mc_mem_region_t* mc_mem_region_t;
#include "src/simix/smx_private.hpp"
-#include <libunwind.h>
#include <libelf.h>
+#include <libunwind.h>
#include "src/mc/mc_private.hpp"
#include <mc/mc.h>
#include "src/mc/mc_hash.hpp"
#include "src/mc/mc_mmu.hpp"
#include "src/mc/mc_smx.hpp"
-#include "src/mc/mc_snapshot.hpp"
#include "src/mc/mc_unw.hpp"
#include "src/mc/remote/mc_protocol.h"
+#include "src/mc/sosp/mc_snapshot.hpp"
-#include "src/mc/RegionSnapshot.hpp"
-#include "src/mc/ObjectInformation.hpp"
#include "src/mc/Frame.hpp"
+#include "src/mc/ObjectInformation.hpp"
#include "src/mc/Variable.hpp"
+#include "src/mc/sosp/RegionSnapshot.hpp"
using simgrid::mc::remote;
*/
static void restore(mc_mem_region_t region)
{
- switch(region->storage_type()) {
- case simgrid::mc::StorageType::Flat:
- mc_model_checker->process().write_bytes(region->flat_data().get(),
- region->size(), region->permanent_address());
- break;
-
- case simgrid::mc::StorageType::Chunked:
- mc_region_restore_sparse(&mc_model_checker->process(), region);
- break;
-
- case simgrid::mc::StorageType::Privatized:
- for (auto& p : region->privatized_data())
- restore(&p);
- break;
-
- default: // includes StorageType::NoData
- xbt_die("Storage type not supported");
- break;
+ switch (region->storage_type()) {
+ case simgrid::mc::StorageType::Flat:
+ mc_model_checker->process().write_bytes(region->flat_data().get(), region->size(), region->permanent_address());
+ break;
+
+ case simgrid::mc::StorageType::Chunked:
+ mc_region_restore_sparse(&mc_model_checker->process(), region);
+ break;
+
+ case simgrid::mc::StorageType::Privatized:
+ for (auto& p : region->privatized_data())
+ restore(&p);
+ break;
+
+ default: // includes StorageType::NoData
+ xbt_die("Storage type not supported");
+ break;
}
}
#if HAVE_SMPI
-RegionSnapshot privatized_region(
- RegionType region_type, void *start_addr, void* permanent_addr,
- std::size_t size
- )
+RegionSnapshot privatized_region(RegionType region_type, void* start_addr, void* permanent_addr, std::size_t size)
{
size_t process_count = MC_smpi_process_count();
// Read smpi_privatization_regions from MCed:
smpi_privatization_region_t remote_smpi_privatization_regions;
- mc_model_checker->process().read_variable(
- "smpi_privatization_regions",
- &remote_smpi_privatization_regions, sizeof(remote_smpi_privatization_regions));
+ mc_model_checker->process().read_variable("smpi_privatization_regions", &remote_smpi_privatization_regions,
+ sizeof(remote_smpi_privatization_regions));
s_smpi_privatization_region_t privatization_regions[process_count];
- mc_model_checker->process().read_bytes(
- &privatization_regions, sizeof(privatization_regions),
- remote(remote_smpi_privatization_regions));
+ mc_model_checker->process().read_bytes(&privatization_regions, sizeof(privatization_regions),
+ remote(remote_smpi_privatization_regions));
std::vector<simgrid::mc::RegionSnapshot> data;
data.reserve(process_count);
for (size_t i = 0; i < process_count; i++)
- data.push_back(simgrid::mc::region(region_type, start_addr,
- privatization_regions[i].address, size));
+ data.push_back(simgrid::mc::region(region_type, start_addr, privatization_regions[i].address, size));
- simgrid::mc::RegionSnapshot region = simgrid::mc::RegionSnapshot(
- region_type, start_addr, permanent_addr, size);
+ simgrid::mc::RegionSnapshot region = simgrid::mc::RegionSnapshot(region_type, start_addr, permanent_addr, size);
region.privatized_data(std::move(data));
return region;
}
#endif
-static
-void add_region(int index, simgrid::mc::Snapshot* snapshot,
- simgrid::mc::RegionType type,
- simgrid::mc::ObjectInformation* object_info,
- void *start_addr, void* permanent_addr,
- std::size_t size)
+static void add_region(int index, simgrid::mc::Snapshot* snapshot, simgrid::mc::RegionType type,
+ simgrid::mc::ObjectInformation* object_info, void* start_addr, void* permanent_addr,
+ std::size_t size)
{
if (type == simgrid::mc::RegionType::Data)
xbt_assert(object_info, "Missing object info for object.");
simgrid::mc::RegionSnapshot region;
#if HAVE_SMPI
- const bool privatization_aware = object_info
- && mc_model_checker->process().privatized(*object_info);
+ const bool privatization_aware = object_info && mc_model_checker->process().privatized(*object_info);
if (privatization_aware && MC_smpi_process_count())
- region = simgrid::mc::privatized_region(
- type, start_addr, permanent_addr, size);
+ region = simgrid::mc::privatized_region(type, start_addr, permanent_addr, size);
else
#endif
region = simgrid::mc::region(type, start_addr, permanent_addr, size);
region.object_info(object_info);
- snapshot->snapshot_regions[index]
- = std::unique_ptr<simgrid::mc::RegionSnapshot>(
- new simgrid::mc::RegionSnapshot(std::move(region)));
+ snapshot->snapshot_regions[index] =
+ std::unique_ptr<simgrid::mc::RegionSnapshot>(new simgrid::mc::RegionSnapshot(std::move(region)));
}
static void get_memory_regions(simgrid::mc::RemoteClient* process, simgrid::mc::Snapshot* snapshot)
snapshot->snapshot_regions.resize(n + 1);
int i = 0;
for (auto const& object_info : process->object_infos)
- add_region(i++, snapshot, simgrid::mc::RegionType::Data,
- object_info.get(),
- object_info->start_rw, object_info->start_rw,
- object_info->end_rw - object_info->start_rw);
+ add_region(i++, snapshot, simgrid::mc::RegionType::Data, object_info.get(), object_info->start_rw,
+ object_info->start_rw, object_info->end_rw - object_info->start_rw);
xbt_mheap_t heap = process->get_heap();
- void *start_heap = heap->base;
- void *end_heap = heap->breakval;
+ void* start_heap = heap->base;
+ void* end_heap = heap->breakval;
- add_region(n, snapshot, simgrid::mc::RegionType::Heap, nullptr,
- start_heap, start_heap,
- (char *) end_heap - (char *) start_heap);
- snapshot->heap_bytes_used = mmalloc_get_bytes_used_remote(
- heap->heaplimit,
- process->get_malloc_info());
+ add_region(n, snapshot, simgrid::mc::RegionType::Heap, nullptr, start_heap, start_heap,
+ (char*)end_heap - (char*)start_heap);
+ snapshot->heap_bytes_used = mmalloc_get_bytes_used_remote(heap->heaplimit, process->get_malloc_info());
#if HAVE_SMPI
if (mc_model_checker->process().privatized() && MC_smpi_process_count())
// snapshot->privatization_index = smpi_loaded_page
- mc_model_checker->process().read_variable(
- "smpi_loaded_page", &snapshot->privatization_index,
- sizeof(snapshot->privatization_index));
+ mc_model_checker->process().read_variable("smpi_loaded_page", &snapshot->privatization_index,
+ sizeof(snapshot->privatization_index));
else
#endif
snapshot->privatization_index = simgrid::mc::ProcessIndexMissing;
/** \brief Fills the position of the segments (executable, read-only, read/write).
* */
// TODO, use the ELF segment information for more robustness
-void find_object_address(
- std::vector<simgrid::xbt::VmMap> const& maps,
- simgrid::mc::ObjectInformation* result)
+void find_object_address(std::vector<simgrid::xbt::VmMap> const& maps, simgrid::mc::ObjectInformation* result)
{
std::string name = simgrid::xbt::Path(result->file_name).get_base_name();
// This is the non-GNU_RELRO-part of the data segment:
if (reg.prot == PROT_RW) {
xbt_assert(not result->start_rw, "Multiple read-write segments for %s, not supported", maps[i].pathname.c_str());
- result->start_rw = (char*) reg.start_addr;
- result->end_rw = (char*) reg.end_addr;
+ result->start_rw = (char*)reg.start_addr;
+ result->end_rw = (char*)reg.end_addr;
// The next VMA might be end of the data segment:
- if (i + 1 < maps.size()
- && maps[i + 1].pathname.empty()
- && maps[i + 1].prot == PROT_RW
- && maps[i + 1].start_addr == reg.end_addr)
- result->end_rw = (char*) maps[i + 1].end_addr;
+ if (i + 1 < maps.size() && maps[i + 1].pathname.empty() && maps[i + 1].prot == PROT_RW &&
+ maps[i + 1].start_addr == reg.end_addr)
+ result->end_rw = (char*)maps[i + 1].end_addr;
}
// This is the text segment:
else if (reg.prot == PROT_RX) {
xbt_assert(not result->start_exec, "Multiple executable segments for %s, not supported",
maps[i].pathname.c_str());
- result->start_exec = (char*) reg.start_addr;
- result->end_exec = (char*) reg.end_addr;
+ result->start_exec = (char*)reg.start_addr;
+ result->end_exec = (char*)reg.end_addr;
// The next VMA might be end of the data segment:
- if (i + 1 < maps.size()
- && maps[i + 1].pathname.empty()
- && maps[i + 1].prot == PROT_RW
- && maps[i + 1].start_addr == reg.end_addr) {
- result->start_rw = (char*) maps[i + 1].start_addr;
- result->end_rw = (char*) maps[i + 1].end_addr;
+ if (i + 1 < maps.size() && maps[i + 1].pathname.empty() && maps[i + 1].prot == PROT_RW &&
+ maps[i + 1].start_addr == reg.end_addr) {
+ result->start_rw = (char*)maps[i + 1].start_addr;
+ result->end_rw = (char*)maps[i + 1].end_addr;
}
}
// This is the GNU_RELRO-part of the data segment:
else if (reg.prot == PROT_READ) {
xbt_assert(not result->start_ro, "Multiple read only segments for %s, not supported", maps[i].pathname.c_str());
- result->start_ro = (char*) reg.start_addr;
- result->end_ro = (char*) reg.end_addr;
+ result->start_ro = (char*)reg.start_addr;
+ result->end_ro = (char*)reg.end_addr;
}
}
result->start = result->start_rw;
- if ((const void*) result->start_ro < result->start)
+ if ((const void*)result->start_ro < result->start)
result->start = result->start_ro;
- if ((const void*) result->start_exec < result->start)
+ if ((const void*)result->start_exec < result->start)
result->start = result->start_exec;
result->end = result->end_rw;
- if (result->end_ro && (const void*) result->end_ro > result->end)
+ if (result->end_ro && (const void*)result->end_ro > result->end)
result->end = result->end_ro;
- if (result->end_exec && (const void*) result->end_exec > result->end)
+ if (result->end_exec && (const void*)result->end_exec > result->end)
result->end = result->end_exec;
xbt_assert(result->start_exec || result->start_rw || result->start_ro);
* \param ip Instruction pointer
* \return true if the variable is valid
* */
-static bool valid_variable(simgrid::mc::Variable* var,
- simgrid::mc::Frame* scope,
- const void *ip)
+static bool valid_variable(simgrid::mc::Variable* var, simgrid::mc::Frame* scope, const void* ip)
{
// The variable is not yet valid:
- if (scope->range.begin() + var->start_scope > (std::uint64_t) ip)
+ if (scope->range.begin() + var->start_scope > (std::uint64_t)ip)
return false;
else
return true;
int region_type;
// FIXME, get rid of `region_type`
- if ((long) stack_frame->ip > (long) process->libsimgrid_info->start_exec)
+ if ((long)stack_frame->ip > (long)process->libsimgrid_info->start_exec)
region_type = 1;
else
region_type = 2;
s_local_variable_t new_var;
new_var.subprogram = stack_frame->frame;
- new_var.ip = stack_frame->ip;
- new_var.name = current_variable.name;
- new_var.type = current_variable.type;
- new_var.region = region_type;
- new_var.address = nullptr;
+ new_var.ip = stack_frame->ip;
+ new_var.name = current_variable.name;
+ new_var.type = current_variable.type;
+ new_var.region = region_type;
+ new_var.address = nullptr;
if (current_variable.address != nullptr)
new_var.address = current_variable.address;
else if (not current_variable.location_list.empty()) {
- simgrid::dwarf::Location location =
- simgrid::dwarf::resolve(
- current_variable.location_list,
- current_variable.object_info,
- &(stack_frame->unw_cursor),
- (void *) stack_frame->frame_base,
- &mc_model_checker->process(), process_index);
+ simgrid::dwarf::Location location = simgrid::dwarf::resolve(
+ current_variable.location_list, current_variable.object_info, &(stack_frame->unw_cursor),
+ (void*)stack_frame->frame_base, &mc_model_checker->process(), process_index);
if (not location.in_memory())
xbt_die("Cannot handle non-address variable");
// Recursive processing of nested scopes:
for (simgrid::mc::Frame& nested_scope : scope->scopes)
- fill_local_variables_values(
- stack_frame, &nested_scope, process_index, result);
+ fill_local_variables_values(stack_frame, &nested_scope, process_index, result);
}
static std::vector<s_local_variable_t> get_local_variables_values(std::vector<s_mc_stack_frame_t>& stack_frames,
// TODO, check condition check (unw_init_local==0 means end of frame)
- while (1) {
+ while (1) {
- s_mc_stack_frame_t stack_frame;
+ s_mc_stack_frame_t stack_frame;
- stack_frame.unw_cursor = c;
+ stack_frame.unw_cursor = c;
- unw_word_t ip;
- unw_word_t sp;
+ unw_word_t ip;
+ unw_word_t sp;
- unw_get_reg(&c, UNW_REG_IP, &ip);
- unw_get_reg(&c, UNW_REG_SP, &sp);
+ unw_get_reg(&c, UNW_REG_IP, &ip);
+ unw_get_reg(&c, UNW_REG_SP, &sp);
- stack_frame.ip = ip;
- stack_frame.sp = sp;
+ stack_frame.ip = ip;
+ stack_frame.sp = sp;
- // TODO, use real addresses in frame_t instead of fixing it here
+ // TODO, use real addresses in frame_t instead of fixing it here
- simgrid::mc::Frame* frame = process->find_function(remote(ip));
- stack_frame.frame = frame;
+ simgrid::mc::Frame* frame = process->find_function(remote(ip));
+ stack_frame.frame = frame;
- if (frame) {
- stack_frame.frame_name = frame->name;
- stack_frame.frame_base =
- (unw_word_t) frame->frame_base(c);
- } else {
- stack_frame.frame_base = 0;
- stack_frame.frame_name = std::string();
- }
+ if (frame) {
+ stack_frame.frame_name = frame->name;
+ stack_frame.frame_base = (unw_word_t)frame->frame_base(c);
+ } else {
+ stack_frame.frame_base = 0;
+ stack_frame.frame_name = std::string();
+ }
- result.push_back(std::move(stack_frame));
+ result.push_back(std::move(stack_frame));
- /* Stop before context switch with maestro */
- if (frame != nullptr &&
- frame->name == "smx_ctx_sysv_wrapper")
- break;
+ /* Stop before context switch with maestro */
+ if (frame != nullptr && frame->name == "smx_ctx_sysv_wrapper")
+ break;
- int ret = unw_step(&c);
- if (ret == 0)
- xbt_die("Unexpected end of stack.");
- else if (ret < 0)
- xbt_die("Error while unwinding stack");
- }
+ int ret = unw_step(&c);
+ if (ret == 0)
+ xbt_die("Unexpected end of stack.");
+ else if (ret < 0)
+ xbt_die("Error while unwinding stack");
+ }
if (result.empty()) {
XBT_INFO("unw_init_local failed");
// Read the context from remote process:
unw_context_t context;
- mc_model_checker->process().read_bytes(
- &context, sizeof(context), remote(stack.context));
+ mc_model_checker->process().read_bytes(&context, sizeof(context), remote(stack.context));
st.context.initialize(&mc_model_checker->process(), &context);
- st.stack_frames = unwind_stack_frames(&st.context);
+ st.stack_frames = unwind_stack_frames(&st.context);
st.local_variables = get_local_variables_values(st.stack_frames, stack.process_index);
- st.process_index = stack.process_index;
+ st.process_index = stack.process_index;
unw_word_t sp = st.stack_frames[0].sp;
res.push_back(std::move(st));
- size_t stack_size =
- (char*) stack.address + stack.size - (char*) sp;
+ size_t stack_size = (char*)stack.address + stack.size - (char*)sp;
snapshot->stack_sizes.push_back(stack_size);
}
return res;
-
}
static void snapshot_handle_ignore(simgrid::mc::Snapshot* snapshot)
ignored_data.start = (void*)region.addr;
ignored_data.data.resize(region.size);
// TODO, we should do this once per privatization segment:
- snapshot->process()->read_bytes(
- ignored_data.data.data(), region.size, remote(region.addr),
- simgrid::mc::ProcessIndexDisabled);
+ snapshot->process()->read_bytes(ignored_data.data.data(), region.size, remote(region.addr),
+ simgrid::mc::ProcessIndexDisabled);
snapshot->ignored_data.push_back(std::move(ignored_data));
}
// Zero the memory:
for (auto const& region : mc_model_checker->process().ignored_regions())
snapshot->process()->clear_bytes(remote(region.addr), region.size);
-
}
static void snapshot_ignore_restore(simgrid::mc::Snapshot* snapshot)
{
for (auto const& ignored_data : snapshot->ignored_data)
- snapshot->process()->write_bytes(
- ignored_data.data.data(), ignored_data.data.size(),
- remote(ignored_data.start));
+ snapshot->process()->write_bytes(ignored_data.data.data(), ignored_data.data.size(), remote(ignored_data.start));
}
static std::vector<s_fd_infos_t> get_current_fds(pid_t pid)
{
const size_t fd_dir_path_size = 20;
char fd_dir_path[fd_dir_path_size];
- int res = snprintf(fd_dir_path, fd_dir_path_size,
- "/proc/%lli/fd", (long long int) pid);
+ int res = snprintf(fd_dir_path, fd_dir_path_size, "/proc/%lli/fd", (long long int)pid);
xbt_assert(res >= 0);
- if ((size_t) res > fd_dir_path_size)
+ if ((size_t)res > fd_dir_path_size)
xbt_die("Unexpected buffer is too small for fd_dir_path");
DIR* fd_dir = opendir(fd_dir_path);
int fd_value = xbt_str_parse_int(fd_number->d_name, "Found a non-numerical FD: %s. Freaking out!");
- if(fd_value < 3)
+ if (fd_value < 3)
continue;
const size_t source_size = 25;
char source[25];
- int res = snprintf(source, source_size, "/proc/%lli/fd/%s",
- (long long int) pid, fd_number->d_name);
+ int res = snprintf(source, source_size, "/proc/%lli/fd/%s", (long long int)pid, fd_number->d_name);
xbt_assert(res >= 0);
- if ((size_t) res > source_size)
+ if ((size_t)res > source_size)
xbt_die("Unexpected buffer is too small for fd %s", fd_number->d_name);
const size_t link_size = 200;
char link[200];
res = readlink(source, link, link_size);
- if (res<0)
+ if (res < 0)
xbt_die("Could not read link for %s", source);
- if (res==200)
+ if (res == 200)
xbt_die("Buffer to small for link of %s", source);
link[res] = '\0';
#if HAVE_SMPI
- if(smpi_is_privatization_file(link))
+ if (smpi_is_privatization_file(link))
continue;
#endif
// This is (probably) the DIR* we are reading:
// TODO, read all the file entries at once and close the DIR.*
- if(strcmp(fd_dir_path, link) == 0)
+ if (strcmp(fd_dir_path, link) == 0)
continue;
// We don't handle them.
}
// This is probably a shared memory used by lttng-ust:
- if(strncmp("/dev/shm/ust-shm-tmp-", link, std::strlen("/dev/shm/ust-shm-tmp-"))==0)
+ if (strncmp("/dev/shm/ust-shm-tmp-", link, std::strlen("/dev/shm/ust-shm-tmp-")) == 0)
continue;
// Add an entry for this FD in the snapshot:
s_fd_infos_t fd;
- fd.filename = std::string(link);
- fd.number = fd_value;
- fd.flags = fcntl(fd_value, F_GETFL) | fcntl(fd_value, F_GETFD) ;
+ fd.filename = std::string(link);
+ fd.number = fd_value;
+ fd.flags = fcntl(fd_value, F_GETFL) | fcntl(fd_value, F_GETFD);
fd.current_position = lseek(fd_value, 0, SEEK_CUR);
fds.push_back(std::move(fd));
}
- closedir (fd_dir);
+ closedir(fd_dir);
return fds;
}
return snapshot;
}
-static inline
-void restore_snapshot_regions(simgrid::mc::Snapshot* snapshot)
+static inline void restore_snapshot_regions(simgrid::mc::Snapshot* snapshot)
{
for (std::unique_ptr<s_mc_mem_region_t> const& region : snapshot->snapshot_regions) {
// For privatized, variables we decided it was not necessary to take the snapshot:
}
#if HAVE_SMPI
- if(snapshot->privatization_index >= 0) {
+ if (snapshot->privatization_index >= 0) {
// Fix the privatization mmap:
s_mc_message_restore_t message{MC_MESSAGE_RESTORE, snapshot->privatization_index};
mc_model_checker->process().getChannel().send(message);
#endif
}
-static inline
-void restore_snapshot_fds(simgrid::mc::Snapshot* snapshot)
+static inline void restore_snapshot_fds(simgrid::mc::Snapshot* snapshot)
{
xbt_die("FD snapshot not implemented in client/server mode.");
mc_model_checker->process().clear_cache();
}
-}
-}
+} // namespace mc
+} // namespace simgrid
-/* Copyright (c) 2014-2018. The SimGrid Team.
- * All rights reserved. */
+/* Copyright (c) 2014-2018. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
/* MC interface: definitions that non-MC modules must see, but not the user */
-
#include <unistd.h> // pread, pwrite
-#include "src/mc/PageStore.hpp"
#include "src/mc/mc_mmu.hpp"
#include "src/mc/mc_private.hpp"
-#include "src/mc/mc_snapshot.hpp"
+#include "src/mc/sosp/PageStore.hpp"
+#include "src/mc/sosp/mc_snapshot.hpp"
+#include "src/mc/sosp/ChunkedData.hpp"
#include <xbt/mmalloc.h>
-#include "src/mc/ChunkedData.hpp"
using simgrid::mc::remote;
{
for (size_t i = 0; i != pages_copy.page_count(); ++i) {
// Otherwise, copy the page:
- void* target_page = (void*) simgrid::mc::mmu::join(i, (std::uintptr_t) start_addr);
+ void* target_page = (void*)simgrid::mc::mmu::join(i, (std::uintptr_t)start_addr);
const void* source_page = pages_copy.page(i);
process->write_bytes(source_page, xbt_pagesize, remote(target_page));
}
void mc_region_restore_sparse(simgrid::mc::RemoteClient* process, mc_mem_region_t reg)
{
- xbt_assert(((reg->permanent_address().address()) & (xbt_pagesize-1)) == 0,
- "Not at the beginning of a page");
+ xbt_assert(((reg->permanent_address().address()) & (xbt_pagesize - 1)) == 0, "Not at the beginning of a page");
xbt_assert(simgrid::mc::mmu::chunkCount(reg->size()) == reg->page_data().page_count());
- mc_restore_page_snapshot_region(process,
- (void*) reg->permanent_address().address(), reg->page_data());
+ mc_restore_page_snapshot_region(process, (void*)reg->permanent_address().address(), reg->page_data());
}
-/* Copyright (c) 2014-2018. The SimGrid Team.
- * All rights reserved. */
+/* Copyright (c) 2014-2018. The SimGrid Team. All rights reserved. */
/* This program is free software; you can redistribute it and/or modify it
* under the terms of the license (GNU LGPL) which comes with this package. */
#include "src/internal_config.h"
#include "src/smpi/include/private.hpp"
-#include "src/mc/PageStore.hpp"
#include "src/mc/mc_mmu.hpp"
#include "src/mc/mc_private.hpp"
-#include "src/mc/mc_snapshot.hpp"
+#include "src/mc/sosp/PageStore.hpp"
+#include "src/mc/sosp/mc_snapshot.hpp"
/** @brief Find the snapshoted region from a pointer
*
* @param Snapshot region in the snapshot this pointer belongs to
* (or nullptr if it does not belong to any snapshot region)
* */
-mc_mem_region_t mc_get_snapshot_region(
- const void* addr, const simgrid::mc::Snapshot* snapshot, int process_index)
+mc_mem_region_t mc_get_snapshot_region(const void* addr, const simgrid::mc::Snapshot* snapshot, int process_index)
{
size_t n = snapshot->snapshot_regions.size();
for (size_t i = 0; i != n; ++i) {
process_index = snapshot->privatization_index;
if (process_index < 0)
xbt_die("Missing process index");
- if (process_index >= (int) region->privatized_data().size())
+ if (process_index >= (int)region->privatized_data().size())
xbt_die("Invalid process index");
simgrid::mc::RegionSnapshot& priv_region = region->privatized_data()[process_index];
xbt_assert(priv_region.contain(simgrid::mc::remote(addr)));
const void* MC_region_read_fragmented(mc_mem_region_t region, void* target, const void* addr, size_t size)
{
// Last byte of the memory area:
- void* end = (char*) addr + size - 1;
+ void* end = (char*)addr + size - 1;
// TODO, we assume the chunks are aligned to natural chunk boundaries.
// We should remove this assumption.
// Page of the last byte of the memory area:
- size_t page_end = simgrid::mc::mmu::split((std::uintptr_t) end).first;
+ size_t page_end = simgrid::mc::mmu::split((std::uintptr_t)end).first;
void* dest = target;
- if (dest==nullptr)
+ if (dest == nullptr)
xbt_die("Missing destination buffer for fragmented memory access");
// Read each page:
- while (simgrid::mc::mmu::split((std::uintptr_t) addr).first != page_end) {
- void* snapshot_addr = mc_translate_address_region_chunked((uintptr_t) addr, region);
- void* next_page = (void*) simgrid::mc::mmu::join(
- simgrid::mc::mmu::split((std::uintptr_t) addr).first + 1,
- 0);
- size_t readable = (char*) next_page - (char*) addr;
+ while (simgrid::mc::mmu::split((std::uintptr_t)addr).first != page_end) {
+ void* snapshot_addr = mc_translate_address_region_chunked((uintptr_t)addr, region);
+ void* next_page = (void*)simgrid::mc::mmu::join(simgrid::mc::mmu::split((std::uintptr_t)addr).first + 1, 0);
+ size_t readable = (char*)next_page - (char*)addr;
memcpy(dest, snapshot_addr, readable);
- addr = (char*) addr + readable;
- dest = (char*) dest + readable;
+ addr = (char*)addr + readable;
+ dest = (char*)dest + readable;
size -= readable;
}
* @param snapshot2 Region of the address in the second snapshot
* @return same as memcmp
* */
-int MC_snapshot_region_memcmp(
- const void* addr1, mc_mem_region_t region1,
- const void* addr2, mc_mem_region_t region2,
- size_t size)
+int MC_snapshot_region_memcmp(const void* addr1, mc_mem_region_t region1, const void* addr2, mc_mem_region_t region2,
+ size_t size)
{
// Using alloca() for large allocations may trigger stack overflow:
// use malloc if the buffer is too big.
, privatization_index(0)
, hash(0)
{
-
}
-const void* Snapshot::read_bytes(void* buffer, std::size_t size,
- RemotePtr<void> address, int process_index,
- ReadOptions options) const
+const void* Snapshot::read_bytes(void* buffer, std::size_t size, RemotePtr<void> address, int process_index,
+ ReadOptions options) const
{
mc_mem_region_t region = mc_get_snapshot_region((void*)address.address(), this, process_index);
if (region) {
memcpy(buffer, res, size);
return buffer;
}
- }
- else
- return this->process()->read_bytes(
- buffer, size, address, process_index, options);
+ } else
+ return this->process()->read_bytes(buffer, size, address, process_index, options);
}
-}
-}
+} // namespace mc
+} // namespace simgrid
#ifdef SIMGRID_TEST
static inline void init_memory(void* mem, size_t size)
{
- char* dest = (char*) mem;
+ char* dest = (char*)mem;
for (size_t i = 0; i < size; ++i) {
dest[i] = rand() & 255;
}
test_snapshot(1);
}
-static void test_snapshot(bool sparse_checkpoint) {
+static void test_snapshot(bool sparse_checkpoint)
+{
xbt_test_add("Initialization");
_sg_mc_sparse_checkpoint = sparse_checkpoint;
process->init();
mc_model_checker = new ::simgrid::mc::ModelChecker(std::move(process));
- for(int n=1; n!=256; ++n) {
+ for (int n = 1; n != 256; ++n) {
// Store region page(s):
size_t byte_size = n * xbt_pagesize;
- void* source = mmap(nullptr, byte_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
- xbt_assert(source!=MAP_FAILED, "Could not allocate source memory");
+ void* source = mmap(nullptr, byte_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ xbt_assert(source != MAP_FAILED, "Could not allocate source memory");
// Init memory and take snapshots:
init_memory(source, byte_size);
- simgrid::mc::RegionSnapshot region0 = simgrid::mc::sparse_region(
- simgrid::mc::RegionType::Unknown, source, source, byte_size);
- for(int i=0; i<n; i+=2) {
- init_memory((char*) source + i*xbt_pagesize, xbt_pagesize);
+ simgrid::mc::RegionSnapshot region0 =
+ simgrid::mc::sparse_region(simgrid::mc::RegionType::Unknown, source, source, byte_size);
+ for (int i = 0; i < n; i += 2) {
+ init_memory((char*)source + i * xbt_pagesize, xbt_pagesize);
}
- simgrid::mc::RegionSnapshot region = simgrid::mc::sparse_region(
- simgrid::mc::RegionType::Unknown, source, source, byte_size);
+ simgrid::mc::RegionSnapshot region =
+ simgrid::mc::sparse_region(simgrid::mc::RegionType::Unknown, source, source, byte_size);
- void* destination = mmap(nullptr, byte_size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
- xbt_assert(source!=MAP_FAILED, "Could not allocate destination memory");
+ void* destination = mmap(nullptr, byte_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ xbt_assert(source != MAP_FAILED, "Could not allocate destination memory");
xbt_test_add("Reading whole region data for %i page(s)", n);
const void* read = MC_region_read(®ion, destination, source, byte_size);
xbt_test_assert(not memcmp(source, read, byte_size), "Mismatch in MC_region_read()");
xbt_test_add("Reading parts of region data for %i page(s)", n);
- for(int j=0; j!=100; ++j) {
- size_t offset = rand() % byte_size;
- size_t size = rand() % (byte_size - offset);
- const void* read = MC_region_read(®ion, destination, (const char*) source+offset, size);
+ for (int j = 0; j != 100; ++j) {
+ size_t offset = rand() % byte_size;
+ size_t size = rand() % (byte_size - offset);
+ const void* read = MC_region_read(®ion, destination, (const char*)source + offset, size);
xbt_test_assert(not memcmp((char*)source + offset, read, size), "Mismatch in MC_region_read()");
}
xbt_test_add("Compare whole region data for %i page(s)", n);
xbt_test_assert(MC_snapshot_region_memcmp(source, ®ion0, source, ®ion, byte_size),
- "Unexpected match in MC_snapshot_region_memcmp() with previous snapshot");
+ "Unexpected match in MC_snapshot_region_memcmp() with previous snapshot");
xbt_test_add("Compare parts of region data for %i page(s) with itself", n);
- for(int j=0; j!=100; ++j) {
+ for (int j = 0; j != 100; ++j) {
size_t offset = rand() % byte_size;
- size_t size = rand() % (byte_size - offset);
+ size_t size = rand() % (byte_size - offset);
xbt_test_assert(
not MC_snapshot_region_memcmp((char*)source + offset, ®ion, (char*)source + offset, ®ion, size),
"Mismatch in MC_snapshot_region_memcmp()");
}
- if (n==1) {
+ if (n == 1) {
xbt_test_add("Read pointer for %i page(s)", n);
memcpy(source, &mc_model_checker, sizeof(void*));
- simgrid::mc::RegionSnapshot region2 = simgrid::mc::sparse_region(
- simgrid::mc::RegionType::Unknown, source, source, byte_size);
+ simgrid::mc::RegionSnapshot region2 =
+ simgrid::mc::sparse_region(simgrid::mc::RegionType::Unknown, source, source, byte_size);
xbt_test_assert(MC_region_read_pointer(®ion2, source) == mc_model_checker,
- "Mismtach in MC_region_read_pointer()");
+ "Mismtach in MC_region_read_pointer()");
}
munmap(destination, byte_size);
}
#endif /* SIMGRID_TEST */
-
#define SIMGRID_MC_SNAPSHOT_HPP
#include "src/mc/ModelChecker.hpp"
-#include "src/mc/RegionSnapshot.hpp"
#include "src/mc/mc_unw.hpp"
#include "src/mc/remote/RemoteClient.hpp"
+#include "src/mc/sosp/RegionSnapshot.hpp"
// ***** Snapshot region
std::vector<s_mc_snapshot_ignored_data_t> ignored_data;
std::vector<s_fd_infos_t> current_fds;
};
-}
-}
+} // namespace mc
+} // namespace simgrid
static XBT_ALWAYS_INLINE mc_mem_region_t mc_get_region_hinted(void* addr, simgrid::mc::Snapshot* snapshot,
int process_index, mc_mem_region_t region)
XBT_PRIVATE std::shared_ptr<simgrid::mc::Snapshot> take_snapshot(int num_state);
XBT_PRIVATE void restore_snapshot(std::shared_ptr<simgrid::mc::Snapshot> snapshot);
-}
-}
+} // namespace mc
+} // namespace simgrid
XBT_PRIVATE void mc_restore_page_snapshot_region(simgrid::mc::RemoteClient* process, void* start_addr,
simgrid::mc::ChunkedData const& pagenos);
src/mc/remote/mc_protocol.h
src/mc/remote/mc_protocol.cpp
+ src/mc/sosp/PageStore.hpp
+ src/mc/sosp/PageStore.cpp
+ src/mc/sosp/ChunkedData.hpp
+ src/mc/sosp/ChunkedData.cpp
+ src/mc/sosp/RegionSnapshot.cpp
+ src/mc/sosp/RegionSnapshot.hpp
+ src/mc/sosp/mc_checkpoint.cpp
+ src/mc/sosp/mc_snapshot.hpp
+ src/mc/sosp/mc_snapshot.cpp
+ src/mc/sosp/mc_page_snapshot.cpp
+
src/mc/AddressSpace.hpp
src/mc/Frame.hpp
src/mc/Frame.cpp
src/mc/ModelChecker.cpp
src/mc/ObjectInformation.hpp
src/mc/ObjectInformation.cpp
- src/mc/PageStore.hpp
- src/mc/PageStore.cpp
- src/mc/ChunkedData.hpp
- src/mc/ChunkedData.cpp
- src/mc/RegionSnapshot.cpp
- src/mc/RegionSnapshot.hpp
src/mc/Type.hpp
src/mc/Variable.hpp
src/mc/mc_forward.hpp
src/mc/mc_unw.hpp
src/mc/mc_unw.cpp
src/mc/mc_unw_vmread.cpp
- src/mc/mc_checkpoint.cpp
- src/mc/mc_snapshot.hpp
- src/mc/mc_snapshot.cpp
- src/mc/mc_page_snapshot.cpp
src/mc/mc_comm_pattern.cpp
src/mc/mc_comm_pattern.hpp
src/mc/compare.cpp
### Simgrid Lib sources
set(simgrid_sources
- ${MSG_SRC}
${S4U_SRC}
- ${SIMDAG_SRC}
${SIMGRID_SRC}
+ ${MC_SRC_BASE}
${SIMIX_SRC}
${SURF_SRC}
${TRACING_SRC}
${XBT_SRC}
${PLUGINS_SRC}
${BINDINGS_SRC}
- ${MC_SRC_BASE}
+ ${MSG_SRC}
+ ${SIMDAG_SRC}
)
if(${SIMGRID_HAVE_JEDULE})