mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
7f7f9571a8
Add infrastructure to pypeline that allows containers to be notified when they are being used last, this allows two things: * `Pipe`s eagerly clearing those containers once they are done reading their contents * `ScheduledTask`s clearing those out at the end of their execution in case the pipe did not do it This overall should improve memory usage as container no longer take up memory if they are no longer used as part of a `Schedule`.
145 lines
4.3 KiB
C++
145 lines
4.3 KiB
C++
#pragma once
|
|
|
|
//
|
|
// This file is distributed under the MIT License. See LICENSE.md for details.
|
|
//
|
|
|
|
#include "llvm/Support/SHA256.h"
|
|
|
|
#include "revng/PipeboxCommon/Common.h"
|
|
#include "revng/PipeboxCommon/Concepts.h"
|
|
#include "revng/Support/Tar.h"
|
|
#include "revng/Support/TemporaryFile.h"
|
|
|
|
namespace revng::pypeline {
|
|
|
|
/// Container that stores binaries for later consumption of Pipes. The files
|
|
/// are stored in memory.
|
|
/// When (de)serializing the format is: a plain tar
|
|
/// * A plain tar file (without compression)
|
|
/// * Each file is named "binaries/" + <SHA256 hash of the file, in lowercase>
|
|
/// e.g.
|
|
/// `binaries/e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`
|
|
/// * Each file hash 644 (rw-r--r--) permissions
|
|
class BinariesContainer {
|
|
public:
|
|
static constexpr llvm::StringRef Name = "BinariesContainer";
|
|
static constexpr Kind Kind = Kinds::Binary;
|
|
static constexpr llvm::StringRef MimeType = "application/x-tar";
|
|
|
|
private:
|
|
static constexpr llvm::StringRef Prefix = "binaries-container-file";
|
|
struct File {
|
|
std::string Filename;
|
|
TemporaryFile Contents;
|
|
std::unique_ptr<llvm::MemoryBuffer> Buffer;
|
|
};
|
|
|
|
private:
|
|
bool Disposable = false;
|
|
std::vector<File> Files;
|
|
|
|
public:
|
|
BinariesContainer() = default;
|
|
BinariesContainer(const BinariesContainer &) = delete;
|
|
BinariesContainer &operator=(const BinariesContainer &) = delete;
|
|
BinariesContainer(BinariesContainer &&) = default;
|
|
BinariesContainer &operator=(BinariesContainer &&) = default;
|
|
|
|
public:
|
|
std::set<ObjectID> objects() const {
|
|
if (Files.empty())
|
|
return std::set<ObjectID>{};
|
|
else
|
|
return std::set{ ObjectID() };
|
|
}
|
|
|
|
void
|
|
deserialize(const std::map<const ObjectID *, llvm::ArrayRef<char>> Data) {
|
|
if (Data.size() == 0)
|
|
return;
|
|
|
|
revng_assert(Data.size() == 1);
|
|
auto [Key, Value] = *Data.begin();
|
|
revng_assert(Key->kind() == Kind);
|
|
|
|
Files.clear();
|
|
TarReader Reader(Value, TarFormat::Plain);
|
|
for (TarReader::Entry Entry : Reader.entries()) {
|
|
std::string Hash = hash(Entry.Data);
|
|
revng_assert(Entry.Filename.starts_with("binaries/"));
|
|
revng_assert(Entry.Filename == "binaries/" + Hash);
|
|
|
|
auto TempFile = llvm::cantFail(TemporaryFile::make(Prefix));
|
|
writeToFile({ Entry.Data.data(), Entry.Data.size() }, TempFile.path());
|
|
// Set the file as read-only so that it cannot be (easily) changed
|
|
llvm::sys::fs::setPermissions(TempFile.path(),
|
|
llvm::sys::fs::perms::owner_read);
|
|
|
|
auto Buffer = revng::cantFail(llvm::MemoryBuffer::getFile(TempFile
|
|
.path()));
|
|
Files.push_back({ std::move(Hash),
|
|
std::move(TempFile),
|
|
std::move(Buffer) });
|
|
}
|
|
}
|
|
|
|
std::map<ObjectID, Buffer>
|
|
serialize(const std::vector<const ObjectID *> Objects) const {
|
|
if (Objects.size() == 0)
|
|
return {};
|
|
|
|
revng_assert(Objects.size() == 1 and Objects[0]->kind() == Kind);
|
|
|
|
Buffer Output;
|
|
{
|
|
llvm::raw_svector_ostream OS(Output.data());
|
|
TarWriter Writer(OS, TarFormat::Plain);
|
|
for (const File &Entry : Files) {
|
|
std::string Filename = "binaries/" + Entry.Filename;
|
|
Writer.addMember(Filename,
|
|
{ Entry.Buffer->getBufferStart(),
|
|
Entry.Buffer->getBufferSize() });
|
|
}
|
|
}
|
|
|
|
return { { ObjectID(), Output } };
|
|
}
|
|
|
|
bool verify() const { return true; }
|
|
|
|
void setIsDisposable() { Disposable = true; }
|
|
|
|
void disposeIfPossible() {
|
|
if (not Disposable)
|
|
return;
|
|
Files.clear();
|
|
Disposable = false;
|
|
}
|
|
|
|
public:
|
|
size_t size() const { return Files.size(); }
|
|
|
|
llvm::ArrayRef<char> getFile(size_t Index) const {
|
|
revng_assert(Index < Files.size());
|
|
const llvm::MemoryBuffer &Buffer = *Files[Index].Buffer;
|
|
return { Buffer.getBufferStart(), Buffer.getBufferSize() };
|
|
}
|
|
|
|
llvm::StringRef getFilePath(size_t Index) const {
|
|
revng_assert(Index < Files.size());
|
|
return Files[Index].Contents.path();
|
|
}
|
|
|
|
private:
|
|
static std::string hash(llvm::ArrayRef<char> Data) {
|
|
const uint8_t *DataPtr = reinterpret_cast<const uint8_t *>(Data.data());
|
|
std::array<uint8_t, 32> Hash = llvm::SHA256::hash({ DataPtr, Data.size() });
|
|
return llvm::toHex(Hash, true);
|
|
}
|
|
};
|
|
|
|
static_assert(IsContainer<BinariesContainer>);
|
|
|
|
} // namespace revng::pypeline
|