diff options
Diffstat (limited to 'src/sim/fd_array.cc')
-rw-r--r-- | src/sim/fd_array.cc | 344 |
1 files changed, 344 insertions, 0 deletions
diff --git a/src/sim/fd_array.cc b/src/sim/fd_array.cc new file mode 100644 index 000000000..d73707054 --- /dev/null +++ b/src/sim/fd_array.cc @@ -0,0 +1,344 @@ +/* + * Copyright (c) 2016 Advanced Micro Devices, Inc. + * All rights reserved. + * + * For use for simulation and test purposes only + * + * 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. + * + * Author: Brandon Potter + */ + +#include "sim/fd_array.hh" + +#include <fcntl.h> +#include <unistd.h> + +#include <array> +#include <memory> +#include <string> + +#include "base/misc.hh" +#include "params/Process.hh" +#include "sim/fd_entry.hh" + +FDArray::FDArray(std::string const& input, std::string const& output, + std::string const& errout) + : _input(input), _output(output), _errout(errout), _fdArray(), + 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. + */ + void (*seek)(std::shared_ptr<FileFDEntry>) + = [] (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 %O", 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(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]; +} + +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; +} |