blob: 5ca93700542350bc816c96d253ecdc79bfa8b179 [file] [log] [blame]
/*
* Copyright (c) 2016 Advanced Micro Devices, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "sim/fd_array.hh"
#include <fcntl.h>
#include <unistd.h>
#include <array>
#include <memory>
#include <string>
#include "base/logging.hh"
#include "base/output.hh"
#include "params/Process.hh"
#include "sim/fd_entry.hh"
namespace gem5
{
FDArray::FDArray(std::string const& input, std::string const& output,
std::string const& errout)
: _fdArray(), _input(input), _output(output), _errout(errout),
_imap {{"", -1},
{"cin", STDIN_FILENO},
{"stdin", STDIN_FILENO}},
_oemap{{"", -1},
{"cout", STDOUT_FILENO},
{"stdout", STDOUT_FILENO},
{"cerr", STDERR_FILENO},
{"stderr", STDERR_FILENO}}
{
int sim_fd;
std::map<std::string, int>::iterator it;
/**
* Search through the input options and setup the default fd if match is
* found; otherwise, open an input file and seek to location.
*/
if ((it = _imap.find(input)) != _imap.end())
sim_fd = it->second;
else
sim_fd = openInputFile(input);
auto ffd = std::make_shared<FileFDEntry>(sim_fd, O_RDONLY, input, false);
_fdArray[STDIN_FILENO] = ffd;
/**
* Search through the output/error options and setup the default fd if
* match is found; otherwise, open an output file and seek to location.
*/
if ((it = _oemap.find(output)) != _oemap.end())
sim_fd = it->second;
else
sim_fd = openOutputFile(output);
ffd = std::make_shared<FileFDEntry>(sim_fd, O_WRONLY | O_CREAT | O_TRUNC,
output, false);
_fdArray[STDOUT_FILENO] = ffd;
if (output == errout)
; /* Reuse the same file descriptor if these match. */
else if ((it = _oemap.find(errout)) != _oemap.end())
sim_fd = it->second;
else
sim_fd = openOutputFile(errout);
ffd = std::make_shared<FileFDEntry>(sim_fd, O_WRONLY | O_CREAT | O_TRUNC,
errout, false);
_fdArray[STDERR_FILENO] = ffd;
}
void
FDArray::updateFileOffsets()
{
for (auto& fdp : _fdArray) {
/**
* It only makes sense to check the offsets if the file descriptor
* type is 'File' (which indicates that this file is backed by a
* file on the host). If the type is File, then record the offset.
*/
auto ffd = std::dynamic_pointer_cast<FileFDEntry>(fdp);
if (!ffd)
continue;
/**
* Use lseek with SEEK_CUR with offset 0 to figure out where the
* offset currently resides and pass that back to our setter.
*/
int sim_fd = ffd->getSimFD();
ffd->setFileOffset(lseek(sim_fd, 0, SEEK_CUR));
}
}
void
FDArray::restoreFileOffsets()
{
/**
* Use this lambda to highlight what we mean to do with the seek.
* Notice that this either seeks correctly (sets the file location on the
* host) or it fails with a fatal. The error is fatal because it's not
* possible to guarantee that the simulation will proceed as it should
* have in the same way that it would have proceeded sans checkpoints.
*/
auto seek = [] (std::shared_ptr<FileFDEntry> ffd)
{
if (lseek(ffd->getSimFD(), ffd->getFileOffset(), SEEK_SET) < 0)
fatal("Unable to seek to location in %s", ffd->getFileName());
};
std::map<std::string, int>::iterator it;
/**
* Search through the input options and set fd if match is found;
* otherwise, open an input file and seek to location.
* Check if user has specified a different input file, and if so, use it
* instead of the file specified in the checkpoint. This also resets the
* file offset from the checkpointed value
*/
std::shared_ptr<FDEntry> stdin_fde = _fdArray[STDIN_FILENO];
auto stdin_ffd = std::dynamic_pointer_cast<FileFDEntry>(stdin_fde);
if (_input != stdin_ffd->getFileName()) {
warn("Using new input file (%s) rather than checkpointed (%s)\n",
_input, stdin_ffd->getFileName());
stdin_ffd->setFileName(_input);
stdin_ffd->setFileOffset(0);
}
if ((it = _imap.find(stdin_ffd->getFileName())) != _imap.end()) {
stdin_ffd->setSimFD(it->second);
} else {
stdin_ffd->setSimFD(openInputFile(stdin_ffd->getFileName()));
seek(stdin_ffd);
}
/**
* Search through the output options and set fd if match is found;
* otherwise, open an output file and seek to location.
* Check if user has specified a different output file, and if so, use it
* instead of the file specified in the checkpoint. This also resets the
* file offset from the checkpointed value
*/
std::shared_ptr<FDEntry> stdout_fde = _fdArray[STDOUT_FILENO];
auto stdout_ffd = std::dynamic_pointer_cast<FileFDEntry>(stdout_fde);
if (_output != stdout_ffd->getFileName()) {
warn("Using new output file (%s) rather than checkpointed (%s)\n",
_output, stdout_ffd->getFileName());
stdout_ffd->setFileName(_output);
stdout_ffd->setFileOffset(0);
}
if ((it = _oemap.find(stdout_ffd->getFileName())) != _oemap.end()) {
stdout_ffd->setSimFD(it->second);
} else {
stdout_ffd->setSimFD(openOutputFile(stdout_ffd->getFileName()));
seek(stdout_ffd);
}
/**
* Search through the error options and set fd if match is found;
* otherwise, open an error file and seek to location.
* Check if user has specified a different error file, and if so, use it
* instead of the file specified in the checkpoint. This also resets the
* file offset from the checkpointed value
*/
std::shared_ptr<FDEntry> stderr_fde = _fdArray[STDERR_FILENO];
auto stderr_ffd = std::dynamic_pointer_cast<FileFDEntry>(stderr_fde);
if (_errout != stderr_ffd->getFileName()) {
warn("Using new error file (%s) rather than checkpointed (%s)\n",
_errout, stderr_ffd->getFileName());
stderr_ffd->setFileName(_errout);
stderr_ffd->setFileOffset(0);
}
if (stdout_ffd->getFileName() == stderr_ffd->getFileName()) {
/* Reuse the same sim_fd file descriptor if these match. */
stderr_ffd->setSimFD(stdout_ffd->getSimFD());
} else if ((it = _oemap.find(stderr_ffd->getFileName())) != _oemap.end()) {
stderr_ffd->setSimFD(it->second);
} else {
stderr_ffd->setSimFD(openOutputFile(stderr_ffd->getFileName()));
seek(stderr_ffd);
}
for (int tgt_fd = 3; tgt_fd < _fdArray.size(); tgt_fd++) {
std::shared_ptr<FDEntry> fdp = _fdArray[tgt_fd];
if (!fdp)
continue;
/* Need to reconnect pipe ends. */
if (auto pfd = std::dynamic_pointer_cast<PipeFDEntry>(fdp)) {
/**
* Check which end of the pipe we are looking at; we only want
* to setup the pipe once so we arbitrarily choose the read
* end to be the end that sets up the pipe.
*/
if (pfd->getEndType() == PipeFDEntry::EndType::write)
continue;
/* Setup the pipe or fatal out of the simulation. */
int fd_pair[2];
if (pipe(fd_pair) < 0)
fatal("Unable to create new pipe");
/**
* Reconstruct the ends of the pipe by reassigning the pipe
* that we created on the host. This one is the read end.
*/
pfd->setSimFD(fd_pair[0]);
/**
* Grab the write end by referencing the read ends source and
* using that tgt_fd to index the array.
*/
int prs = pfd->getPipeReadSource();
std::shared_ptr<FDEntry> write_fdp = _fdArray[prs];
/* Now cast it and make sure that we are still sane. */
auto write_pfd = std::dynamic_pointer_cast<PipeFDEntry>(write_fdp);
/* Hook up the write end back to the right side of the pipe. */
write_pfd->setSimFD(fd_pair[1]);
}
/* Need to reassign 'driver'. */
if (auto dfd = std::dynamic_pointer_cast<DeviceFDEntry>(fdp)) {
/**
* Yeah, how does one retain the entire driver state from this
* particular set of code? If you figure it out, add some code
* here to rectify the issue.
*/
fatal("Unable to restore checkpoints with emulated drivers");
}
/* Need to open files and seek. */
if (auto ffd = std::dynamic_pointer_cast<FileFDEntry>(fdp)) {
/**
* Assume that this has the mode of an output file so there's no
* need to worry about properly recording the mode. If you're
* reading this and this happens to be your issue, at least be
* happy that you've discovered the issue (and not mad at me).
* Onward ho!
*/
int sim_fd = openFile(ffd->getFileName(), ffd->getFlags(), 0664);
ffd->setSimFD(sim_fd);
seek(ffd);
}
}
}
int
FDArray::allocFD(std::shared_ptr<FDEntry> in)
{
for (int i = 0; i < _fdArray.size(); i++) {
std::shared_ptr<FDEntry> fdp = _fdArray[i];
if (!fdp) {
_fdArray[i] = in;
return i;
}
}
fatal("Out of target file descriptors");
}
int
FDArray::openFile(std::string const& filename, int flags, mode_t mode) const
{
int sim_fd = open(filename.c_str(), flags, mode);
if (sim_fd != -1)
return sim_fd;
fatal("Unable to open %s with mode %d", filename, mode);
}
int
FDArray::openInputFile(std::string const& filename) const
{
return openFile(filename, O_RDONLY, 00);
}
int
FDArray::openOutputFile(std::string const& filename) const
{
return openFile(simout.resolve(filename),
O_WRONLY | O_CREAT | O_TRUNC, 0664);
}
std::shared_ptr<FDEntry>
FDArray::getFDEntry(int tgt_fd)
{
assert(0 <= tgt_fd && tgt_fd < _fdArray.size());
return _fdArray[tgt_fd];
}
void
FDArray::setFDEntry(int tgt_fd, std::shared_ptr<FDEntry> fdep)
{
assert(0 <= tgt_fd && tgt_fd < _fdArray.size());
_fdArray[tgt_fd] = fdep;
}
int
FDArray::closeFDEntry(int tgt_fd)
{
if (tgt_fd >= _fdArray.size() || tgt_fd < 0)
return -EBADF;
int sim_fd = -1;
auto hbfdp = std::dynamic_pointer_cast<HBFDEntry>(_fdArray[tgt_fd]);
if (hbfdp)
sim_fd = hbfdp->getSimFD();
int status = 0;
if (sim_fd > 2)
status = close(sim_fd);
if (status == 0)
_fdArray[tgt_fd] = nullptr;
return status;
}
void
FDArray::serialize(CheckpointOut &cp) const {
ScopedCheckpointSection sec(cp, "fdarray");
paramOut(cp, "size", _fdArray.size());
for (int tgt_fd = 0; tgt_fd < _fdArray.size(); tgt_fd++) {
auto fd = _fdArray[tgt_fd];
ScopedCheckpointSection sec(cp, csprintf("Entry%d", tgt_fd));
if (!fd) {
paramOut(cp, "class", FDEntry::FDClass::fd_null);
continue;
}
paramOut(cp, "class", fd->getClass());
fd->serialize(cp);
}
}
void
FDArray::unserialize(CheckpointIn &cp) {
ScopedCheckpointSection sec(cp, "fdarray");
uint64_t size;
paramIn(cp, "size", size);
assert(_fdArray.size() == size &&
"FDArray sizes do not match at unserialize!");
for (int tgt_fd = 0; tgt_fd < _fdArray.size(); tgt_fd++) {
if (tgt_fd == STDIN_FILENO || tgt_fd == STDOUT_FILENO ||
tgt_fd == STDERR_FILENO)
continue;
ScopedCheckpointSection sec(cp, csprintf("Entry%d", tgt_fd));
FDEntry::FDClass fd_class;
paramIn(cp, "class", fd_class);
std::shared_ptr<FDEntry> fdep;
switch (fd_class) {
case FDEntry::FDClass::fd_base:
panic("Abstract fd entry was serialized");
break;
case FDEntry::FDClass::fd_hb:
fdep = std::make_shared<HBFDEntry>(0, 0);
break;
case FDEntry::FDClass::fd_file:
fdep = std::make_shared<FileFDEntry>(0, 0, "", 0, 00);
break;
case FDEntry::FDClass::fd_device:
fdep = std::make_shared<DeviceFDEntry>(nullptr, "");
break;
case FDEntry::FDClass::fd_pipe:
fdep = std::make_shared<PipeFDEntry>(
0, 0, PipeFDEntry::EndType::read);
break;
case FDEntry::FDClass::fd_socket:
fdep = std::make_shared<SocketFDEntry>(0, 0, 0, 0);
break;
case FDEntry::FDClass::fd_null:
continue;
default:
panic("Unrecognized fd class");
break;
}
fdep->unserialize(cp);
auto this_ffd = std::dynamic_pointer_cast<FileFDEntry>(fdep);
if (!this_ffd)
continue;
setFDEntry(tgt_fd, fdep);
mode_t mode = this_ffd->getFileMode();
std::string const& path = this_ffd->getFileName();
int flags = this_ffd->getFlags();
// Re-open the file and assign a new sim_fd
int sim_fd = openFile(path, flags, mode);
this_ffd->setSimFD(sim_fd);
// Restore the file offset to the proper value
uint64_t file_offset = this_ffd->getFileOffset();
lseek(sim_fd, file_offset, SEEK_SET);
}
}
} // namespace gem5