| /* |
| * 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. |
| */ |
| |
| #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 |